From c851c522f61370d2fea0a2258412573248e04fbb 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, 17 Jul 2020 20:30:31 -0700 Subject: [PATCH] Updated spirv-tools. --- .../include/generated/build-version.inc | 2 +- .../include/spirv-tools/instrument.hpp | 10 + .../include/spirv-tools/libspirv.h | 6 + .../include/spirv-tools/libspirv.hpp | 5 + .../spirv-tools/source/fuzz/CMakeLists.txt | 8 +- .../spirv-tools/source/fuzz/fact_manager.cpp | 90 +++++- 3rdparty/spirv-tools/source/fuzz/fuzzer.cpp | 10 +- .../source/fuzz/fuzzer_context.cpp | 22 ++ .../spirv-tools/source/fuzz/fuzzer_context.h | 8 + .../spirv-tools/source/fuzz/fuzzer_pass.cpp | 59 +++- .../spirv-tools/source/fuzz/fuzzer_pass.h | 24 ++ .../fuzz/fuzzer_pass_add_dead_blocks.cpp | 6 +- .../fuzz/fuzzer_pass_add_dead_breaks.cpp | 7 +- .../fuzz/fuzzer_pass_add_dead_continues.cpp | 9 +- .../fuzzer_pass_add_equation_instructions.cpp | 119 ++++++- .../fuzzer_pass_add_equation_instructions.h | 11 +- .../fuzz/fuzzer_pass_add_parameters.cpp | 37 +-- .../source/fuzz/fuzzer_pass_add_parameters.h | 4 - .../source/fuzz/fuzzer_pass_add_synonyms.cpp | 126 ++++++++ .../source/fuzz/fuzzer_pass_add_synonyms.h | 40 +++ .../fuzz/fuzzer_pass_apply_id_synonyms.cpp | 5 +- .../source/fuzz/fuzzer_pass_copy_objects.cpp | 8 +- ...r_pass_interchange_zero_like_constants.cpp | 119 +++++++ ...zer_pass_interchange_zero_like_constants.h | 63 ++++ .../source/fuzz/fuzzer_pass_merge_blocks.cpp | 6 +- .../fuzz/fuzzer_pass_obfuscate_constants.cpp | 34 +- .../fuzz/fuzzer_pass_outline_functions.cpp | 6 +- .../fuzz/fuzzer_pass_permute_blocks.cpp | 7 +- ...ss_replace_linear_algebra_instructions.cpp | 1 + ...zer_pass_replace_parameter_with_global.cpp | 7 +- .../source/fuzz/fuzzer_pass_split_blocks.cpp | 6 +- .../spirv-tools/source/fuzz/fuzzer_util.cpp | 64 ++-- .../spirv-tools/source/fuzz/fuzzer_util.h | 49 +-- .../fuzz/protobufs/spirvfuzz_protobufs.h | 1 + .../source/fuzz/protobufs/spvtoolsfuzz.proto | 157 +++++---- 3rdparty/spirv-tools/source/fuzz/replayer.cpp | 16 +- 3rdparty/spirv-tools/source/fuzz/replayer.h | 14 +- 3rdparty/spirv-tools/source/fuzz/shrinker.cpp | 2 + .../source/fuzz/transformation.cpp | 6 +- .../fuzz/transformation_access_chain.cpp | 6 +- .../fuzz/transformation_add_dead_break.cpp | 12 +- .../fuzz/transformation_add_parameter.cpp | 25 +- .../fuzz/transformation_add_parameter.h | 7 +- .../fuzz/transformation_add_synonym.cpp | 299 ++++++++++++++++++ .../source/fuzz/transformation_add_synonym.h | 86 +++++ .../source/fuzz/transformation_context.cpp | 6 +- .../fuzz/transformation_copy_object.cpp | 115 ------- .../source/fuzz/transformation_copy_object.h | 77 ----- .../transformation_equation_instruction.cpp | 109 ++++++- .../transformation_equation_instruction.h | 8 +- ...nsformation_invert_comparison_operator.cpp | 36 +++ ...ransformation_push_id_through_variable.cpp | 18 +- ..._boolean_constant_with_constant_binary.cpp | 37 ++- ...ion_replace_linear_algebra_instruction.cpp | 180 +++++++++++ ...ation_replace_linear_algebra_instruction.h | 4 + ...ormation_replace_parameter_with_global.cpp | 15 +- ...sformation_replace_parameter_with_global.h | 2 +- .../source/opt/debug_info_manager.cpp | 23 +- .../source/opt/dominator_analysis.cpp | 19 +- .../source/opt/instrument_pass.cpp | 5 +- .../source/spirv_fuzzer_options.cpp | 6 + .../spirv-tools/source/spirv_fuzzer_options.h | 3 + .../spirv-tools/source/val/basic_block.cpp | 6 - 3rdparty/spirv-tools/source/val/basic_block.h | 3 - 3rdparty/spirv-tools/source/val/function.cpp | 5 +- 3rdparty/spirv-tools/source/val/function.h | 4 +- 3rdparty/spirv-tools/source/val/validate.cpp | 4 + 3rdparty/spirv-tools/source/val/validate.h | 3 + .../spirv-tools/source/val/validate_cfg.cpp | 31 +- .../source/val/validate_interfaces.cpp | 24 +- .../source/val/validate_mode_setting.cpp | 4 - 71 files changed, 1778 insertions(+), 578 deletions(-) create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.h delete mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_copy_object.cpp delete mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_copy_object.h diff --git a/3rdparty/spirv-tools/include/generated/build-version.inc b/3rdparty/spirv-tools/include/generated/build-version.inc index d930a7090..689de7a8c 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 38bba44706260cfb807097c58ca926c64c3a13d2" +"v2020.4-dev", "SPIRV-Tools v2020.4-dev 8d5c7eae3a89ee8b898c9771119abb8a3f977898" diff --git a/3rdparty/spirv-tools/include/spirv-tools/instrument.hpp b/3rdparty/spirv-tools/include/spirv-tools/instrument.hpp index ef5136a8c..b4f33554d 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/instrument.hpp +++ b/3rdparty/spirv-tools/include/spirv-tools/instrument.hpp @@ -110,6 +110,16 @@ static const int kInstRayTracingOutLaunchIdX = kInstCommonOutCnt; static const int kInstRayTracingOutLaunchIdY = kInstCommonOutCnt + 1; static const int kInstRayTracingOutLaunchIdZ = kInstCommonOutCnt + 2; +// Mesh Shader Output Record Offsets +static const int kInstMeshOutGlobalInvocationIdX = kInstCommonOutCnt; +static const int kInstMeshOutGlobalInvocationIdY = kInstCommonOutCnt + 1; +static const int kInstMeshOutGlobalInvocationIdZ = kInstCommonOutCnt + 2; + +// Task Shader Output Record Offsets +static const int kInstTaskOutGlobalInvocationIdX = kInstCommonOutCnt; +static const int kInstTaskOutGlobalInvocationIdY = kInstCommonOutCnt + 1; +static const int kInstTaskOutGlobalInvocationIdZ = kInstCommonOutCnt + 2; + // Size of Common and Stage-specific Members static const int kInstStageOutCnt = kInstCommonOutCnt + 3; diff --git a/3rdparty/spirv-tools/include/spirv-tools/libspirv.h b/3rdparty/spirv-tools/include/spirv-tools/libspirv.h index 788d7d347..84b711e49 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/libspirv.h +++ b/3rdparty/spirv-tools/include/spirv-tools/libspirv.h @@ -677,6 +677,12 @@ SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableReplayValidation( SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed( spv_fuzzer_options options, uint32_t seed); +// Sets the range of transformations that should be applied during replay: 0 +// means all transformations, +N means the first N transformations, -N means all +// except the final N transformations. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetReplayRange( + spv_fuzzer_options options, int32_t replay_range); + // Sets the maximum number of steps that the shrinker should take before giving // up. SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit( diff --git a/3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp b/3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp index 5e1819ee6..6b31a07e0 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp +++ b/3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp @@ -227,6 +227,11 @@ class FuzzerOptions { spvFuzzerOptionsSetRandomSeed(options_, seed); } + // See spvFuzzerOptionsSetReplayRange. + void set_replay_range(int32_t replay_range) { + spvFuzzerOptionsSetReplayRange(options_, replay_range); + } + // See spvFuzzerOptionsSetShrinkerStepLimit. void set_shrinker_step_limit(uint32_t shrinker_step_limit) { spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit); diff --git a/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt b/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt index 9adb40091..a5f67ca31 100644 --- a/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt @@ -47,6 +47,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_add_function_calls.h fuzzer_pass_add_global_variables.h fuzzer_pass_add_image_sample_unused_components.h + fuzzer_pass_add_synonyms.h fuzzer_pass_add_loads.h fuzzer_pass_add_local_variables.h fuzzer_pass_add_no_contraction_decorations.h @@ -63,6 +64,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_copy_objects.h fuzzer_pass_donate_modules.h fuzzer_pass_invert_comparison_operators.h + fuzzer_pass_interchange_zero_like_constants.h fuzzer_pass_merge_blocks.h fuzzer_pass_obfuscate_constants.h fuzzer_pass_outline_functions.h @@ -103,6 +105,7 @@ if(SPIRV_BUILD_FUZZER) transformation_add_no_contraction_decoration.h transformation_add_parameter.h transformation_add_spec_constant_op.h + transformation_add_synonym.h transformation_add_type_array.h transformation_add_type_boolean.h transformation_add_type_float.h @@ -117,7 +120,6 @@ if(SPIRV_BUILD_FUZZER) transformation_composite_extract.h transformation_compute_data_synonym_fact_closure.h transformation_context.h - transformation_copy_object.h transformation_equation_instruction.h transformation_function_call.h transformation_invert_comparison_operator.h @@ -164,6 +166,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_add_function_calls.cpp fuzzer_pass_add_global_variables.cpp fuzzer_pass_add_image_sample_unused_components.cpp + fuzzer_pass_add_synonyms.cpp fuzzer_pass_add_loads.cpp fuzzer_pass_add_local_variables.cpp fuzzer_pass_add_no_contraction_decorations.cpp @@ -180,6 +183,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_copy_objects.cpp fuzzer_pass_donate_modules.cpp fuzzer_pass_invert_comparison_operators.cpp + fuzzer_pass_interchange_zero_like_constants.cpp fuzzer_pass_merge_blocks.cpp fuzzer_pass_obfuscate_constants.cpp fuzzer_pass_outline_functions.cpp @@ -219,6 +223,7 @@ if(SPIRV_BUILD_FUZZER) transformation_add_no_contraction_decoration.cpp transformation_add_parameter.cpp transformation_add_spec_constant_op.cpp + transformation_add_synonym.cpp transformation_add_type_array.cpp transformation_add_type_boolean.cpp transformation_add_type_float.cpp @@ -233,7 +238,6 @@ if(SPIRV_BUILD_FUZZER) transformation_composite_extract.cpp transformation_compute_data_synonym_fact_closure.cpp transformation_context.cpp - transformation_copy_object.cpp transformation_equation_instruction.cpp transformation_function_call.cpp transformation_invert_comparison_operator.cpp diff --git a/3rdparty/spirv-tools/source/fuzz/fact_manager.cpp b/3rdparty/spirv-tools/source/fuzz/fact_manager.cpp index 2cbac9d8b..320049a86 100644 --- a/3rdparty/spirv-tools/source/fuzz/fact_manager.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fact_manager.cpp @@ -457,6 +457,18 @@ class FactManager::DataSynonymAndIdEquationFacts { const protobufs::DataDescriptor& dd2, opt::IRContext* context); + // Computes various corollary facts from the data descriptor |dd| if members + // of its equivalence class participate in equation facts with OpConvert* + // opcodes. The descriptor should be registered in the equivalence relation. + void ComputeConversionDataSynonymFacts(const protobufs::DataDescriptor& dd, + opt::IRContext* context); + + // Recurses into sub-components of the data descriptors, if they are + // composites, to record that their components are pairwise-synonymous. + void ComputeCompositeDataSynonymFacts(const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2, + opt::IRContext* context); + // Records the fact that |dd1| and |dd2| are equivalent, and merges the sets // of equations that are known about them. void MakeEquivalent(const protobufs::DataDescriptor& dd1, @@ -588,9 +600,13 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive( // Now try to work out corollaries implied by the new equation and existing // facts. switch (opcode) { + case SpvOpConvertSToF: + case SpvOpConvertUToF: + ComputeConversionDataSynonymFacts(*rhs_dds[0], context); + break; case SpvOpIAdd: { // Equation form: "a = b + c" - for (auto equation : GetEquations(rhs_dds[0])) { + for (const auto& equation : GetEquations(rhs_dds[0])) { if (equation.opcode == SpvOpISub) { // Equation form: "a = (d - e) + c" if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) { @@ -606,7 +622,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive( } } } - for (auto equation : GetEquations(rhs_dds[1])) { + for (const auto& equation : GetEquations(rhs_dds[1])) { if (equation.opcode == SpvOpISub) { // Equation form: "a = b + (d - e)" if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) { @@ -620,7 +636,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive( } case SpvOpISub: { // Equation form: "a = b - c" - for (auto equation : GetEquations(rhs_dds[0])) { + for (const auto& equation : GetEquations(rhs_dds[0])) { if (equation.opcode == SpvOpIAdd) { // Equation form: "a = (d + e) - c" if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) { @@ -646,7 +662,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive( } } - for (auto equation : GetEquations(rhs_dds[1])) { + for (const auto& equation : GetEquations(rhs_dds[1])) { if (equation.opcode == SpvOpIAdd) { // Equation form: "a = b - (d + e)" if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) { @@ -676,7 +692,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive( case SpvOpLogicalNot: case SpvOpSNegate: { // Equation form: "a = !b" or "a = -b" - for (auto equation : GetEquations(rhs_dds[0])) { + for (const auto& equation : GetEquations(rhs_dds[0])) { if (equation.opcode == opcode) { // Equation form: "a = !!b" or "a = -(-b)" // We can thus infer "a = b" @@ -698,7 +714,69 @@ void FactManager::DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive( // Record that the data descriptors provided in the fact are equivalent. MakeEquivalent(dd1, dd2); - // We now check whether this is a synonym about composite objects. If it is, + // Compute various corollary facts. + ComputeConversionDataSynonymFacts(dd1, context); + ComputeCompositeDataSynonymFacts(dd1, dd2, context); +} + +void FactManager::DataSynonymAndIdEquationFacts:: + ComputeConversionDataSynonymFacts(const protobufs::DataDescriptor& dd, + opt::IRContext* context) { + assert(synonymous_.Exists(dd) && + "|dd| should've been registered in the equivalence relation"); + + const auto* representative = synonymous_.Find(&dd); + assert(representative && + "Representative can't be null for a registered descriptor"); + + const auto* type = + context->get_type_mgr()->GetType(fuzzerutil::WalkCompositeTypeIndices( + context, fuzzerutil::GetTypeId(context, representative->object()), + representative->index())); + assert(type && "Data descriptor has invalid type"); + + if ((type->AsVector() && type->AsVector()->element_type()->AsInteger()) || + type->AsInteger()) { + // If there exist equation facts of the form |%a = opcode %representative| + // and |%b = opcode %representative| where |opcode| is either OpConvertSToF + // or OpConvertUToF, then |a| and |b| are synonymous. + std::vector convert_s_to_f_lhs; + std::vector convert_u_to_f_lhs; + + for (const auto& fact : id_equations_) { + for (const auto& equation : fact.second) { + if (synonymous_.IsEquivalent(*equation.operands[0], *representative)) { + if (equation.opcode == SpvOpConvertSToF) { + convert_s_to_f_lhs.push_back(fact.first); + } else if (equation.opcode == SpvOpConvertUToF) { + convert_u_to_f_lhs.push_back(fact.first); + } + } + } + } + + for (const auto& synonyms : + {std::move(convert_s_to_f_lhs), std::move(convert_u_to_f_lhs)}) { + for (const auto* synonym_a : synonyms) { + for (const auto* synonym_b : synonyms) { + if (!synonymous_.IsEquivalent(*synonym_a, *synonym_b) && + DataDescriptorsAreWellFormedAndComparable(context, *synonym_a, + *synonym_b)) { + // |synonym_a| and |synonym_b| have compatible types - they are + // synonymous. + AddDataSynonymFactRecursive(*synonym_a, *synonym_b, context); + } + } + } + } + } +} + +void FactManager::DataSynonymAndIdEquationFacts:: + ComputeCompositeDataSynonymFacts(const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2, + opt::IRContext* context) { + // Check whether this is a synonym about composite objects. If it is, // we can recursively add synonym facts about their associated sub-components. // Get the type of the object referred to by the first data descriptor in the diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp index 663a0c7d7..edd6e1794 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp @@ -18,7 +18,6 @@ #include #include -#include "fuzzer_pass_adjust_memory_operands_masks.h" #include "source/fuzz/fact_manager.h" #include "source/fuzz/fuzzer_context.h" #include "source/fuzz/fuzzer_pass_add_access_chains.h" @@ -36,15 +35,18 @@ #include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h" #include "source/fuzz/fuzzer_pass_add_parameters.h" #include "source/fuzz/fuzzer_pass_add_stores.h" +#include "source/fuzz/fuzzer_pass_add_synonyms.h" #include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h" #include "source/fuzz/fuzzer_pass_adjust_branch_weights.h" #include "source/fuzz/fuzzer_pass_adjust_function_controls.h" #include "source/fuzz/fuzzer_pass_adjust_loop_controls.h" +#include "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h" #include "source/fuzz/fuzzer_pass_adjust_selection_controls.h" #include "source/fuzz/fuzzer_pass_apply_id_synonyms.h" #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_interchange_zero_like_constants.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" @@ -241,6 +243,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); @@ -326,6 +331,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( MaybeAddPass( &final_passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); + MaybeAddPass( + &final_passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); MaybeAddPass( &final_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 e0c2c4243..f2b81704e 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp @@ -43,6 +43,7 @@ const std::pair kChanceOfAddingNoContractionDecoration = { 5, 70}; const std::pair kChanceOfAddingParameters = {5, 70}; const std::pair kChanceOfAddingStore = {5, 50}; +const std::pair kChanceOfAddingSynonyms = {20, 50}; const std::pair kChanceOfAddingVectorType = {20, 70}; const std::pair kChanceOfAddingVectorShuffle = {20, 70}; const std::pair kChanceOfAdjustingBranchWeights = {20, 90}; @@ -63,6 +64,8 @@ const std::pair kChanceOfCopyingObject = {20, 50}; const std::pair kChanceOfDonatingAdditionalModule = {5, 50}; const std::pair kChanceOfGoingDeeperWhenMakingAccessChain = {50, 95}; +const std::pair kChanceOfInterchangingZeroLikeConstants = { + 10, 90}; const std::pair kChanceOfInvertingComparisonOperators = { 20, 50}; const std::pair kChanceOfMakingDonorLivesafe = {40, 60}; @@ -160,6 +163,7 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfAdjustingBranchWeights); chance_of_adjusting_function_control_ = ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl); + chance_of_adding_synonyms_ = ChooseBetweenMinAndMax(kChanceOfAddingSynonyms); chance_of_adjusting_loop_control_ = ChooseBetweenMinAndMax(kChanceOfAdjustingLoopControl); chance_of_adjusting_memory_operands_mask_ = @@ -179,6 +183,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule); chance_of_going_deeper_when_making_access_chain_ = ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain); + chance_of_interchanging_zero_like_constants_ = + ChooseBetweenMinAndMax(kChanceOfInterchangingZeroLikeConstants); chance_of_inverting_comparison_operators_ = ChooseBetweenMinAndMax(kChanceOfInvertingComparisonOperators); chance_of_making_donor_livesafe_ = @@ -237,5 +243,21 @@ uint32_t FuzzerContext::ChooseBetweenMinAndMax( random_generator_->RandomUint32(min_max.second - min_max.first + 1); } +protobufs::TransformationAddSynonym::SynonymType +FuzzerContext::GetRandomSynonymType() { + // value_count method is guaranteed to return a value greater than 0. + auto result_index = ChooseBetweenMinAndMax( + {0, static_cast( + protobufs::TransformationAddSynonym::SynonymType_descriptor() + ->value_count() - + 1)}); + auto result = protobufs::TransformationAddSynonym::SynonymType_descriptor() + ->value(result_index) + ->number(); + assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(result) && + "|result| is not a value of SynonymType"); + return static_cast(result); +} + } // namespace fuzz } // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h index 86dbc141e..90539c88c 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h @@ -18,6 +18,7 @@ #include #include +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/random_generator.h" #include "source/opt/function.h" @@ -143,6 +144,7 @@ class FuzzerContext { } uint32_t GetChanceOfAddingParameters() { return chance_of_adding_parameters; } uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; } + uint32_t GetChanceOfAddingSynonyms() { return chance_of_adding_synonyms_; } uint32_t GetChanceOfAddingVectorShuffle() { return chance_of_adding_vector_shuffle_; } @@ -181,6 +183,9 @@ class FuzzerContext { uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() { return chance_of_going_deeper_when_making_access_chain_; } + uint32_t GetChanceOfInterchangingZeroLikeConstants() { + return chance_of_interchanging_zero_like_constants_; + } uint32_t GetChanceOfInvertingComparisonOperators() { return chance_of_inverting_comparison_operators_; } @@ -274,6 +279,7 @@ class FuzzerContext { // Ensure that the array size is non-zero. return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1; } + protobufs::TransformationAddSynonym::SynonymType GetRandomSynonymType(); uint32_t GetRandomUnusedComponentCountForImageSample( uint32_t max_unused_component_count) { // Ensure that the number of unused components is non-zero. @@ -307,6 +313,7 @@ class FuzzerContext { uint32_t chance_of_adding_no_contraction_decoration_; uint32_t chance_of_adding_parameters; uint32_t chance_of_adding_store_; + uint32_t chance_of_adding_synonyms_; uint32_t chance_of_adding_vector_shuffle_; uint32_t chance_of_adding_vector_type_; uint32_t chance_of_adjusting_branch_weights_; @@ -321,6 +328,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_interchanging_zero_like_constants_; uint32_t chance_of_inverting_comparison_operators_; uint32_t chance_of_making_donor_livesafe_; uint32_t chance_of_merging_blocks_; diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp index c44a4eaa7..bf409c9ae 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp @@ -20,6 +20,7 @@ #include "source/fuzz/instruction_descriptor.h" #include "source/fuzz/transformation_add_constant_boolean.h" #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_global_undef.h" #include "source/fuzz/transformation_add_type_boolean.h" @@ -163,8 +164,9 @@ uint32_t FuzzerPass::FindOrCreateBoolType() { } uint32_t FuzzerPass::FindOrCreateIntegerType(uint32_t width, bool is_signed) { - if (auto existing_id = - fuzzerutil::MaybeGetIntegerType(GetIRContext(), width, is_signed)) { + opt::analysis::Integer int_type(width, is_signed); + auto existing_id = GetIRContext()->get_type_mgr()->GetId(&int_type); + if (existing_id) { return existing_id; } auto result = GetFuzzerContext()->GetFreshId(); @@ -173,7 +175,9 @@ uint32_t FuzzerPass::FindOrCreateIntegerType(uint32_t width, bool is_signed) { } uint32_t FuzzerPass::FindOrCreateFloatType(uint32_t width) { - if (auto existing_id = fuzzerutil::MaybeGetFloatType(GetIRContext(), width)) { + opt::analysis::Float float_type(width); + auto existing_id = GetIRContext()->get_type_mgr()->GetId(&float_type); + if (existing_id) { return existing_id; } auto result = GetFuzzerContext()->GetFreshId(); @@ -203,8 +207,14 @@ uint32_t FuzzerPass::FindOrCreateFunctionType( uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id, uint32_t component_count) { - if (auto existing_id = fuzzerutil::MaybeGetVectorType( - GetIRContext(), component_type_id, 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) { return existing_id; } auto result = GetFuzzerContext()->GetFreshId(); @@ -344,6 +354,24 @@ uint32_t FuzzerPass::FindOrCreateConstant(const std::vector& words, return 0; } +uint32_t FuzzerPass::FindOrCreateCompositeConstant( + const std::vector& component_ids, uint32_t type_id) { + assert(type_id && "|type_id| can't be 0"); + const auto* type_inst = GetIRContext()->get_def_use_mgr()->GetDef(type_id); + assert(type_inst && "|type_id| is invalid"); + + std::vector constants; + for (auto id : component_ids) { + assert(id && "Component's id can't be 0"); + const auto* constant = + GetIRContext()->get_constant_mgr()->FindDeclaredConstant(id); + assert(constant && "Component's id is invalid"); + constants.push_back(constant); + } + + return FindOrCreateCompositeConstant(*type_inst, constants, component_ids); +} + uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) { for (auto& inst : GetIRContext()->types_values()) { if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) { @@ -355,6 +383,27 @@ uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) { return result; } +uint32_t FuzzerPass::FindOrCreateNullConstant(uint32_t type_id) { + // Find existing declaration + opt::analysis::NullConstant null_constant( + GetIRContext()->get_type_mgr()->GetType(type_id)); + auto existing_constant = + GetIRContext()->get_constant_mgr()->FindConstant(&null_constant); + + // Return if found + if (existing_constant) { + return GetIRContext() + ->get_constant_mgr() + ->GetDefiningInstruction(existing_constant) + ->result_id(); + } + + // Create new if not found + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantNull(result, type_id)); + return result; +} + std::pair, std::map>> FuzzerPass::GetAvailableBasicTypesAndPointers( SpvStorageClass storage_class) const { diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h index f1f68ed07..0603c940d 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h @@ -103,6 +103,20 @@ class FuzzerPass { *GetTransformations()->add_transformation() = transformation.ToMessage(); } + // A generic helper for applying a transformation only if it is applicable. + // If it is applicable, the transformation is applied and then added to the + // sequence of applied transformations and the function returns true. + // Otherwise, the function returns false. + bool MaybeApplyTransformation(const Transformation& transformation) { + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + transformation.Apply(GetIRContext(), GetTransformationContext()); + *GetTransformations()->add_transformation() = transformation.ToMessage(); + return true; + } + return false; + } + // Returns the id of an OpTypeBool instruction. If such an instruction does // not exist, a transformation is applied to add it. uint32_t FindOrCreateBoolType(); @@ -183,11 +197,21 @@ class FuzzerPass { uint32_t FindOrCreateConstant(const std::vector& words, uint32_t type_id); + // Returns the id of an OpConstantComposite + uint32_t FindOrCreateCompositeConstant( + const std::vector& component_ids, uint32_t type_id); + // Returns the result id of an instruction of the form: // %id = OpUndef %|type_id| // If no such instruction exists, a transformation is applied to add it. uint32_t FindOrCreateGlobalUndef(uint32_t type_id); + // Returns the id of an OpNullConstant instruction of type |type_id|. If + // that instruction doesn't exist, it is added through a transformation. + // |type_id| must be a valid result id of an OpType* instruction that exists + // in the module. + uint32_t FindOrCreateNullConstant(uint32_t type_id); + // Define a *basic type* to be an integer, boolean or floating-point type, // or a matrix, vector, struct or fixed-size array built from basic types. In // particular, a basic type cannot contain an opaque type (such as an image), diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp index 30a414544..56d33e56f 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp @@ -59,11 +59,7 @@ void FuzzerPassAddDeadBlocks::Apply() { } // Apply all those transformations that are in fact applicable. for (auto& transformation : candidate_transformations) { - if (transformation.IsApplicable(GetIRContext(), - *GetTransformationContext())) { - transformation.Apply(GetIRContext(), GetTransformationContext()); - *GetTransformations()->add_transformation() = transformation.ToMessage(); - } + MaybeApplyTransformation(transformation); } } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp index f3900aa69..120661670 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp @@ -114,12 +114,9 @@ void FuzzerPassAddDeadBreaks::Apply() { candidate_transformations.erase(candidate_transformations.begin() + index); // Probabilistically decide whether to try to apply it vs. ignore it, in the // case that it is applicable. - if (transformation.IsApplicable(GetIRContext(), - *GetTransformationContext()) && - GetFuzzerContext()->ChoosePercentage( + if (GetFuzzerContext()->ChoosePercentage( GetFuzzerContext()->GetChanceOfAddingDeadBreak())) { - transformation.Apply(GetIRContext(), GetTransformationContext()); - *GetTransformations()->add_transformation() = transformation.ToMessage(); + MaybeApplyTransformation(transformation); } } } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp index 56a7fd17c..a383c2ebd 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp @@ -80,14 +80,9 @@ void FuzzerPassAddDeadContinues::Apply() { block.id(), condition_value, std::move(phi_ids)); // Probabilistically decide whether to apply the transformation in the // case that it is applicable. - if (candidate_transformation.IsApplicable(GetIRContext(), - *GetTransformationContext()) && - GetFuzzerContext()->ChoosePercentage( + if (GetFuzzerContext()->ChoosePercentage( GetFuzzerContext()->GetChanceOfAddingDeadContinue())) { - candidate_transformation.Apply(GetIRContext(), - GetTransformationContext()); - *GetTransformations()->add_transformation() = - candidate_transformation.ToMessage(); + MaybeApplyTransformation(candidate_transformation); } } } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp index 49c4a8aae..c8f8681fe 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp @@ -64,12 +64,106 @@ void FuzzerPassAddEquationInstructions::Apply() { // Try the opcodes for which we know how to make ids at random until // something works. - std::vector candidate_opcodes = {SpvOpIAdd, SpvOpISub, - SpvOpLogicalNot, SpvOpSNegate}; + std::vector candidate_opcodes = { + SpvOpIAdd, SpvOpISub, SpvOpLogicalNot, SpvOpSNegate, + SpvOpConvertUToF, SpvOpConvertSToF, SpvOpBitcast}; do { auto opcode = GetFuzzerContext()->RemoveAtRandomIndex(&candidate_opcodes); switch (opcode) { + case SpvOpConvertSToF: + case SpvOpConvertUToF: { + auto candidate_instructions = + GetIntegerInstructions(available_instructions); + + if (candidate_instructions.empty()) { + break; + } + + const auto* operand = + candidate_instructions[GetFuzzerContext()->RandomIndex( + candidate_instructions)]; + + const auto* type = + GetIRContext()->get_type_mgr()->GetType(operand->type_id()); + assert(type && "Operand has invalid type"); + + // Make sure a result type exists in the module. + if (const auto* vector = type->AsVector()) { + FindOrCreateVectorType( + FindOrCreateFloatType( + vector->element_type()->AsInteger()->width()), + vector->element_count()); + } else { + FindOrCreateFloatType(type->AsInteger()->width()); + } + + ApplyTransformation(TransformationEquationInstruction( + GetFuzzerContext()->GetFreshId(), opcode, + {operand->result_id()}, instruction_descriptor)); + return; + } + case SpvOpBitcast: { + std::vector candidate_instructions; + for (const auto* inst : available_instructions) { + const auto* type = + GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + assert(type && "Instruction has invalid type"); + if ((type->AsVector() && + (type->AsVector()->element_type()->AsInteger() || + type->AsVector()->element_type()->AsFloat())) || + type->AsInteger() || type->AsFloat()) { + // We support OpBitcast for only scalars or vectors of + // numerical type. + candidate_instructions.push_back(inst); + } + } + + if (!candidate_instructions.empty()) { + const auto* operand_inst = + candidate_instructions[GetFuzzerContext()->RandomIndex( + candidate_instructions)]; + const auto* operand_type = + GetIRContext()->get_type_mgr()->GetType( + operand_inst->type_id()); + assert(operand_type && "Operand instruction has invalid type"); + + // Make sure a result type exists in the module. + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3539): + // The only constraint on the types of OpBitcast's parameters + // is that they must have the same number of bits. Consider + // improving the code below to support this in full. + if (const auto* vector = operand_type->AsVector()) { + uint32_t element_type_id; + if (const auto* int_type = + vector->element_type()->AsInteger()) { + element_type_id = FindOrCreateFloatType(int_type->width()); + } else { + assert(vector->element_type()->AsFloat() && + "Vector must have numerical elements"); + element_type_id = FindOrCreateIntegerType( + vector->element_type()->AsFloat()->width(), + GetFuzzerContext()->ChooseEven()); + } + + FindOrCreateVectorType(element_type_id, + vector->element_count()); + } else if (const auto* int_type = operand_type->AsInteger()) { + FindOrCreateFloatType(int_type->width()); + } else { + assert(operand_type->AsFloat() && + "Operand is not a scalar of numerical type"); + FindOrCreateIntegerType(operand_type->AsFloat()->width(), + GetFuzzerContext()->ChooseEven()); + } + + ApplyTransformation(TransformationEquationInstruction( + GetFuzzerContext()->GetFreshId(), opcode, + {operand_inst->result_id()}, instruction_descriptor)); + return; + } + } break; case SpvOpIAdd: case SpvOpISub: { // Instructions of integer (scalar or vector) result type are @@ -181,6 +275,20 @@ FuzzerPassAddEquationInstructions::GetIntegerInstructions( return result; } +std::vector +FuzzerPassAddEquationInstructions::GetFloatInstructions( + const std::vector& instructions) const { + std::vector result; + for (auto& inst : instructions) { + auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + if (type->AsFloat() || + (type->AsVector() && type->AsVector()->element_type()->AsFloat())) { + result.push_back(inst); + } + } + return result; +} + std::vector FuzzerPassAddEquationInstructions::GetBooleanInstructions( const std::vector& instructions) const { @@ -225,10 +333,11 @@ FuzzerPassAddEquationInstructions::RestrictToElementBitWidth( if (type->AsVector()) { type = type->AsVector()->element_type(); } - assert(type->AsInteger() && + assert((type->AsInteger() || type->AsFloat()) && "Precondition: all input instructions must " - "have integer scalar or vector type."); - if (type->AsInteger()->width() == bit_width) { + "have integer or float scalar or vector type."); + if ((type->AsInteger() && type->AsInteger()->width() == bit_width) || + (type->AsFloat() && type->AsFloat()->width() == bit_width)) { result.push_back(inst); } } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h index 6e6497738..8328b6b8b 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h @@ -41,6 +41,11 @@ class FuzzerPassAddEquationInstructions : public FuzzerPass { std::vector GetIntegerInstructions( const std::vector& instructions) const; + // Returns only instructions, that have either a scalar floating-point or a + // vector type. + std::vector GetFloatInstructions( + const std::vector& instructions) const; + // Yields those instructions in |instructions| that have boolean scalar or // vector result type. std::vector GetBooleanInstructions( @@ -53,9 +58,9 @@ class FuzzerPassAddEquationInstructions : public FuzzerPass { const std::vector& instructions, uint32_t vector_width) const; - // Requires that |instructions| are integer scalars or vectors. Returns only - // those instructions for which the bit-width of the underlying integer type - // is |bit_width|. + // Requires that |instructions| are integer or float scalars or vectors. + // Returns only those instructions for which the bit-width of the underlying + // integer or floating-point type is |bit_width|. std::vector RestrictToElementBitWidth( const std::vector& instructions, uint32_t bit_width) const; 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 927345209..c03b4f4ee 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp @@ -32,7 +32,16 @@ FuzzerPassAddParameters::FuzzerPassAddParameters( FuzzerPassAddParameters::~FuzzerPassAddParameters() = default; void FuzzerPassAddParameters::Apply() { - const auto& type_candidates = ComputeTypeCandidates(); + // Compute type candidates for the new parameter. + std::vector type_candidates; + for (const auto& type_inst : GetIRContext()->module()->GetTypes()) { + const auto* type = + GetIRContext()->get_type_mgr()->GetType(type_inst->result_id()); + assert(type && "Type instruction is not registered in the type manager"); + if (TransformationAddParameter::IsParameterTypeSupported(*type)) { + type_candidates.push_back(type_inst->result_id()); + } + } if (type_candidates.empty()) { // The module contains no suitable types to use in new parameters. @@ -71,32 +80,6 @@ void FuzzerPassAddParameters::Apply() { } } -std::vector FuzzerPassAddParameters::ComputeTypeCandidates() const { - std::vector result; - - for (const auto* type_inst : GetIRContext()->module()->GetTypes()) { - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403): - // the number of types we support here is limited by the number of types - // supported by |FindOrCreateZeroConstant|. - switch (type_inst->opcode()) { - case SpvOpTypeBool: - case SpvOpTypeInt: - case SpvOpTypeFloat: - case SpvOpTypeArray: - case SpvOpTypeMatrix: - case SpvOpTypeVector: - case SpvOpTypeStruct: { - result.push_back(type_inst->result_id()); - } break; - default: - // Ignore other types. - break; - } - } - - return result; -} - uint32_t FuzzerPassAddParameters::GetNumberOfParameters( const opt::Function& function) const { const auto* type = GetIRContext()->get_type_mgr()->GetType( diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h index feb009f9e..f1261ae51 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h @@ -37,10 +37,6 @@ class FuzzerPassAddParameters : public FuzzerPass { void Apply() override; private: - // Uses types, defined in the module, to compute a vector of their ids, which - // will be used as type ids of new parameters. - std::vector ComputeTypeCandidates() const; - // Returns number of parameters of |function|. uint32_t GetNumberOfParameters(const opt::Function& function) const; }; diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp new file mode 100644 index 000000000..032f3697a --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp @@ -0,0 +1,126 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_synonyms.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddSynonyms::FuzzerPassAddSynonyms( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddSynonyms::~FuzzerPassAddSynonyms() = default; + +void FuzzerPassAddSynonyms::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + // Skip |inst_it| if we can't insert anything above it. OpIAdd is just + // a representative of some instruction that might be produced by the + // transformation. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, inst_it)) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingSynonyms())) { + return; + } + + auto synonym_type = GetFuzzerContext()->GetRandomSynonymType(); + + // Select all instructions that can be used to create a synonym to. + auto available_instructions = FindAvailableInstructions( + function, block, inst_it, + [synonym_type](opt::IRContext* ir_context, opt::Instruction* inst) { + // Check that we can create a synonym to |inst| as described by + // the |synonym_type| and insert it before |inst_it|. + return TransformationAddSynonym::IsInstructionValid( + ir_context, inst, synonym_type); + }); + + if (available_instructions.empty()) { + return; + } + + const auto* existing_synonym = + available_instructions[GetFuzzerContext()->RandomIndex( + available_instructions)]; + + // Make sure the module contains all instructions required to apply the + // transformation. + switch (synonym_type) { + case protobufs::TransformationAddSynonym::ADD_ZERO: + case protobufs::TransformationAddSynonym::SUB_ZERO: + case protobufs::TransformationAddSynonym::LOGICAL_OR: + // Create a zero constant to be used as an operand of the synonymous + // instruction. + FindOrCreateZeroConstant(existing_synonym->type_id()); + break; + case protobufs::TransformationAddSynonym::MUL_ONE: + case protobufs::TransformationAddSynonym::LOGICAL_AND: { + const auto* existing_synonym_type = + GetIRContext()->get_type_mgr()->GetType( + existing_synonym->type_id()); + assert(existing_synonym_type && "Instruction has invalid type"); + + if (const auto* vector = existing_synonym_type->AsVector()) { + auto element_type_id = + GetIRContext()->get_type_mgr()->GetId(vector->element_type()); + assert(element_type_id && "Vector's element type is invalid"); + + auto one_word = vector->element_type()->AsFloat() + ? fuzzerutil::FloatToWord(1) + : 1u; + FindOrCreateCompositeConstant( + std::vector( + vector->element_count(), + FindOrCreateConstant({one_word}, element_type_id)), + existing_synonym->type_id()); + } else { + FindOrCreateConstant( + {existing_synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) + : 1u}, + existing_synonym->type_id()); + } + } break; + default: + // This assertion will fail if some SynonymType is missing from the + // switch statement. + assert( + !TransformationAddSynonym::IsAdditionalConstantRequired( + synonym_type) && + "|synonym_type| requires an additional constant to be present " + "in the module"); + break; + } + + ApplyTransformation(TransformationAddSynonym( + existing_synonym->result_id(), synonym_type, + GetFuzzerContext()->GetFreshId(), instruction_descriptor)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h new file mode 100644 index 000000000..dcfb93898 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.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_SYNONYMS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Sprinkles instructions through the module that produce ids, synonymous to +// some other instructions. +class FuzzerPassAddSynonyms : public FuzzerPass { + public: + FuzzerPassAddSynonyms(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddSynonyms() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp index 0ec93e1ba..122c2ddfe 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp @@ -73,10 +73,9 @@ void FuzzerPassApplyIdSynonyms::Apply() { continue; } // |use_index| is the absolute index of the operand. We require - // the index of the operand restricted to input operands only, so - // we subtract the number of non-input operands from |use_index|. + // the index of the operand restricted to input operands only. uint32_t use_in_operand_index = - use_index - use_inst->NumOperands() + use_inst->NumInOperands(); + fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index); if (!TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym( GetIRContext(), use_inst, use_in_operand_index)) { continue; diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp index f055b5987..5933bf6cf 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp @@ -15,7 +15,8 @@ #include "source/fuzz/fuzzer_pass_copy_objects.h" #include "source/fuzz/fuzzer_util.h" -#include "source/fuzz/transformation_copy_object.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation_add_synonym.h" namespace spvtools { namespace fuzz { @@ -65,11 +66,12 @@ void FuzzerPassCopyObjects::Apply() { // Choose a copyable instruction at random, and create and apply an // object copying transformation based on it. - ApplyTransformation(TransformationCopyObject( + ApplyTransformation(TransformationAddSynonym( relevant_instructions[GetFuzzerContext()->RandomIndex( relevant_instructions)] ->result_id(), - instruction_descriptor, GetFuzzerContext()->GetFreshId())); + protobufs::TransformationAddSynonym::COPY_OBJECT, + GetFuzzerContext()->GetFreshId(), instruction_descriptor)); }); } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp new file mode 100644 index 000000000..201c6ffc0 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp @@ -0,0 +1,119 @@ +// Copyright (c) 2020 Stefano Milizia +// Copyright (c) 2020 Google LLC +// +// 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_interchange_zero_like_constants.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/transformation_record_synonymous_constants.h" +#include "source/fuzz/transformation_replace_id_with_synonym.h" + +namespace spvtools { +namespace fuzz { +FuzzerPassInterchangeZeroLikeConstants::FuzzerPassInterchangeZeroLikeConstants( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassInterchangeZeroLikeConstants:: + ~FuzzerPassInterchangeZeroLikeConstants() = default; + +uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant( + opt::Instruction* declaration) { + auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant( + declaration->result_id()); + + // This pass only toggles zero-like constants + if (!constant->IsZero()) { + return 0; + } + + if (constant->AsScalarConstant()) { + return FindOrCreateNullConstant(declaration->type_id()); + } else if (constant->AsNullConstant()) { + // Add declaration of equivalent scalar constant + auto kind = constant->type()->kind(); + if (kind == opt::analysis::Type::kBool || + kind == opt::analysis::Type::kInteger || + kind == opt::analysis::Type::kFloat) { + return FindOrCreateZeroConstant(declaration->type_id()); + } + } + + return 0; +} + +void FuzzerPassInterchangeZeroLikeConstants::MaybeAddUseToReplace( + opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id, + std::vector>* + uses_to_replace) { + // Only consider this use if it is in a block + if (!GetIRContext()->get_instr_block(use_inst)) { + return; + } + + // Get the index of the operand restricted to input operands. + uint32_t in_operand_index = + fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index); + auto id_use_descriptor = + MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, in_operand_index); + uses_to_replace->emplace_back( + std::make_pair(id_use_descriptor, replacement_id)); +} + +void FuzzerPassInterchangeZeroLikeConstants::Apply() { + // Make vector keeping track of all the uses we want to replace. + // This is a vector of pairs, where the first element is an id use descriptor + // identifying the use of a constant id and the second is the id that should + // be used to replace it. + std::vector> uses_to_replace; + + for (auto constant : GetIRContext()->GetConstants()) { + uint32_t constant_id = constant->result_id(); + uint32_t toggled_id = FindOrCreateToggledConstant(constant); + + if (!toggled_id) { + // Not a zero-like constant + continue; + } + + // Record synonymous constants + ApplyTransformation( + TransformationRecordSynonymousConstants(constant_id, toggled_id)); + + // Find all the uses of the constant and, for each, probabilistically + // decide whether to replace it. + GetIRContext()->get_def_use_mgr()->ForEachUse( + constant_id, + [this, toggled_id, &uses_to_replace](opt::Instruction* use_inst, + uint32_t use_index) -> void { + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfInterchangingZeroLikeConstants())) { + MaybeAddUseToReplace(use_inst, use_index, toggled_id, + &uses_to_replace); + } + }); + } + + // Replace the ids + for (auto use_to_replace : uses_to_replace) { + MaybeApplyTransformation(TransformationReplaceIdWithSynonym( + use_to_replace.first, use_to_replace.second)); + } +} +} // namespace fuzz +} // namespace spvtools \ No newline at end of file diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h new file mode 100644 index 000000000..4fcc44e0b --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h @@ -0,0 +1,63 @@ +// Copyright (c) 2020 Stefano Milizia +// Copyright (c) 2020 Google LLC +// +// 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_INTERCHANGE_ZERO_LIKE_CONSTANTS_ +#define SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that: +// - Finds all the zero-like constant definitions in the module and adds the +// definitions of the corresponding synonym, recording the fact that they +// are synonymous. If the synonym is already in the module, it does not +// add a new one. +// - For each use of a zero-like constant, decides whether to change it to the +// id of the toggled constant. +class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass { + public: + FuzzerPassInterchangeZeroLikeConstants( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassInterchangeZeroLikeConstants() override; + + void Apply() override; + + private: + // Given the declaration of a zero-like constant, it finds or creates the + // corresponding toggled constant (a scalar constant of value 0 becomes a + // null constant of the same type and vice versa). + // Returns the id of the toggled instruction if the constant is zero-like, + // 0 otherwise. + uint32_t FindOrCreateToggledConstant(opt::Instruction* declaration); + + // Given an id use (described by an instruction and an index) and an id with + // which the original one should be replaced, adds a pair (with the elements + // being the corresponding id use descriptor and the replacement id) to + // |uses_to_replace| if the use is in an instruction block, otherwise does + // nothing. + void MaybeAddUseToReplace( + opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id, + std::vector>* + uses_to_replace); +}; + +} // namespace fuzz +} // namespace spvtools +#endif // SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_ diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp index 49778aea5..ac2a22f87 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp @@ -56,11 +56,7 @@ void FuzzerPassMergeBlocks::Apply() { uint32_t index = GetFuzzerContext()->RandomIndex(potential_transformations); auto transformation = potential_transformations.at(index); potential_transformations.erase(potential_transformations.begin() + index); - if (transformation.IsApplicable(GetIRContext(), - *GetTransformationContext())) { - transformation.Apply(GetIRContext(), GetTransformationContext()); - *GetTransformations()->add_transformation() = transformation.ToMessage(); - } + MaybeApplyTransformation(transformation); } } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp index 0f522d8bf..c92639a2c 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp @@ -477,28 +477,18 @@ void FuzzerPassObfuscateConstants::Apply() { skipped_opcode_count.clear(); } - switch (inst.opcode()) { - case SpvOpPhi: - // The instruction must not be an OpPhi, as we cannot insert - // instructions before an OpPhi. - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): - // there is scope for being less conservative. - break; - case SpvOpVariable: - // The instruction must not be an OpVariable, the only id that an - // OpVariable uses is an initializer id, which has to remain - // constant. - break; - default: - // Consider each operand of the instruction, and add a constant id - // use for the operand if relevant. - for (uint32_t in_operand_index = 0; - in_operand_index < inst.NumInOperands(); in_operand_index++) { - MaybeAddConstantIdUse(inst, in_operand_index, - base_instruction_result_id, - skipped_opcode_count, &constant_uses); - } - break; + // The instruction must not be an OpVariable, the only id that an + // OpVariable uses is an initializer id, which has to remain + // constant. + if (inst.opcode() != SpvOpVariable) { + // Consider each operand of the instruction, and add a constant id + // use for the operand if relevant. + for (uint32_t in_operand_index = 0; + in_operand_index < inst.NumInOperands(); in_operand_index++) { + MaybeAddConstantIdUse(inst, in_operand_index, + base_instruction_result_id, + skipped_opcode_count, &constant_uses); + } } if (!inst.HasResultId()) { diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp index 1665d050a..e4281d107 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp @@ -89,11 +89,7 @@ void FuzzerPassOutlineFunctions::Apply() { /*new_callee_result_id*/ GetFuzzerContext()->GetFreshId(), /*input_id_to_fresh_id*/ std::move(input_id_to_fresh_id), /*output_id_to_fresh_id*/ std::move(output_id_to_fresh_id)); - if (transformation.IsApplicable(GetIRContext(), - *GetTransformationContext())) { - transformation.Apply(GetIRContext(), GetTransformationContext()); - *GetTransformations()->add_transformation() = transformation.ToMessage(); - } + MaybeApplyTransformation(transformation); } } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.cpp index 27a2d6710..24c16fbbb 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.cpp @@ -67,12 +67,7 @@ void FuzzerPassPermuteBlocks::Apply() { // down indefinitely. while (true) { TransformationMoveBlockDown transformation(*id); - if (transformation.IsApplicable(GetIRContext(), - *GetTransformationContext())) { - transformation.Apply(GetIRContext(), GetTransformationContext()); - *GetTransformations()->add_transformation() = - transformation.ToMessage(); - } else { + if (!MaybeApplyTransformation(transformation)) { break; } } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp index 43fba5260..1e5d697f7 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp @@ -45,6 +45,7 @@ void FuzzerPassReplaceLinearAlgebraInstructions::Apply() { instruction->opcode() != SpvOpMatrixTimesScalar && instruction->opcode() != SpvOpVectorTimesMatrix && instruction->opcode() != SpvOpMatrixTimesVector && + instruction->opcode() != SpvOpMatrixTimesMatrix && instruction->opcode() != SpvOpDot) { return; } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp index 87b2b5d72..907c5b41d 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp @@ -58,7 +58,7 @@ void FuzzerPassReplaceParameterWithGlobal::Apply() { param->type_id()); assert(param_type && "Parameter has invalid type"); return TransformationReplaceParameterWithGlobal:: - CanReplaceFunctionParameterType(*param_type); + IsParameterTypeSupported(*param_type); })) { continue; } @@ -71,8 +71,9 @@ void FuzzerPassReplaceParameterWithGlobal::Apply() { param_type = GetIRContext()->get_type_mgr()->GetType(replaced_param->type_id()); assert(param_type && "Parameter has invalid type"); - } while (!TransformationReplaceParameterWithGlobal:: - CanReplaceFunctionParameterType(*param_type)); + } while ( + !TransformationReplaceParameterWithGlobal::IsParameterTypeSupported( + *param_type)); assert(replaced_param && "Unable to find a parameter to replace"); diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp index 15c67901e..481cd9607 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp @@ -96,11 +96,7 @@ void FuzzerPassSplitBlocks::Apply() { // If the position we have chosen turns out to be a valid place to split // the block, we apply the split. Otherwise the block just doesn't get // split. - if (transformation.IsApplicable(GetIRContext(), - *GetTransformationContext())) { - transformation.Apply(GetIRContext(), GetTransformationContext()); - *GetTransformations()->add_transformation() = transformation.ToMessage(); - } + MaybeApplyTransformation(transformation); } } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp index 359de9a63..a9b03b3bd 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp @@ -156,6 +156,25 @@ void AddUnreachableEdgeAndUpdateOpPhis( } } +bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id, + uint32_t loop_header_id) { + auto block = context->cfg()->block(block_id); + auto loop_header = context->cfg()->block(loop_header_id); + + // |block| and |loop_header| must be defined, |loop_header| must be in fact + // loop header and |block| must branch to it. + if (!(block && loop_header && loop_header->IsLoopHeader() && + block->IsSuccessor(loop_header))) { + return false; + } + + // |block_id| must be reachable and be dominated by |loop_header|. + opt::DominatorAnalysis* dominator_analysis = + context->GetDominatorAnalysis(loop_header->GetParent()); + return dominator_analysis->IsReachable(block_id) && + dominator_analysis->Dominates(loop_header_id, block_id); +} + bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id, uint32_t maybe_loop_header_id) { // We deem a block to be part of a loop's continue construct if the loop's @@ -533,6 +552,12 @@ uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id, return 0; } +uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst, + uint32_t absolute_index) { + // Subtract the number of non-input operands from the index + return absolute_index - inst.NumOperands() + inst.NumInOperands(); +} + bool IsNullConstantSupported(const opt::analysis::Type& type) { return type.AsBool() || type.AsInteger() || type.AsFloat() || type.AsMatrix() || type.AsVector() || type.AsArray() || @@ -1013,45 +1038,6 @@ void AddStructType(opt::IRContext* ir_context, uint32_t result_id, 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 36e860f45..94dcc837b 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h @@ -64,6 +64,12 @@ void AddUnreachableEdgeAndUpdateOpPhis( bool condition_value, const google::protobuf::RepeatedField& phi_ids); +// Returns true if and only if |loop_header_id| is a loop header and +// |block_id| is a reachable block branching to and dominated by +// |loop_header_id|. +bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id, + uint32_t loop_header_id); + // Returns true if and only if |maybe_loop_header_id| is a loop header and // |block_id| is in the continue construct of the associated loop. bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id, @@ -207,6 +213,11 @@ SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context, uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id, SpvStorageClass storage_class); +// Given an instruction |inst| and an operand absolute index |absolute_index|, +// returns the index of the operand restricted to the input operands. +uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst, + uint32_t absolute_index); + // Returns true if and only if |type| is one of the types for which it is legal // to have an OpConstantNull value. bool IsNullConstantSupported(const opt::analysis::Type& type); @@ -369,38 +380,12 @@ void AddVectorType(opt::IRContext* ir_context, uint32_t result_id, 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); +// Returns a bit pattern that represents a floating-point |value|. +inline uint32_t FloatToWord(float value) { + uint32_t result; + memcpy(&result, &value, sizeof(uint32_t)); + return result; +} } // namespace fuzzerutil diff --git a/3rdparty/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h b/3rdparty/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h index b801626bb..26b8672f1 100644 --- a/3rdparty/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h +++ b/3rdparty/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h @@ -39,6 +39,7 @@ // where warnings are ignored. #include "google/protobuf/util/json_util.h" +#include "google/protobuf/util/message_differencer.h" #include "source/fuzz/protobufs/spvtoolsfuzz.pb.h" #if defined(__clang__) diff --git a/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto index eebfccf23..53f571b84 100644 --- a/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -341,51 +341,51 @@ message Transformation { TransformationAddTypePointer add_type_pointer = 10; TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11; TransformationAddDeadContinue add_dead_continue = 12; - TransformationCopyObject copy_object = 13; - TransformationReplaceIdWithSynonym replace_id_with_synonym = 14; - TransformationSetSelectionControl set_selection_control = 15; - TransformationCompositeConstruct composite_construct = 16; - TransformationSetLoopControl set_loop_control = 17; - TransformationSetFunctionControl set_function_control = 18; - TransformationAddNoContractionDecoration add_no_contraction_decoration = 19; - TransformationSetMemoryOperandsMask set_memory_operands_mask = 20; - TransformationCompositeExtract composite_extract = 21; - TransformationVectorShuffle vector_shuffle = 22; - TransformationOutlineFunction outline_function = 23; - TransformationMergeBlocks merge_blocks = 24; - TransformationAddTypeVector add_type_vector = 25; - TransformationAddTypeArray add_type_array = 26; - TransformationAddTypeMatrix add_type_matrix = 27; - TransformationAddTypeStruct add_type_struct = 28; - TransformationAddTypeFunction add_type_function = 29; - TransformationAddConstantComposite add_constant_composite = 30; - TransformationAddGlobalVariable add_global_variable = 31; - TransformationAddGlobalUndef add_global_undef = 32; - TransformationAddFunction add_function = 33; - TransformationAddDeadBlock add_dead_block = 34; - TransformationAddLocalVariable add_local_variable = 35; - TransformationLoad load = 36; - TransformationStore store = 37; - TransformationFunctionCall function_call = 38; - TransformationAccessChain access_chain = 39; - TransformationEquationInstruction equation_instruction = 40; - TransformationSwapCommutableOperands swap_commutable_operands = 41; - TransformationPermuteFunctionParameters permute_function_parameters = 42; - TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 43; - TransformationAddConstantNull add_constant_null = 44; - TransformationComputeDataSynonymFactClosure compute_data_synonym_fact_closure = 45; - TransformationAdjustBranchWeights adjust_branch_weights = 46; - TransformationPushIdThroughVariable push_id_through_variable = 47; - TransformationAddSpecConstantOp add_spec_constant_op = 48; - TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 49; - TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 50; - TransformationPermutePhiOperands permute_phi_operands = 51; - TransformationAddParameter add_parameter = 52; - TransformationAddCopyMemory add_copy_memory = 53; - TransformationInvertComparisonOperator invert_comparison_operator = 54; - TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 55; - TransformationReplaceParameterWithGlobal replace_parameter_with_global = 56; - TransformationRecordSynonymousConstants record_synonymous_constants = 57; + TransformationReplaceIdWithSynonym replace_id_with_synonym = 13; + TransformationSetSelectionControl set_selection_control = 14; + TransformationCompositeConstruct composite_construct = 15; + TransformationSetLoopControl set_loop_control = 16; + TransformationSetFunctionControl set_function_control = 17; + TransformationAddNoContractionDecoration add_no_contraction_decoration = 18; + TransformationSetMemoryOperandsMask set_memory_operands_mask = 19; + TransformationCompositeExtract composite_extract = 20; + TransformationVectorShuffle vector_shuffle = 21; + TransformationOutlineFunction outline_function = 22; + TransformationMergeBlocks merge_blocks = 23; + TransformationAddTypeVector add_type_vector = 24; + TransformationAddTypeArray add_type_array = 25; + TransformationAddTypeMatrix add_type_matrix = 26; + TransformationAddTypeStruct add_type_struct = 27; + TransformationAddTypeFunction add_type_function = 28; + TransformationAddConstantComposite add_constant_composite = 29; + TransformationAddGlobalVariable add_global_variable = 30; + TransformationAddGlobalUndef add_global_undef = 31; + TransformationAddFunction add_function = 32; + TransformationAddDeadBlock add_dead_block = 33; + TransformationAddLocalVariable add_local_variable = 34; + TransformationLoad load = 35; + TransformationStore store = 36; + TransformationFunctionCall function_call = 37; + TransformationAccessChain access_chain = 38; + TransformationEquationInstruction equation_instruction = 39; + TransformationSwapCommutableOperands swap_commutable_operands = 40; + TransformationPermuteFunctionParameters permute_function_parameters = 41; + TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 42; + TransformationAddConstantNull add_constant_null = 43; + TransformationComputeDataSynonymFactClosure compute_data_synonym_fact_closure = 44; + TransformationAdjustBranchWeights adjust_branch_weights = 45; + TransformationPushIdThroughVariable push_id_through_variable = 46; + TransformationAddSpecConstantOp add_spec_constant_op = 47; + TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 48; + TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 49; + TransformationPermutePhiOperands permute_phi_operands = 50; + TransformationAddParameter add_parameter = 51; + TransformationAddCopyMemory add_copy_memory = 52; + TransformationInvertComparisonOperator invert_comparison_operator = 53; + TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 54; + TransformationReplaceParameterWithGlobal replace_parameter_with_global = 55; + TransformationRecordSynonymousConstants record_synonymous_constants = 56; + TransformationAddSynonym add_synonym = 57; // Add additional option using the next available number. } } @@ -705,6 +705,54 @@ message TransformationAddSpecConstantOp { } +message TransformationAddSynonym { + + // Adds a |synonymous_instruction| before |insert_before| instruction with + // and creates a fact that |result_id| and the result id of |synonymous_instruction| + // are synonymous. + + // Result id of the first synonym. + uint32 result_id = 1; + + // Type of the synonym to apply. Some types might produce instructions + // with commutative operands. Such types do not specify the order of the + // operands since we have a special transformation to swap commutable operands. + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3499): + // Consider adding more types here. + enum SynonymType { + // New synonym is derived by adding zero to the |result_id|. + ADD_ZERO = 0; + + // New synonym is derived by subtracting zero from the |result_id|. + SUB_ZERO = 1; + + // New synonym is derived by multiplying |result_id| by one. + MUL_ONE = 2; + + // New synonym is derived by applying OpCopyObject instruction to |result_id|. + COPY_OBJECT = 3; + + // New synonym is derived by applying OpLogicalOr to |result_id| with the second + // operand being 'false'. + LOGICAL_OR = 4; + + // New synonym is derived by applying OpLogicalAnd to |result_id| with the second + // operand being 'true'. + LOGICAL_AND = 5; + } + + // Type of the synonym to create. See SynonymType for more details. + SynonymType synonym_type = 2; + + // Fresh result id for a created synonym. + uint32 synonym_fresh_id = 3; + + // An instruction to insert a new synonym before. + InstructionDescriptor insert_before = 4; + +} + message TransformationAddTypeArray { // Adds an array type of the given element type and size to the module @@ -896,23 +944,6 @@ message TransformationComputeDataSynonymFactClosure { } -message TransformationCopyObject { - - // A transformation that introduces an OpCopyObject instruction to make a - // copy of an object. - - // Id of the object to be copied - uint32 object = 1; - - // A descriptor for an instruction in a block before which the new - // OpCopyObject instruction should be inserted - InstructionDescriptor instruction_to_insert_before = 2; - - // A fresh id for the copied object - uint32 fresh_id = 3; - -} - message TransformationEquationInstruction { // A transformation that adds an instruction to the module that defines an @@ -1230,13 +1261,13 @@ message TransformationReplaceLinearAlgebraInstruction { // OpMatrixTimesScalar // OpVectorTimesMatrix // OpMatrixTimesVector + // OpMatrixTimesMatrix // OpDot // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354): // Right now we only support certain operations. When this issue is addressed // the supporting comments can be removed. // To be supported in the future: // OpTranspose - // OpMatrixTimesMatrix // OpOuterProduct InstructionDescriptor instruction_descriptor = 2; diff --git a/3rdparty/spirv-tools/source/fuzz/replayer.cpp b/3rdparty/spirv-tools/source/fuzz/replayer.cpp index 6312cbafb..bc680d893 100644 --- a/3rdparty/spirv-tools/source/fuzz/replayer.cpp +++ b/3rdparty/spirv-tools/source/fuzz/replayer.cpp @@ -65,12 +65,20 @@ Replayer::ReplayerResultStatus Replayer::Run( const std::vector& binary_in, const protobufs::FactSequence& initial_facts, const protobufs::TransformationSequence& transformation_sequence_in, - std::vector* binary_out, + uint32_t num_transformations_to_apply, std::vector* binary_out, protobufs::TransformationSequence* transformation_sequence_out) const { // Check compatibility between the library version being linked with and the // header files being used. GOOGLE_PROTOBUF_VERIFY_VERSION; + if (num_transformations_to_apply > + static_cast(transformation_sequence_in.transformation_size())) { + impl_->consumer(SPV_MSG_ERROR, nullptr, {}, + "The number of transformations to be replayed must not " + "exceed the size of the transformation sequence."); + return Replayer::ReplayerResultStatus::kTooManyTransformationsRequested; + } + spvtools::SpirvTools tools(impl_->target_env); if (!tools.IsValid()) { impl_->consumer(SPV_MSG_ERROR, nullptr, {}, @@ -104,7 +112,13 @@ Replayer::ReplayerResultStatus Replayer::Run( impl_->validator_options); // Consider the transformation proto messages in turn. + uint32_t counter = 0; for (auto& message : transformation_sequence_in.transformation()) { + if (counter >= num_transformations_to_apply) { + break; + } + counter++; + auto transformation = Transformation::FromMessage(message); // Check whether the transformation can be applied. diff --git a/3rdparty/spirv-tools/source/fuzz/replayer.h b/3rdparty/spirv-tools/source/fuzz/replayer.h index e77d8400c..d6395aaa6 100644 --- a/3rdparty/spirv-tools/source/fuzz/replayer.h +++ b/3rdparty/spirv-tools/source/fuzz/replayer.h @@ -34,6 +34,7 @@ class Replayer { kFailedToCreateSpirvToolsInterface, kInitialBinaryInvalid, kReplayValidationFailure, + kTooManyTransformationsRequested, }; // Constructs a replayer from the given target environment. @@ -52,16 +53,17 @@ class Replayer { // invoked once for each message communicated from the library. void SetMessageConsumer(MessageConsumer consumer); - // Transforms |binary_in| to |binary_out| by attempting to apply the - // transformations from |transformation_sequence_in|. Initial facts about the - // input binary and the context in which it will execute are provided via - // |initial_facts|. The transformations that were successfully applied are - // returned via |transformation_sequence_out|. + // Transforms |binary_in| to |binary_out| by attempting to apply the first + // |num_transformations_to_apply| transformations from + // |transformation_sequence_in|. Initial facts about the input binary and the + // context in which it will execute are provided via |initial_facts|. The + // transformations that were successfully applied are returned via + // |transformation_sequence_out|. ReplayerResultStatus Run( const std::vector& binary_in, const protobufs::FactSequence& initial_facts, const protobufs::TransformationSequence& transformation_sequence_in, - std::vector* binary_out, + uint32_t num_transformations_to_apply, std::vector* binary_out, protobufs::TransformationSequence* transformation_sequence_out) const; private: diff --git a/3rdparty/spirv-tools/source/fuzz/shrinker.cpp b/3rdparty/spirv-tools/source/fuzz/shrinker.cpp index 002e8a7ef..85a06fd83 100644 --- a/3rdparty/spirv-tools/source/fuzz/shrinker.cpp +++ b/3rdparty/spirv-tools/source/fuzz/shrinker.cpp @@ -124,6 +124,7 @@ Shrinker::ShrinkerResultStatus Shrinker::Run( if (Replayer(impl_->target_env, impl_->validate_during_replay, impl_->validator_options) .Run(binary_in, initial_facts, transformation_sequence_in, + transformation_sequence_in.transformation_size(), ¤t_best_binary, ¤t_best_transformations) != Replayer::ReplayerResultStatus::kComplete) { return ShrinkerResultStatus::kReplayFailed; @@ -196,6 +197,7 @@ Shrinker::ShrinkerResultStatus Shrinker::Run( if (Replayer(impl_->target_env, impl_->validate_during_replay, impl_->validator_options) .Run(binary_in, initial_facts, transformations_with_chunk_removed, + transformations_with_chunk_removed.transformation_size(), &next_binary, &next_transformation_sequence) != Replayer::ReplayerResultStatus::kComplete) { // Replay should not fail; if it does, we need to abort shrinking. diff --git a/3rdparty/spirv-tools/source/fuzz/transformation.cpp b/3rdparty/spirv-tools/source/fuzz/transformation.cpp index 4adb39c34..4455107ec 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation.cpp @@ -34,6 +34,7 @@ #include "source/fuzz/transformation_add_no_contraction_decoration.h" #include "source/fuzz/transformation_add_parameter.h" #include "source/fuzz/transformation_add_spec_constant_op.h" +#include "source/fuzz/transformation_add_synonym.h" #include "source/fuzz/transformation_add_type_array.h" #include "source/fuzz/transformation_add_type_boolean.h" #include "source/fuzz/transformation_add_type_float.h" @@ -47,7 +48,6 @@ #include "source/fuzz/transformation_composite_construct.h" #include "source/fuzz/transformation_composite_extract.h" #include "source/fuzz/transformation_compute_data_synonym_fact_closure.h" -#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" @@ -131,6 +131,8 @@ std::unique_ptr Transformation::FromMessage( case protobufs::Transformation::TransformationCase::kAddSpecConstantOp: return MakeUnique( message.add_spec_constant_op()); + case protobufs::Transformation::TransformationCase::kAddSynonym: + return MakeUnique(message.add_synonym()); case protobufs::Transformation::TransformationCase::kAddTypeArray: return MakeUnique(message.add_type_array()); case protobufs::Transformation::TransformationCase::kAddTypeBoolean: @@ -165,8 +167,6 @@ std::unique_ptr Transformation::FromMessage( kComputeDataSynonymFactClosure: return MakeUnique( message.compute_data_synonym_fact_closure()); - case protobufs::Transformation::TransformationCase::kCopyObject: - return MakeUnique(message.copy_object()); case protobufs::Transformation::TransformationCase::kEquationInstruction: return MakeUnique( message.equation_instruction()); diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_access_chain.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_access_chain.cpp index ff17c3685..f805bab71 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_access_chain.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_access_chain.cpp @@ -74,9 +74,9 @@ bool TransformationAccessChain::IsApplicable( switch (pointer->opcode()) { case SpvOpConstantNull: case SpvOpUndef: - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3185): When - // fuzzing for real we would like an 'assert(false)' here. But we also - // want to be able to write negative unit tests. + assert( + false && + "Access chains should not be created from null/undefined pointers"); return false; default: break; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.cpp index 44c9abada..695c23fe7 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.cpp @@ -85,12 +85,10 @@ bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow( // loop as part of the loop, but it is not legal to jump from a loop's // continue construct to the loop's merge (except from the back-edge block), // so we need to check for this case. - // - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2577): We do not - // currently allow a dead break from a back edge block, but we could and - // ultimately should. return !fuzzerutil::BlockIsInLoopContinueConstruct( - ir_context, message_.from_block(), containing_construct); + ir_context, message_.from_block(), containing_construct) || + fuzzerutil::BlockIsBackEdge(ir_context, message_.from_block(), + containing_construct); } // Case (3) holds if and only if |to_block| is the merge block for this @@ -102,7 +100,9 @@ bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow( message_.to_block() == ir_context->cfg()->block(containing_loop_header)->MergeBlockId()) { return !fuzzerutil::BlockIsInLoopContinueConstruct( - ir_context, message_.from_block(), containing_loop_header); + ir_context, message_.from_block(), containing_loop_header) || + fuzzerutil::BlockIsBackEdge(ir_context, message_.from_block(), + containing_loop_header); } return false; } diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.cpp index b2dd7be9b..b64c7e5cc 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.cpp @@ -56,7 +56,7 @@ bool TransformationAddParameter::IsApplicable( const auto* initializer_type = ir_context->get_type_mgr()->GetType(initializer_inst->type_id()); - if (!initializer_type || initializer_type->AsVoid()) { + if (!initializer_type || !IsParameterTypeSupported(*initializer_type)) { return false; } @@ -139,5 +139,28 @@ protobufs::Transformation TransformationAddParameter::ToMessage() const { return result; } +bool TransformationAddParameter::IsParameterTypeSupported( + const opt::analysis::Type& type) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403): + // Think about other type instructions we can add here. + switch (type.kind()) { + case opt::analysis::Type::kBool: + case opt::analysis::Type::kInteger: + case opt::analysis::Type::kFloat: + case opt::analysis::Type::kArray: + case opt::analysis::Type::kMatrix: + case opt::analysis::Type::kVector: + return true; + case opt::analysis::Type::kStruct: + return std::all_of(type.AsStruct()->element_types().begin(), + type.AsStruct()->element_types().end(), + [](const opt::analysis::Type* element_type) { + return IsParameterTypeSupported(*element_type); + }); + default: + return false; + } +} + } // 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 index 42b5fbb9a..e6b901929 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.h @@ -35,7 +35,8 @@ class TransformationAddParameter : public Transformation { // - |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. + // module. Instruction's type must be supported by this transformation + // as specified by IsParameterTypeSupported function. // - |parameter_fresh_id| and |function_type_fresh_id| are fresh ids and are // not equal. bool IsApplicable( @@ -52,6 +53,10 @@ class TransformationAddParameter : public Transformation { protobufs::Transformation ToMessage() const override; + // Returns true if the type of the parameter is supported by this + // transformation. + static bool IsParameterTypeSupported(const opt::analysis::Type& type); + private: protobufs::TransformationAddParameter message_; }; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.cpp new file mode 100644 index 000000000..749d7872d --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.cpp @@ -0,0 +1,299 @@ +// 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_synonym.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddSynonym::TransformationAddSynonym( + protobufs::TransformationAddSynonym message) + : message_(std::move(message)) {} + +TransformationAddSynonym::TransformationAddSynonym( + uint32_t result_id, + protobufs::TransformationAddSynonym::SynonymType synonym_type, + uint32_t synonym_fresh_id, + const protobufs::InstructionDescriptor& insert_before) { + message_.set_result_id(result_id); + message_.set_synonym_type(synonym_type); + message_.set_synonym_fresh_id(synonym_fresh_id); + *message_.mutable_insert_before() = insert_before; +} + +bool TransformationAddSynonym::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + assert(protobufs::TransformationAddSynonym::SynonymType_IsValid( + message_.synonym_type()) && + "Synonym type is invalid"); + + // |synonym_fresh_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.synonym_fresh_id())) { + return false; + } + + // Check that |message_.result_id| is valid. + auto* synonym = ir_context->get_def_use_mgr()->GetDef(message_.result_id()); + if (!synonym) { + return false; + } + + // Check that we can apply |synonym_type| to |result_id|. + if (!IsInstructionValid(ir_context, synonym, message_.synonym_type())) { + return false; + } + + // Check that |insert_before| is valid. + auto* insert_before_inst = + FindInstruction(message_.insert_before(), ir_context); + if (!insert_before_inst) { + return false; + } + + // Check that we can insert |message._synonymous_instruction| before + // |message_.insert_before| instruction. We use OpIAdd to represent some + // instruction that can produce a synonym. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, + insert_before_inst)) { + return false; + } + + // A constant instruction must be present in the module if required. + if (IsAdditionalConstantRequired(message_.synonym_type()) && + MaybeGetConstantId(ir_context) == 0) { + return false; + } + + // Domination rules must be satisfied. + return fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before_inst, message_.result_id()); +} + +void TransformationAddSynonym::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Add a synonymous instruction. + FindInstruction(message_.insert_before(), ir_context) + ->InsertBefore(MakeSynonymousInstruction(ir_context)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id()); + + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + // Propagate PointeeValueIsIrrelevant fact. + const auto* new_synonym_type = ir_context->get_type_mgr()->GetType( + fuzzerutil::GetTypeId(ir_context, message_.synonym_fresh_id())); + assert(new_synonym_type && "New synonym should have a valid type"); + + if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant( + message_.result_id()) && + new_synonym_type->AsPointer()) { + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.synonym_fresh_id()); + } + + // Mark two ids as synonymous. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.result_id(), {}), + MakeDataDescriptor(message_.synonym_fresh_id(), {}), ir_context); +} + +protobufs::Transformation TransformationAddSynonym::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_synonym() = message_; + return result; +} + +bool TransformationAddSynonym::IsInstructionValid( + opt::IRContext* ir_context, opt::Instruction* inst, + protobufs::TransformationAddSynonym::SynonymType synonym_type) { + // Instruction must have a result id, type id. We skip OpUndef and + // OpConstantNull. + if (!inst || !inst->result_id() || !inst->type_id() || + inst->opcode() == SpvOpUndef || inst->opcode() == SpvOpConstantNull) { + return false; + } + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): + // We can't create a synonym of an irrelevant id. + switch (synonym_type) { + case protobufs::TransformationAddSynonym::ADD_ZERO: + case protobufs::TransformationAddSynonym::SUB_ZERO: + case protobufs::TransformationAddSynonym::MUL_ONE: { + // The instruction must be either scalar or vector of integers or floats. + const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(type && "Instruction's result id is invalid"); + + if (const auto* vector = type->AsVector()) { + return vector->element_type()->AsInteger() || + vector->element_type()->AsFloat(); + } + + return type->AsInteger() || type->AsFloat(); + } + case protobufs::TransformationAddSynonym::COPY_OBJECT: + return fuzzerutil::CanMakeSynonymOf(ir_context, inst); + case protobufs::TransformationAddSynonym::LOGICAL_AND: + case protobufs::TransformationAddSynonym::LOGICAL_OR: { + // The instruction must be either a scalar or a vector of booleans. + const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(type && "Instruction's result id is invalid"); + return (type->AsVector() && type->AsVector()->element_type()->AsBool()) || + type->AsBool(); + } + default: + assert(false && "Synonym type is not supported"); + return false; + } +} + +std::unique_ptr +TransformationAddSynonym::MakeSynonymousInstruction( + opt::IRContext* ir_context) const { + auto synonym_type_id = + fuzzerutil::GetTypeId(ir_context, message_.result_id()); + assert(synonym_type_id && "Synonym has invalid type id"); + + switch (message_.synonym_type()) { + case protobufs::TransformationAddSynonym::SUB_ZERO: + case protobufs::TransformationAddSynonym::MUL_ONE: + case protobufs::TransformationAddSynonym::ADD_ZERO: { + const auto* synonym_type = + ir_context->get_type_mgr()->GetType(synonym_type_id); + assert(synonym_type && "Synonym has invalid type"); + + // Compute instruction's opcode based on the type of the operand. + // We have already checked that the operand is either a scalar or a vector + // of either integers or floats. + auto is_integral = + (synonym_type->AsVector() && + synonym_type->AsVector()->element_type()->AsInteger()) || + synonym_type->AsInteger(); + auto opcode = SpvOpNop; + switch (message_.synonym_type()) { + case protobufs::TransformationAddSynonym::SUB_ZERO: + opcode = is_integral ? SpvOpISub : SpvOpFSub; + break; + case protobufs::TransformationAddSynonym::MUL_ONE: + opcode = is_integral ? SpvOpIMul : SpvOpFMul; + break; + case protobufs::TransformationAddSynonym::ADD_ZERO: + opcode = is_integral ? SpvOpIAdd : SpvOpFAdd; + break; + default: + assert(false && "Unreachable"); + break; + } + + return MakeUnique( + ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.result_id()}}, + {SPV_OPERAND_TYPE_ID, {MaybeGetConstantId(ir_context)}}}); + } + case protobufs::TransformationAddSynonym::COPY_OBJECT: + return MakeUnique( + ir_context, SpvOpCopyObject, synonym_type_id, + message_.synonym_fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.result_id()}}}); + case protobufs::TransformationAddSynonym::LOGICAL_OR: + case protobufs::TransformationAddSynonym::LOGICAL_AND: { + auto opcode = message_.synonym_type() == + protobufs::TransformationAddSynonym::LOGICAL_OR + ? SpvOpLogicalOr + : SpvOpLogicalAnd; + return MakeUnique( + ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.result_id()}}, + {SPV_OPERAND_TYPE_ID, {MaybeGetConstantId(ir_context)}}}); + } + default: + assert(false && "Unhandled synonym type"); + return nullptr; + } +} + +uint32_t TransformationAddSynonym::MaybeGetConstantId( + opt::IRContext* ir_context) const { + assert(IsAdditionalConstantRequired(message_.synonym_type()) && + "Synonym type doesn't require an additional constant"); + + auto synonym_type_id = + fuzzerutil::GetTypeId(ir_context, message_.result_id()); + assert(synonym_type_id && "Synonym has invalid type id"); + + switch (message_.synonym_type()) { + case protobufs::TransformationAddSynonym::ADD_ZERO: + case protobufs::TransformationAddSynonym::SUB_ZERO: + case protobufs::TransformationAddSynonym::LOGICAL_OR: + return fuzzerutil::MaybeGetZeroConstant(ir_context, synonym_type_id); + case protobufs::TransformationAddSynonym::MUL_ONE: + case protobufs::TransformationAddSynonym::LOGICAL_AND: { + auto synonym_type = ir_context->get_type_mgr()->GetType(synonym_type_id); + assert(synonym_type && "Synonym has invalid type"); + + if (const auto* vector = synonym_type->AsVector()) { + auto element_type_id = + ir_context->get_type_mgr()->GetId(vector->element_type()); + assert(element_type_id && "Vector's element type is invalid"); + + auto one_word = + vector->element_type()->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u; + if (auto scalar_one_id = fuzzerutil::MaybeGetScalarConstant( + ir_context, {one_word}, element_type_id)) { + return fuzzerutil::MaybeGetCompositeConstant( + ir_context, + std::vector(vector->element_count(), scalar_one_id), + synonym_type_id); + } + + return 0; + } else { + return fuzzerutil::MaybeGetScalarConstant( + ir_context, + {synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u}, + synonym_type_id); + } + } + default: + // The assertion at the beginning of the function will fail in the debug + // mode. + return 0; + } +} + +bool TransformationAddSynonym::IsAdditionalConstantRequired( + protobufs::TransformationAddSynonym::SynonymType synonym_type) { + switch (synonym_type) { + case protobufs::TransformationAddSynonym::ADD_ZERO: + case protobufs::TransformationAddSynonym::SUB_ZERO: + case protobufs::TransformationAddSynonym::LOGICAL_OR: + case protobufs::TransformationAddSynonym::MUL_ONE: + case protobufs::TransformationAddSynonym::LOGICAL_AND: + return true; + default: + return false; + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.h b/3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.h new file mode 100644 index 000000000..85465661f --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.h @@ -0,0 +1,86 @@ +// 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_SYNONYM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_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 TransformationAddSynonym : public Transformation { + public: + explicit TransformationAddSynonym( + protobufs::TransformationAddSynonym message); + + TransformationAddSynonym( + uint32_t result_id, + protobufs::TransformationAddSynonym::SynonymType synonym_type, + uint32_t synonym_fresh_id, + const protobufs::InstructionDescriptor& insert_before); + + // - |result_id| must be a valid result id of some instruction in the module. + // - |synonym_type| is a type of the synonymous instruction that will be + // created. + // - |synonym_fresh_id| is a fresh id. + // - |insert_before| must be a valid instruction descriptor and we must be + // able to insert a new synonymous instruction before |insert_before|. + // - |result_id| must be available before |insert_before|. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Creates a new synonymous instruction according to the |synonym_type| with + // result id |synonym_fresh_id|. + // Inserts that instruction before |insert_before| and creates a fact + // that the |synonym_fresh_id| and the |result_id| are synonymous. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if we can create a synonym of |inst| according to the + // |synonym_type|. + static bool IsInstructionValid( + opt::IRContext* ir_context, opt::Instruction* inst, + protobufs::TransformationAddSynonym::SynonymType synonym_type); + + // Returns true if |synonym_type| requires an additional constant instruction + // to be present in the module. + static bool IsAdditionalConstantRequired( + protobufs::TransformationAddSynonym::SynonymType synonym_type); + + private: + // Returns a new instruction which is synonymous to |message_.result_id|. + std::unique_ptr MakeSynonymousInstruction( + opt::IRContext* ir_context) const; + + // Returns a result id of a constant instruction that is required to be + // present in some synonym types (e.g. returns a result id of a zero constant + // for ADD_ZERO synonym type). Returns 0 if no such instruction is present in + // the module. This method should only be called when + // IsAdditionalConstantRequired returns true. + uint32_t MaybeGetConstantId(opt::IRContext* ir_context) const; + + protobufs::TransformationAddSynonym message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_context.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_context.cpp index 9c8a90f69..6c2dfdff8 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_context.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_context.cpp @@ -18,10 +18,8 @@ namespace spvtools { namespace fuzz { TransformationContext::TransformationContext( - FactManager* transformation_context, - spv_validator_options validator_options) - : fact_manager_(transformation_context), - validator_options_(validator_options) {} + FactManager* fact_manager, spv_validator_options validator_options) + : fact_manager_(fact_manager), validator_options_(validator_options) {} TransformationContext::~TransformationContext() = default; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_copy_object.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_copy_object.cpp deleted file mode 100644 index 7b5b5c9e0..000000000 --- a/3rdparty/spirv-tools/source/fuzz/transformation_copy_object.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) 2019 Google LLC -// -// 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_copy_object.h" - -#include "source/fuzz/data_descriptor.h" -#include "source/fuzz/fuzzer_util.h" -#include "source/fuzz/instruction_descriptor.h" -#include "source/opt/instruction.h" -#include "source/util/make_unique.h" - -namespace spvtools { -namespace fuzz { - -TransformationCopyObject::TransformationCopyObject( - const protobufs::TransformationCopyObject& message) - : message_(message) {} - -TransformationCopyObject::TransformationCopyObject( - uint32_t object, - const protobufs::InstructionDescriptor& instruction_to_insert_before, - uint32_t fresh_id) { - message_.set_object(object); - *message_.mutable_instruction_to_insert_before() = - instruction_to_insert_before; - message_.set_fresh_id(fresh_id); -} - -bool TransformationCopyObject::IsApplicable( - opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { - if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { - // We require the id for the object copy to be unused. - return false; - } - // The id of the object to be copied must exist - auto object_inst = ir_context->get_def_use_mgr()->GetDef(message_.object()); - if (!object_inst) { - return false; - } - if (!fuzzerutil::CanMakeSynonymOf(ir_context, object_inst)) { - return false; - } - - auto insert_before = - FindInstruction(message_.instruction_to_insert_before(), ir_context); - if (!insert_before) { - // The instruction before which the copy should be inserted was not found. - return false; - } - - if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject, - insert_before)) { - return false; - } - - // |message_object| must be available directly before the point where we want - // to add the copy. - return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, - message_.object()); -} - -void TransformationCopyObject::Apply( - opt::IRContext* ir_context, - TransformationContext* transformation_context) const { - auto object_inst = ir_context->get_def_use_mgr()->GetDef(message_.object()); - assert(object_inst && "The object to be copied must exist."); - auto insert_before_inst = - FindInstruction(message_.instruction_to_insert_before(), ir_context); - auto destination_block = ir_context->get_instr_block(insert_before_inst); - assert(destination_block && "The base instruction must be in a block."); - auto insert_before = fuzzerutil::GetIteratorForInstruction( - destination_block, insert_before_inst); - assert(insert_before != destination_block->end() && - "There must be an instruction before which the copy can be inserted."); - - opt::Instruction::OperandList operands = { - {SPV_OPERAND_TYPE_ID, {message_.object()}}}; - insert_before->InsertBefore(MakeUnique( - ir_context, SpvOp::SpvOpCopyObject, object_inst->type_id(), - message_.fresh_id(), operands)); - - fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); - - transformation_context->GetFactManager()->AddFactDataSynonym( - MakeDataDescriptor(message_.object(), {}), - MakeDataDescriptor(message_.fresh_id(), {}), ir_context); - - if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant( - message_.object())) { - transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( - message_.fresh_id()); - } -} - -protobufs::Transformation TransformationCopyObject::ToMessage() const { - protobufs::Transformation result; - *result.mutable_copy_object() = message_; - return result; -} - -} // namespace fuzz -} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_copy_object.h b/3rdparty/spirv-tools/source/fuzz/transformation_copy_object.h deleted file mode 100644 index 80d57aebe..000000000 --- a/3rdparty/spirv-tools/source/fuzz/transformation_copy_object.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2019 Google LLC -// -// 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_COPY_OBJECT_H_ -#define SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_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 TransformationCopyObject : public Transformation { - public: - explicit TransformationCopyObject( - const protobufs::TransformationCopyObject& message); - - TransformationCopyObject( - uint32_t object, - const protobufs::InstructionDescriptor& instruction_to_insert_before, - uint32_t fresh_id); - - // - |message_.fresh_id| must not be used by the module. - // - |message_.object| must be a result id that is a legitimate operand for - // OpCopyObject. In particular, it must be the id of an instruction that - // has a result type - // - |message_.object| must not be the target of any decoration. - // TODO(afd): consider copying decorations along with objects. - // - |message_.base_instruction_id| must be the result id of an instruction - // 'base' in some block 'blk'. - // - 'blk' must contain an instruction 'inst' located |message_.offset| - // instructions after 'base' (if |message_.offset| = 0 then 'inst' = - // 'base'). - // - It must be legal to insert an OpCopyObject instruction directly - // before 'inst'. - // - |message_.object| must be available directly before 'inst'. - // - |message_.object| must not be a null pointer or undefined pointer (so as - // to make it legal to load from copied pointers). - bool IsApplicable( - opt::IRContext* ir_context, - const TransformationContext& transformation_context) const override; - - // - A new instruction, - // %|message_.fresh_id| = OpCopyObject %ty %|message_.object| - // is added directly before the instruction at |message_.insert_after_id| + - // |message_|.offset, where %ty is the type of |message_.object|. - // - The fact that |message_.fresh_id| and |message_.object| are synonyms - // is added to the fact manager in |transformation_context|. - // - If |message_.object| is a pointer whose pointee value is known to be - // irrelevant, the analogous fact is added to the fact manager in - // |transformation_context| about |message_.fresh_id|. - void Apply(opt::IRContext* ir_context, - TransformationContext* transformation_context) const override; - - protobufs::Transformation ToMessage() const override; - - private: - protobufs::TransformationCopyObject message_; -}; - -} // namespace fuzz -} // namespace spvtools - -#endif // SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.cpp index 5c3141714..5f2c7f78a 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.cpp @@ -42,6 +42,7 @@ bool TransformationEquationInstruction::IsApplicable( if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } + // The instruction to insert before must exist. auto insert_before = FindInstruction(message_.instruction_to_insert_before(), ir_context); @@ -64,7 +65,7 @@ bool TransformationEquationInstruction::IsApplicable( } } - return MaybeGetResultType(ir_context) != 0; + return MaybeGetResultTypeId(ir_context) != 0; } void TransformationEquationInstruction::Apply( @@ -82,7 +83,8 @@ void TransformationEquationInstruction::Apply( FindInstruction(message_.instruction_to_insert_before(), ir_context) ->InsertBefore(MakeUnique( ir_context, static_cast(message_.opcode()), - MaybeGetResultType(ir_context), message_.fresh_id(), in_operands)); + MaybeGetResultTypeId(ir_context), message_.fresh_id(), + std::move(in_operands))); ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); @@ -97,12 +99,108 @@ protobufs::Transformation TransformationEquationInstruction::ToMessage() const { return result; } -uint32_t TransformationEquationInstruction::MaybeGetResultType( +uint32_t TransformationEquationInstruction::MaybeGetResultTypeId( opt::IRContext* ir_context) const { - switch (static_cast(message_.opcode())) { + auto opcode = static_cast(message_.opcode()); + switch (opcode) { + case SpvOpConvertUToF: + case SpvOpConvertSToF: { + if (message_.in_operand_id_size() != 1) { + return 0; + } + + const auto* type = ir_context->get_type_mgr()->GetType( + fuzzerutil::GetTypeId(ir_context, message_.in_operand_id(0))); + if (!type) { + return 0; + } + + if (const auto* vector = type->AsVector()) { + if (!vector->element_type()->AsInteger()) { + return 0; + } + + if (auto element_type_id = fuzzerutil::MaybeGetFloatType( + ir_context, vector->element_type()->AsInteger()->width())) { + return fuzzerutil::MaybeGetVectorType(ir_context, element_type_id, + vector->element_count()); + } + + return 0; + } else { + if (!type->AsInteger()) { + return 0; + } + + return fuzzerutil::MaybeGetFloatType(ir_context, + type->AsInteger()->width()); + } + } + case SpvOpBitcast: { + if (message_.in_operand_id_size() != 1) { + return 0; + } + + const auto* operand_inst = + ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0)); + if (!operand_inst) { + return 0; + } + + const auto* operand_type = + ir_context->get_type_mgr()->GetType(operand_inst->type_id()); + if (!operand_type) { + return 0; + } + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3539): + // The only constraint on the types of OpBitcast's parameters is that + // they must have the same number of bits. Consider improving the code + // below to support this in full. + if (const auto* vector = operand_type->AsVector()) { + uint32_t component_type_id; + if (const auto* int_type = vector->element_type()->AsInteger()) { + component_type_id = + fuzzerutil::MaybeGetFloatType(ir_context, int_type->width()); + } else if (const auto* float_type = vector->element_type()->AsFloat()) { + component_type_id = fuzzerutil::MaybeGetIntegerType( + ir_context, float_type->width(), true); + if (component_type_id == 0 || + fuzzerutil::MaybeGetVectorType(ir_context, component_type_id, + vector->element_count()) == 0) { + component_type_id = fuzzerutil::MaybeGetIntegerType( + ir_context, float_type->width(), false); + } + } else { + assert(false && "Only vectors of numerical components are supported"); + return 0; + } + + if (component_type_id == 0) { + return 0; + } + + return fuzzerutil::MaybeGetVectorType(ir_context, component_type_id, + vector->element_count()); + } else if (const auto* int_type = operand_type->AsInteger()) { + return fuzzerutil::MaybeGetFloatType(ir_context, int_type->width()); + } else if (const auto* float_type = operand_type->AsFloat()) { + if (auto existing_id = fuzzerutil::MaybeGetIntegerType( + ir_context, float_type->width(), true)) { + return existing_id; + } + + return fuzzerutil::MaybeGetIntegerType(ir_context, float_type->width(), + false); + } else { + assert(false && + "Operand is not a scalar or a vector of numerical type"); + return 0; + } + } case SpvOpIAdd: case SpvOpISub: { - if (message_.in_operand_id().size() != 2) { + if (message_.in_operand_id_size() != 2) { return 0; } uint32_t first_operand_width = 0; @@ -175,7 +273,6 @@ uint32_t TransformationEquationInstruction::MaybeGetResultType( } return operand_inst->type_id(); } - default: assert(false && "Inappropriate opcode for equation instruction."); return 0; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.h b/3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.h index 7eec9c659..9ed01a87a 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.h @@ -63,11 +63,9 @@ class TransformationEquationInstruction : public Transformation { protobufs::Transformation ToMessage() const override; private: - // A helper that, in one fell swoop, checks that |message_.opcode| and the ids - // in |message_.in_operand_id| are compatible, and that the module contains - // an appropriate result type id. If all is well, the result type id is - // returned. Otherwise, 0 is returned. - uint32_t MaybeGetResultType(opt::IRContext* ir_context) const; + // Returns type id for the equation instruction. Returns 0 if result type does + // not exist. + uint32_t MaybeGetResultTypeId(opt::IRContext* ir_context) const; protobufs::TransformationEquationInstruction message_; }; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp index be8ce5e74..a4e6d8b0c 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp @@ -94,6 +94,18 @@ bool TransformationInvertComparisonOperator::IsInversionSupported( case SpvOpULessThanEqual: case SpvOpIEqual: case SpvOpINotEqual: + case SpvOpFOrdEqual: + case SpvOpFUnordEqual: + case SpvOpFOrdNotEqual: + case SpvOpFUnordNotEqual: + case SpvOpFOrdLessThan: + case SpvOpFUnordLessThan: + case SpvOpFOrdLessThanEqual: + case SpvOpFUnordLessThanEqual: + case SpvOpFOrdGreaterThan: + case SpvOpFUnordGreaterThan: + case SpvOpFOrdGreaterThanEqual: + case SpvOpFUnordGreaterThanEqual: return true; default: return false; @@ -124,6 +136,30 @@ SpvOp TransformationInvertComparisonOperator::InvertOpcode(SpvOp opcode) { return SpvOpINotEqual; case SpvOpINotEqual: return SpvOpIEqual; + case SpvOpFOrdEqual: + return SpvOpFUnordNotEqual; + case SpvOpFUnordEqual: + return SpvOpFOrdNotEqual; + case SpvOpFOrdNotEqual: + return SpvOpFUnordEqual; + case SpvOpFUnordNotEqual: + return SpvOpFOrdEqual; + case SpvOpFOrdLessThan: + return SpvOpFUnordGreaterThanEqual; + case SpvOpFUnordLessThan: + return SpvOpFOrdGreaterThanEqual; + case SpvOpFOrdLessThanEqual: + return SpvOpFUnordGreaterThan; + case SpvOpFUnordLessThanEqual: + return SpvOpFOrdGreaterThan; + case SpvOpFOrdGreaterThan: + return SpvOpFUnordLessThanEqual; + case SpvOpFUnordGreaterThan: + return SpvOpFOrdLessThanEqual; + case SpvOpFOrdGreaterThanEqual: + return SpvOpFUnordLessThan; + case SpvOpFUnordGreaterThanEqual: + return SpvOpFOrdLessThan; default: // The program will fail in the debug mode because of the assertion // at the beginning of the function. diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp index 0206c77bc..a0bf79740 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp @@ -127,22 +127,20 @@ void TransformationPushIdThroughVariable::Apply( message_.initializer_id()); } - // Stores value id to variable id. - FindInstruction(message_.instruction_descriptor(), ir_context) - ->InsertBefore(MakeUnique( - ir_context, SpvOpStore, 0, 0, - opt::Instruction::OperandList( - {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}, - {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}))); - - // Loads variable id to value synonym id. + // First, insert the OpLoad instruction before |instruction_descriptor| and + // then insert the OpStore instruction before the OpLoad instruction. fuzzerutil::UpdateModuleIdBound(ir_context, message_.value_synonym_id()); FindInstruction(message_.instruction_descriptor(), ir_context) ->InsertBefore(MakeUnique( ir_context, SpvOpLoad, value_instruction->type_id(), message_.value_synonym_id(), opt::Instruction::OperandList( - {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}}))); + {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}}))) + ->InsertBefore(MakeUnique( + ir_context, SpvOpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}))); ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp index d6f17fc37..6e22e7c6d 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp @@ -243,22 +243,15 @@ bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable( return false; } - switch (instruction->opcode()) { - case SpvOpPhi: - // The instruction must not be an OpPhi, as we cannot insert a binary - // operator instruction before an OpPhi. - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): there is - // scope for being less conservative. - return false; - case SpvOpVariable: - // The instruction must not be an OpVariable, because (a) we cannot insert - // a binary operator before an OpVariable, but in any case (b) the - // constant we would be replacing is the initializer constant of the - // OpVariable, and this cannot be the result of a binary operation. - return false; - default: - return true; + // The instruction must not be an OpVariable, because (a) we cannot insert + // a binary operator before an OpVariable, but in any case (b) the + // constant we would be replacing is the initializer constant of the + // OpVariable, and this cannot be the result of a binary operation. + if (instruction->opcode() == SpvOpVariable) { + return false; } + + return true; } void TransformationReplaceBooleanConstantWithConstantBinary::Apply( @@ -281,11 +274,22 @@ TransformationReplaceBooleanConstantWithConstantBinary::ApplyWithResult( opt::Instruction* result = binary_instruction.get(); auto instruction_containing_constant_use = FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + auto instruction_before_which_to_insert = instruction_containing_constant_use; + + // If |instruction_before_which_to_insert| is an OpPhi instruction, + // then |binary_instruction| will be inserted into the parent block associated + // with the OpPhi variable operand. + if (instruction_containing_constant_use->opcode() == SpvOpPhi) { + instruction_before_which_to_insert = + ir_context->cfg() + ->block(instruction_containing_constant_use->GetSingleWordInOperand( + message_.id_use_descriptor().in_operand_index() + 1)) + ->terminator(); + } // We want to insert the new instruction before the instruction that contains // the use of the boolean, but we need to go backwards one more instruction if // the using instruction is preceded by a merge instruction. - auto instruction_before_which_to_insert = instruction_containing_constant_use; { opt::Instruction* previous_node = instruction_before_which_to_insert->PreviousNode(); @@ -294,6 +298,7 @@ TransformationReplaceBooleanConstantWithConstantBinary::ApplyWithResult( instruction_before_which_to_insert = previous_node; } } + instruction_before_which_to_insert->InsertBefore( std::move(binary_instruction)); instruction_containing_constant_use->SetInOperand( diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp index cebb6efec..76f083bea 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp @@ -49,6 +49,7 @@ bool TransformationReplaceLinearAlgebraInstruction::IsApplicable( instruction->opcode() != SpvOpMatrixTimesScalar && instruction->opcode() != SpvOpVectorTimesMatrix && instruction->opcode() != SpvOpMatrixTimesVector && + instruction->opcode() != SpvOpMatrixTimesMatrix && instruction->opcode() != SpvOpDot) { return false; } @@ -88,6 +89,9 @@ void TransformationReplaceLinearAlgebraInstruction::Apply( case SpvOpMatrixTimesVector: ReplaceOpMatrixTimesVector(ir_context, linear_algebra_instruction); break; + case SpvOpMatrixTimesMatrix: + ReplaceOpMatrixTimesMatrix(ir_context, linear_algebra_instruction); + break; case SpvOpDot: ReplaceOpDot(ir_context, linear_algebra_instruction); break; @@ -178,6 +182,37 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount( return 3 * matrix_column_count * matrix_row_count + 2 * matrix_column_count - matrix_row_count; } + case SpvOpMatrixTimesMatrix: { + // For each matrix 2 column, 1 OpCompositeExtract, 1 OpCompositeConstruct, + // |3 * matrix_1_row_count * matrix_1_column_count| OpCompositeExtract, + // |matrix_1_row_count * matrix_1_column_count| OpFMul, + // |matrix_1_row_count * (matrix_1_column_count - 1)| OpFAdd instructions + // will be inserted. + auto matrix_1_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + uint32_t matrix_1_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_1_instruction->type_id()) + ->AsMatrix() + ->element_count(); + uint32_t matrix_1_row_count = + ir_context->get_type_mgr() + ->GetType(matrix_1_instruction->type_id()) + ->AsMatrix() + ->element_type() + ->AsVector() + ->element_count(); + + auto matrix_2_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(1)); + uint32_t matrix_2_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_2_instruction->type_id()) + ->AsMatrix() + ->element_count(); + return matrix_2_column_count * + (2 + matrix_1_row_count * (5 * matrix_1_column_count - 1)); + } case SpvOpDot: // For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul // will be inserted. The first two OpFMul instructions will result the @@ -560,6 +595,151 @@ void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesVector( ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1)); } +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesMatrix( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets matrix 1 information. + auto matrix_1_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + uint32_t matrix_1_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_1_instruction->type_id()) + ->AsMatrix() + ->element_count(); + auto matrix_1_column_type = ir_context->get_type_mgr() + ->GetType(matrix_1_instruction->type_id()) + ->AsMatrix() + ->element_type(); + auto matrix_1_column_component_type = + matrix_1_column_type->AsVector()->element_type(); + uint32_t matrix_1_row_count = + matrix_1_column_type->AsVector()->element_count(); + + // Gets matrix 2 information. + auto matrix_2_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + uint32_t matrix_2_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_2_instruction->type_id()) + ->AsMatrix() + ->element_count(); + auto matrix_2_column_type = ir_context->get_type_mgr() + ->GetType(matrix_2_instruction->type_id()) + ->AsMatrix() + ->element_type(); + + uint32_t fresh_id_index = 0; + std::vector result_column_ids(matrix_2_column_count); + for (uint32_t i = 0; i < matrix_2_column_count; i++) { + // Extracts matrix 2 column. + uint32_t matrix_2_column_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_2_column_type), + matrix_2_column_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_2_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + std::vector column_component_ids(matrix_1_row_count); + for (uint32_t j = 0; j < matrix_1_row_count; j++) { + std::vector float_multiplication_ids(matrix_1_column_count); + for (uint32_t k = 0; k < matrix_1_column_count; k++) { + // Extracts matrix 1 column. + uint32_t matrix_1_column_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_1_column_type), + matrix_1_column_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_1_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {k}}}))); + + // Extracts matrix 1 column component. + uint32_t matrix_1_column_component_id = + message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_1_column_component_type), + matrix_1_column_component_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_1_column_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}}))); + + // Extracts matrix 2 column component. + uint32_t matrix_2_column_component_id = + message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_1_column_component_type), + matrix_2_column_component_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_2_column_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {k}}}))); + + // Multiplies corresponding matrix 1 and matrix 2 column components. + float_multiplication_ids[k] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFMul, + ir_context->get_type_mgr()->GetId(matrix_1_column_component_type), + float_multiplication_ids[k], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_1_column_component_id}}, + {SPV_OPERAND_TYPE_ID, {matrix_2_column_component_id}}}))); + } + + // Adds the multiplication results. + std::vector float_add_ids; + uint32_t float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFAdd, + ir_context->get_type_mgr()->GetId(matrix_1_column_component_type), + float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}}, + {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}}))); + for (uint32_t k = 2; k < float_multiplication_ids.size(); k++) { + float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFAdd, + ir_context->get_type_mgr()->GetId(matrix_1_column_component_type), + float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[k]}}, + {SPV_OPERAND_TYPE_ID, {float_add_ids[k - 2]}}}))); + } + + column_component_ids[j] = float_add_ids.back(); + } + + // Inserts the resulting matrix column. + opt::Instruction::OperandList in_operands; + for (auto& column_component_id : column_component_ids) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}}); + } + result_column_ids[i] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeConstruct, + ir_context->get_type_mgr()->GetId(matrix_1_column_type), + result_column_ids[i], opt::Instruction::OperandList(in_operands))); + } + + // The OpMatrixTimesMatrix instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {result_column_ids[1]}); + for (uint32_t i = 2; i < result_column_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {result_column_ids[i]}}); + } + + fuzzerutil::UpdateModuleIdBound( + ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1)); +} + void TransformationReplaceLinearAlgebraInstruction::ReplaceOpDot( opt::IRContext* ir_context, opt::Instruction* linear_algebra_instruction) const { diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h b/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h index 39dc58912..530c1f203 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h @@ -68,6 +68,10 @@ class TransformationReplaceLinearAlgebraInstruction : public Transformation { void ReplaceOpMatrixTimesVector(opt::IRContext* ir_context, opt::Instruction* instruction) const; + // Replaces an OpMatrixTimesMatrix instruction. + void ReplaceOpMatrixTimesMatrix(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + // Replaces an OpDot instruction. void ReplaceOpDot(opt::IRContext* ir_context, opt::Instruction* instruction) const; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp index 2ce7cfeeb..61c7f1011 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp @@ -76,7 +76,7 @@ bool TransformationReplaceParameterWithGlobal::IsApplicable( const auto* param_type = ir_context->get_type_mgr()->GetType(param_inst->type_id()); assert(param_type && "Parameter has invalid type"); - if (!CanReplaceFunctionParameterType(*param_type)) { + if (!IsParameterTypeSupported(*param_type)) { return false; } @@ -217,7 +217,7 @@ protobufs::Transformation TransformationReplaceParameterWithGlobal::ToMessage() return result; } -bool TransformationReplaceParameterWithGlobal::CanReplaceFunctionParameterType( +bool TransformationReplaceParameterWithGlobal::IsParameterTypeSupported( const opt::analysis::Type& type) { // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403): // Think about other type instructions we can add here. @@ -230,12 +230,11 @@ bool TransformationReplaceParameterWithGlobal::CanReplaceFunctionParameterType( case opt::analysis::Type::kVector: return true; case opt::analysis::Type::kStruct: - return std::all_of( - type.AsStruct()->element_types().begin(), - type.AsStruct()->element_types().end(), - [](const opt::analysis::Type* element_type) { - return CanReplaceFunctionParameterType(*element_type); - }); + return std::all_of(type.AsStruct()->element_types().begin(), + type.AsStruct()->element_types().end(), + [](const opt::analysis::Type* element_type) { + return IsParameterTypeSupported(*element_type); + }); default: return false; } diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.h b/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.h index cd379b0f6..49e7585ae 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.h @@ -55,7 +55,7 @@ class TransformationReplaceParameterWithGlobal : public Transformation { // Returns true if the type of the parameter is supported by this // transformation. - static bool CanReplaceFunctionParameterType(const opt::analysis::Type& type); + static bool IsParameterTypeSupported(const opt::analysis::Type& type); private: protobufs::TransformationReplaceParameterWithGlobal message_; diff --git a/3rdparty/spirv-tools/source/opt/debug_info_manager.cpp b/3rdparty/spirv-tools/source/opt/debug_info_manager.cpp index 55e3f4a5e..cc631fc10 100644 --- a/3rdparty/spirv-tools/source/opt/debug_info_manager.cpp +++ b/3rdparty/spirv-tools/source/opt/debug_info_manager.cpp @@ -375,20 +375,17 @@ bool DebugInfoManager::IsDebugDeclared(uint32_t variable_id) { } void DebugInfoManager::KillDebugDeclares(uint32_t variable_id) { - bool done = false; - while (!done) { - Instruction* kill_inst = nullptr; - auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id); - if (dbg_decl_itr != var_id_to_dbg_decl_.end()) { - for (auto dbg_decl : dbg_decl_itr->second) { - kill_inst = dbg_decl; - break; - } + auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id); + if (dbg_decl_itr != var_id_to_dbg_decl_.end()) { + // We intentionally copy the list of DebugDeclare instructions because + // context()->KillInst(dbg_decl) will update |var_id_to_dbg_decl_|. If we + // directly use |dbg_decl_itr->second|, it accesses a dangling pointer. + auto copy_dbg_decls = dbg_decl_itr->second; + + for (auto* dbg_decl : copy_dbg_decls) { + context()->KillInst(dbg_decl); } - if (kill_inst) - context()->KillInst(kill_inst); - else - done = true; + var_id_to_dbg_decl_.erase(dbg_decl_itr); } } diff --git a/3rdparty/spirv-tools/source/opt/dominator_analysis.cpp b/3rdparty/spirv-tools/source/opt/dominator_analysis.cpp index aef43e69f..b692d26a2 100644 --- a/3rdparty/spirv-tools/source/opt/dominator_analysis.cpp +++ b/3rdparty/spirv-tools/source/opt/dominator_analysis.cpp @@ -55,12 +55,25 @@ bool DominatorAnalysisBase::Dominates(Instruction* a, Instruction* b) const { return tree_.Dominates(bb_a, bb_b); } - Instruction* current_inst = a; - while ((current_inst = current_inst->NextNode())) { - if (current_inst == b) { + const Instruction* current = a; + const Instruction* other = b; + + if (tree_.IsPostDominator()) { + std::swap(current, other); + } + + // We handle OpLabel instructions explicitly since they are not stored in the + // instruction list. + if (current->opcode() == SpvOpLabel) { + return true; + } + + while ((current = current->NextNode())) { + if (current == other) { return true; } } + return false; } diff --git a/3rdparty/spirv-tools/source/opt/instrument_pass.cpp b/3rdparty/spirv-tools/source/opt/instrument_pass.cpp index 4210ad5d7..e6a55a8a4 100644 --- a/3rdparty/spirv-tools/source/opt/instrument_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/instrument_pass.cpp @@ -174,7 +174,9 @@ void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx, context()->GetBuiltinInputVarId(SpvBuiltInInstanceIndex), kInstVertOutInstanceIndex, base_offset_id, builder); } break; - case SpvExecutionModelGLCompute: { + case SpvExecutionModelGLCompute: + case SpvExecutionModelTaskNV: + case SpvExecutionModelMeshNV: { // Load and store GlobalInvocationId. uint32_t load_id = GenVarLoad( context()->GetBuiltinInputVarId(SpvBuiltInGlobalInvocationId), @@ -914,6 +916,7 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) { stage != SpvExecutionModelGLCompute && stage != SpvExecutionModelTessellationControl && stage != SpvExecutionModelTessellationEvaluation && + stage != SpvExecutionModelTaskNV && stage != SpvExecutionModelMeshNV && stage != SpvExecutionModelRayGenerationNV && stage != SpvExecutionModelIntersectionNV && stage != SpvExecutionModelAnyHitNV && diff --git a/3rdparty/spirv-tools/source/spirv_fuzzer_options.cpp b/3rdparty/spirv-tools/source/spirv_fuzzer_options.cpp index b407f148a..64fefbc28 100644 --- a/3rdparty/spirv-tools/source/spirv_fuzzer_options.cpp +++ b/3rdparty/spirv-tools/source/spirv_fuzzer_options.cpp @@ -22,6 +22,7 @@ const uint32_t kDefaultStepLimit = 250; spv_fuzzer_options_t::spv_fuzzer_options_t() : has_random_seed(false), random_seed(0), + replay_range(0), replay_validation_enabled(false), shrinker_step_limit(kDefaultStepLimit), fuzzer_pass_validation_enabled(false) {} @@ -45,6 +46,11 @@ SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed( options->random_seed = seed; } +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetReplayRange( + spv_fuzzer_options options, int32_t replay_range) { + options->replay_range = replay_range; +} + SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit( spv_fuzzer_options options, uint32_t shrinker_step_limit) { options->shrinker_step_limit = shrinker_step_limit; diff --git a/3rdparty/spirv-tools/source/spirv_fuzzer_options.h b/3rdparty/spirv-tools/source/spirv_fuzzer_options.h index 143f77f84..0db16a306 100644 --- a/3rdparty/spirv-tools/source/spirv_fuzzer_options.h +++ b/3rdparty/spirv-tools/source/spirv_fuzzer_options.h @@ -29,6 +29,9 @@ struct spv_fuzzer_options_t { bool has_random_seed; uint32_t random_seed; + // See spvFuzzerOptionsSetReplayRange. + int32_t replay_range; + // See spvFuzzerOptionsEnableReplayValidation. bool replay_validation_enabled; diff --git a/3rdparty/spirv-tools/source/val/basic_block.cpp b/3rdparty/spirv-tools/source/val/basic_block.cpp index a53103c8a..b2a8793d2 100644 --- a/3rdparty/spirv-tools/source/val/basic_block.cpp +++ b/3rdparty/spirv-tools/source/val/basic_block.cpp @@ -58,15 +58,9 @@ void BasicBlock::RegisterSuccessors( for (auto& block : next_blocks) { block->predecessors_.push_back(this); successors_.push_back(block); - if (block->reachable_ == false) block->set_reachable(reachable_); } } -void BasicBlock::RegisterBranchInstruction(SpvOp branch_instruction) { - if (branch_instruction == SpvOpUnreachable) reachable_ = false; - return; -} - bool BasicBlock::dominates(const BasicBlock& other) const { return (this == &other) || !(other.dom_end() == diff --git a/3rdparty/spirv-tools/source/val/basic_block.h b/3rdparty/spirv-tools/source/val/basic_block.h index 876105c27..5eea4f928 100644 --- a/3rdparty/spirv-tools/source/val/basic_block.h +++ b/3rdparty/spirv-tools/source/val/basic_block.h @@ -106,9 +106,6 @@ class BasicBlock { /// Returns the immedate post dominator of this basic block const BasicBlock* immediate_post_dominator() const; - /// Ends the block without a successor - void RegisterBranchInstruction(SpvOp branch_instruction); - /// Returns the label instruction for the block, or nullptr if not set. const Instruction* label() const { return label_; } diff --git a/3rdparty/spirv-tools/source/val/function.cpp b/3rdparty/spirv-tools/source/val/function.cpp index 028177002..249c8664f 100644 --- a/3rdparty/spirv-tools/source/val/function.cpp +++ b/3rdparty/spirv-tools/source/val/function.cpp @@ -130,7 +130,6 @@ spv_result_t Function::RegisterBlock(uint32_t block_id, bool is_definition) { undefined_blocks_.erase(block_id); current_block_ = &inserted_block->second; ordered_blocks_.push_back(current_block_); - if (IsFirstBlock(block_id)) current_block_->set_reachable(true); } else if (success) { // Block doesn't exsist but this is not a definition undefined_blocks_.insert(block_id); } @@ -138,8 +137,7 @@ spv_result_t Function::RegisterBlock(uint32_t block_id, bool is_definition) { return SPV_SUCCESS; } -void Function::RegisterBlockEnd(std::vector next_list, - SpvOp branch_instruction) { +void Function::RegisterBlockEnd(std::vector next_list) { assert( current_block_ && "RegisterBlockEnd can only be called when parsing a binary in a block"); @@ -174,7 +172,6 @@ void Function::RegisterBlockEnd(std::vector next_list, } } - current_block_->RegisterBranchInstruction(branch_instruction); current_block_->RegisterSuccessors(next_blocks); current_block_ = nullptr; return; diff --git a/3rdparty/spirv-tools/source/val/function.h b/3rdparty/spirv-tools/source/val/function.h index 0d6873d8d..400bb6348 100644 --- a/3rdparty/spirv-tools/source/val/function.h +++ b/3rdparty/spirv-tools/source/val/function.h @@ -97,9 +97,7 @@ class Function { /// Registers the end of the block /// /// @param[in] successors_list A list of ids to the block's successors - /// @param[in] branch_instruction the branch instruction that ended the block - void RegisterBlockEnd(std::vector successors_list, - SpvOp branch_instruction); + void RegisterBlockEnd(std::vector successors_list); /// Registers the end of the function. This is idempotent. void RegisterFunctionEnd(); diff --git a/3rdparty/spirv-tools/source/val/validate.cpp b/3rdparty/spirv-tools/source/val/validate.cpp index 168968da4..f964b9b16 100644 --- a/3rdparty/spirv-tools/source/val/validate.cpp +++ b/3rdparty/spirv-tools/source/val/validate.cpp @@ -368,6 +368,10 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( // Catch undefined forward references before performing further checks. if (auto error = ValidateForwardDecls(*vstate)) return error; + // Calculate reachability after all the blocks are parsed, but early that it + // can be relied on in subsequent pases. + ReachabilityPass(*vstate); + // ID usage needs be handled in its own iteration of the instructions, // between the two others. It depends on the first loop to have been // finished, so that all instructions have been registered. And the following diff --git a/3rdparty/spirv-tools/source/val/validate.h b/3rdparty/spirv-tools/source/val/validate.h index 31a775bb3..3fc183de8 100644 --- a/3rdparty/spirv-tools/source/val/validate.h +++ b/3rdparty/spirv-tools/source/val/validate.h @@ -197,6 +197,9 @@ spv_result_t FunctionPass(ValidationState_t& _, const Instruction* inst); /// Validates correctness of miscellaneous instructions. spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst); +/// Calculates the reachability of basic blocks. +void ReachabilityPass(ValidationState_t& _); + /// Validates execution limitations. /// /// Verifies execution models are allowed for all functionality they contain. diff --git a/3rdparty/spirv-tools/source/val/validate_cfg.cpp b/3rdparty/spirv-tools/source/val/validate_cfg.cpp index 1e33e5105..a2fe88279 100644 --- a/3rdparty/spirv-tools/source/val/validate_cfg.cpp +++ b/3rdparty/spirv-tools/source/val/validate_cfg.cpp @@ -1062,7 +1062,7 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) { uint32_t target = inst->GetOperandAs(0); CFG_ASSERT(FirstBlockAssert, target); - _.current_function().RegisterBlockEnd({target}, opcode); + _.current_function().RegisterBlockEnd({target}); } break; case SpvOpBranchConditional: { uint32_t tlabel = inst->GetOperandAs(1); @@ -1070,7 +1070,7 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) { CFG_ASSERT(FirstBlockAssert, tlabel); CFG_ASSERT(FirstBlockAssert, flabel); - _.current_function().RegisterBlockEnd({tlabel, flabel}, opcode); + _.current_function().RegisterBlockEnd({tlabel, flabel}); } break; case SpvOpSwitch: { @@ -1080,7 +1080,7 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) { CFG_ASSERT(FirstBlockAssert, target); cases.push_back(target); } - _.current_function().RegisterBlockEnd({cases}, opcode); + _.current_function().RegisterBlockEnd({cases}); } break; case SpvOpReturn: { const uint32_t return_type = _.current_function().GetResultTypeId(); @@ -1090,13 +1090,13 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) { return _.diag(SPV_ERROR_INVALID_CFG, inst) << "OpReturn can only be called from a function with void " << "return type."; - _.current_function().RegisterBlockEnd(std::vector(), opcode); + _.current_function().RegisterBlockEnd(std::vector()); break; } case SpvOpKill: case SpvOpReturnValue: case SpvOpUnreachable: - _.current_function().RegisterBlockEnd(std::vector(), opcode); + _.current_function().RegisterBlockEnd(std::vector()); if (opcode == SpvOpKill) { _.current_function().RegisterExecutionModelLimitation( SpvExecutionModelFragment, @@ -1109,6 +1109,27 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) { return SPV_SUCCESS; } +void ReachabilityPass(ValidationState_t& _) { + for (auto& f : _.functions()) { + std::vector stack; + auto entry = f.first_block(); + // Skip function declarations. + if (entry) stack.push_back(entry); + + while (!stack.empty()) { + auto block = stack.back(); + stack.pop_back(); + + if (block->reachable()) continue; + + block->set_reachable(true); + for (auto succ : *block->successors()) { + stack.push_back(succ); + } + } + } +} + spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) { switch (inst->opcode()) { case SpvOpPhi: diff --git a/3rdparty/spirv-tools/source/val/validate_interfaces.cpp b/3rdparty/spirv-tools/source/val/validate_interfaces.cpp index ed8cb9c72..833734f4d 100644 --- a/3rdparty/spirv-tools/source/val/validate_interfaces.cpp +++ b/3rdparty/spirv-tools/source/val/validate_interfaces.cpp @@ -208,8 +208,8 @@ uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) { // 4 * location + component. spv_result_t GetLocationsForVariable( ValidationState_t& _, const Instruction* entry_point, - const Instruction* variable, std::vector* locations, - std::vector* output_index1_locations) { + const Instruction* variable, std::unordered_set* locations, + std::unordered_set* output_index1_locations) { const bool is_fragment = entry_point->GetOperandAs(0) == SpvExecutionModelFragment; const bool is_output = @@ -356,17 +356,13 @@ spv_result_t GetLocationsForVariable( auto locs = locations; if (has_index && index == 1) locs = output_index1_locations; - if (end > locs->size()) { - locs->resize(end, false); - } for (uint32_t i = start; i < end; ++i) { - if (locs->at(i)) { + if (!locs->insert(i).second) { return _.diag(SPV_ERROR_INVALID_DATA, entry_point) << "Entry-point has conflicting " << storage_class << " location assignment at location " << i / 4 << ", component " << i % 4; } - (*locs)[i] = true; } } } else { @@ -425,17 +421,13 @@ spv_result_t GetLocationsForVariable( start += component; end = location * 4 + component + num_components; } - if (end > locations->size()) { - locations->resize(end, false); - } for (uint32_t l = start; l < end; ++l) { - if (locations->at(l)) { + if (!locations->insert(l).second) { return _.diag(SPV_ERROR_INVALID_DATA, entry_point) << "Entry-point has conflicting " << storage_class << " location assignment at location " << l / 4 << ", component " << l % 4; } - (*locations)[l] = true; } } } @@ -445,10 +437,10 @@ spv_result_t GetLocationsForVariable( spv_result_t ValidateLocations(ValidationState_t& _, const Instruction* entry_point) { - // Reserve space for 16 locations with 4 components each. - std::vector input_locations(16 * 4, false); - std::vector output_locations_index0(16 * 4, false); - std::vector output_locations_index1(16 * 4, false); + // Locations are stored as a combined location and component values. + std::unordered_set input_locations; + std::unordered_set output_locations_index0; + std::unordered_set output_locations_index1; for (uint32_t i = 3; i < entry_point->operands().size(); ++i) { auto interface_id = entry_point->GetOperandAs(i); auto interface_var = _.FindDef(interface_id); diff --git a/3rdparty/spirv-tools/source/val/validate_mode_setting.cpp b/3rdparty/spirv-tools/source/val/validate_mode_setting.cpp index e020f5ae6..a7f8d3395 100644 --- a/3rdparty/spirv-tools/source/val/validate_mode_setting.cpp +++ b/3rdparty/spirv-tools/source/val/validate_mode_setting.cpp @@ -501,10 +501,6 @@ spv_result_t ValidateMemoryModel(ValidationState_t& _, return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Addressing model must be Logical for WebGPU environment."; } - if (_.memory_model() != SpvMemoryModelVulkanKHR) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Memory model must be VulkanKHR for WebGPU environment."; - } } if (spvIsOpenCLEnv(_.context()->target_env)) {