From 70cbb974c45b2a4f8fd90b6e2b86eb55b51ea30a 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, 11 Oct 2019 20:38:46 -0700 Subject: [PATCH] Updated spirv-tools. --- 3rdparty/spirv-tools/BUILD.gn | 4 - 3rdparty/spirv-tools/CHANGES | 43 +- .../include/generated/build-version.inc | 2 +- .../spirv-tools/source/fuzz/CMakeLists.txt | 22 + .../source/fuzz/force_render_red.cpp | 8 +- 3rdparty/spirv-tools/source/fuzz/fuzzer.cpp | 31 + .../source/fuzz/fuzzer_context.cpp | 25 + .../spirv-tools/source/fuzz/fuzzer_context.h | 32 + .../spirv-tools/source/fuzz/fuzzer_pass.cpp | 99 ++ .../spirv-tools/source/fuzz/fuzzer_pass.h | 42 + ...er_pass_add_no_contraction_decorations.cpp | 61 + ...zzer_pass_add_no_contraction_decorations.h | 39 + .../fuzzer_pass_add_useful_constructs.cpp | 2 - .../fuzzer_pass_adjust_function_controls.cpp | 73 + .../fuzzer_pass_adjust_function_controls.h | 39 + .../fuzz/fuzzer_pass_adjust_loop_controls.cpp | 121 ++ .../fuzzer_pass_adjust_loop_controls.h} | 27 +- .../fuzzer_pass_adjust_selection_controls.cpp | 76 + .../fuzzer_pass_adjust_selection_controls.h | 39 + .../fuzz/fuzzer_pass_apply_id_synonyms.cpp | 23 +- .../fuzz/fuzzer_pass_construct_composites.cpp | 328 +++++ .../fuzz/fuzzer_pass_construct_composites.h | 79 ++ .../source/fuzz/fuzzer_pass_copy_objects.cpp | 131 +- .../fuzz/fuzzer_pass_obfuscate_constants.cpp | 25 +- .../spirv-tools/source/fuzz/fuzzer_util.cpp | 48 + .../spirv-tools/source/fuzz/fuzzer_util.h | 12 + .../source/fuzz/id_use_descriptor.cpp | 94 +- .../source/fuzz/id_use_descriptor.h | 25 +- .../source/fuzz/instruction_descriptor.cpp | 70 + .../source/fuzz/instruction_descriptor.h | 40 + .../source/fuzz/protobufs/spvtoolsfuzz.proto | 137 +- .../source/fuzz/transformation.cpp | 49 +- ...ormation_add_no_contraction_decoration.cpp | 110 ++ ...sformation_add_no_contraction_decoration.h | 58 + .../transformation_construct_composite.cpp | 310 ++++ .../fuzz/transformation_construct_composite.h | 88 ++ .../fuzz/transformation_copy_object.cpp | 50 +- .../source/fuzz/transformation_copy_object.h | 12 +- ..._boolean_constant_with_constant_binary.cpp | 4 +- ...ormation_replace_constant_with_uniform.cpp | 4 +- ...transformation_replace_id_with_synonym.cpp | 104 +- .../transformation_set_function_control.cpp | 100 ++ .../transformation_set_function_control.h | 58 + .../fuzz/transformation_set_loop_control.cpp | 216 +++ .../fuzz/transformation_set_loop_control.h | 79 ++ .../transformation_set_selection_control.cpp | 60 + .../transformation_set_selection_control.h | 54 + .../spirv-tools/source/opt/ir_context.cpp | 8 + .../spirv-tools/source/opt/type_manager.cpp | 30 + 3rdparty/spirv-tools/source/opt/types.cpp | 41 + 3rdparty/spirv-tools/source/opt/types.h | 33 + 3rdparty/spirv-tools/source/print.cpp | 2 +- .../spirv-tools/source/reduce/CMakeLists.txt | 4 - ..._conditional_branch_opportunity_finder.cpp | 2 +- ...nditional_branch_reduction_opportunity.cpp | 20 +- ...conditional_branch_reduction_opportunity.h | 4 +- .../spirv-tools/source/reduce/reducer.cpp | 244 ++-- 3rdparty/spirv-tools/source/reduce/reducer.h | 27 +- .../source/reduce/reduction_util.cpp | 17 + .../source/reduce/reduction_util.h | 5 + ...struction_reduction_opportunity_finder.cpp | 45 - ...instruction_reduction_opportunity_finder.h | 45 - ...recision_decoration_opportunity_finder.cpp | 49 - ...struction_reduction_opportunity_finder.cpp | 93 +- ...instruction_reduction_opportunity_finder.h | 4 +- ...oop_to_selection_reduction_opportunity.cpp | 17 - ..._loop_to_selection_reduction_opportunity.h | 5 - .../spirv-tools/source/val/validate_cfg.cpp | 68 +- .../spirv-tools/source/val/validate_misc.cpp | 3 + 3rdparty/spirv-tools/test/fuzz/CMakeLists.txt | 5 + .../test/fuzz/fuzzer_replayer_test.cpp | 20 +- .../test/fuzz/fuzzer_shrinker_test.cpp | 6 +- .../transformation_add_dead_continue_test.cpp | 46 + ...ion_add_no_contraction_decoration_test.cpp | 194 +++ ...ransformation_construct_composite_test.cpp | 1248 +++++++++++++++++ ...ean_constant_with_constant_binary_test.cpp | 23 +- ...ion_replace_constant_with_uniform_test.cpp | 61 +- ...formation_replace_id_with_synonym_test.cpp | 1166 ++++++++++++++- ...ansformation_set_function_control_test.cpp | 251 ++++ .../transformation_set_loop_control_test.cpp | 968 +++++++++++++ ...nsformation_set_selection_control_test.cpp | 219 +++ .../test/opt/if_conversion_test.cpp | 8 +- .../spirv-tools/test/opt/ir_context_test.cpp | 87 +- .../test/opt/type_manager_test.cpp | 7 +- .../spirv-tools/test/reduce/CMakeLists.txt | 2 - .../spirv-tools/test/reduce/reducer_test.cpp | 255 +++- .../reduce/remove_opname_instruction_test.cpp | 225 --- ...move_relaxed_precision_decoration_test.cpp | 177 --- .../remove_unreferenced_instruction_test.cpp | 359 ++++- .../spirv-tools/test/val/val_cfg_test.cpp | 352 ++++- .../spirv-tools/test/val/val_misc_test.cpp | 3 +- 91 files changed, 8337 insertions(+), 1264 deletions(-) create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp rename 3rdparty/spirv-tools/source/{reduce/remove_relaxed_precision_decoration_opportunity_finder.h => fuzz/fuzzer_pass_adjust_loop_controls.h} (50%) create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h create mode 100644 3rdparty/spirv-tools/source/fuzz/instruction_descriptor.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/instruction_descriptor.h create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.h create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.h create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_set_function_control.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_set_function_control.h create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.h create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_set_selection_control.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_set_selection_control.h delete mode 100644 3rdparty/spirv-tools/source/reduce/remove_opname_instruction_reduction_opportunity_finder.cpp delete mode 100644 3rdparty/spirv-tools/source/reduce/remove_opname_instruction_reduction_opportunity_finder.h delete mode 100644 3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp create mode 100644 3rdparty/spirv-tools/test/fuzz/transformation_add_no_contraction_decoration_test.cpp create mode 100644 3rdparty/spirv-tools/test/fuzz/transformation_construct_composite_test.cpp create mode 100644 3rdparty/spirv-tools/test/fuzz/transformation_set_function_control_test.cpp create mode 100644 3rdparty/spirv-tools/test/fuzz/transformation_set_loop_control_test.cpp create mode 100644 3rdparty/spirv-tools/test/fuzz/transformation_set_selection_control_test.cpp delete mode 100644 3rdparty/spirv-tools/test/reduce/remove_opname_instruction_test.cpp delete mode 100644 3rdparty/spirv-tools/test/reduce/remove_relaxed_precision_decoration_test.cpp diff --git a/3rdparty/spirv-tools/BUILD.gn b/3rdparty/spirv-tools/BUILD.gn index a8bd134e6..2418e0b47 100644 --- a/3rdparty/spirv-tools/BUILD.gn +++ b/3rdparty/spirv-tools/BUILD.gn @@ -734,10 +734,6 @@ static_library("spvtools_reduce") { "source/reduce/remove_function_reduction_opportunity_finder.h", "source/reduce/remove_instruction_reduction_opportunity.cpp", "source/reduce/remove_instruction_reduction_opportunity.h", - "source/reduce/remove_opname_instruction_reduction_opportunity_finder.cpp", - "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h", - "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp", - "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h", "source/reduce/remove_selection_reduction_opportunity.cpp", "source/reduce/remove_selection_reduction_opportunity.h", "source/reduce/remove_selection_reduction_opportunity_finder.cpp", diff --git a/3rdparty/spirv-tools/CHANGES b/3rdparty/spirv-tools/CHANGES index 10062f93b..d1934710a 100644 --- a/3rdparty/spirv-tools/CHANGES +++ b/3rdparty/spirv-tools/CHANGES @@ -1,7 +1,11 @@ Revision history for SPIRV-Tools -v2019.5-dev 2019-08-08 +v2019.5-dev 2019-10-09 - General: + - Export SPIRV-Tools targets on installation + - SPIRV-Tools support for SPIR-V 1.5 (#2865) + - Add WebGPU SPIR-V Assembler in JavaScript. (#2876) + - Add Bazel build configuration. (#2891) - Optimizer - Add descriptor array scalar replacement (#2742) - Add pass to wrap OpKill in a function call (#2790) @@ -9,17 +13,44 @@ v2019.5-dev 2019-08-08 - Add pass to replace AMD shader ballot extension (#2811) - Add pass to make Float32 operation relax precision (#2808) - Add pass to make relax precision operation Float16 (#2808) + - Add pass to replace uses of 3 AMD extensions (#2814) + - Fold Min, Max, and Clamp instructions. (#2836) + - Better handling of OpKill in continues (#2842,#2922,#2933) + - Enable OpTypeCooperativeMatrix specialization (#2927) Fixes: - Instrument: Fix version 2 output record write for tess eval shaders. (#2782) - Instrument: Add support for Buffer Device Address extension (#2792) - Fix check for changed binary in API call. (#2798) + - Instrument: Fix version 2 output record write for tess eval shaders. (#2782) + - Instrument: Add support for Buffer Device Address extension (#2792) + - Fix check for changed binary in API call. (#2798) + - For WebGPU<->Vulkan optimization, set correct execution environment (#2834) + - Handle OpConstantNull in copy-prop-arrays. (#2870) + - Use OpReturn* in wrap-opkill (#2886) - Validator + - Add generic builtin validation of target (#2843) + - Extra resource interface validation (#2864) + - Adding valilidation checks for OpEntryPoint duplicate names and execution mode (#2862) + - Relaxed bitcast with pointers (#2878) + - Validate physical storage buffer restrictions (#2930) + - Add SPV_KHR_shader_clock validation (#2879) Fixes: - Fix validation of constant matrices (#2794) - Update "remquor" validation + - Fix validation of constant matrices (#2794) + - Update "remquor" validation + - Only allow previously declared forward refs in structs (#2920) - Reduce - Remove relaxed precision decorations (#2797) + - Reduce/fuzz: improve command line args (#2932) + - Improve remove unref instr pass (#2945) Fixes: + - Fuzz + - Fix add-dead-break and add-dead-continue passes to respect dominance (#2838) + - Add fuzzer pass to copy objects (#2853) + - Add fuzzer pass to replace ids with synonyms (#2857) + - Allow validation during spirv-fuzz replay (#2873) + - Employ the "swarm testing" idea in spirv-fuzz (#2890) + - reduce/fuzz: improve command line args (#2932) + - option to convert shader into a form that renders red (#2934) + - Add fuzzer pass to change selection controls (#2944) + - add transformation and pass to construct composites (#2941) + v2019.4 2019-08-08 - General: diff --git a/3rdparty/spirv-tools/include/generated/build-version.inc b/3rdparty/spirv-tools/include/generated/build-version.inc index 115b30e9d..7a6751c1f 100644 --- a/3rdparty/spirv-tools/include/generated/build-version.inc +++ b/3rdparty/spirv-tools/include/generated/build-version.inc @@ -1 +1 @@ -"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-102-gc18c9ff6" +"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-120-g2276e597" diff --git a/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt b/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt index b21d210d0..11c9219e1 100644 --- a/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt @@ -37,14 +37,20 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass.h fuzzer_pass_add_dead_breaks.h fuzzer_pass_add_dead_continues.h + fuzzer_pass_add_no_contraction_decorations.h fuzzer_pass_add_useful_constructs.h + fuzzer_pass_adjust_function_controls.h + fuzzer_pass_adjust_loop_controls.h + fuzzer_pass_adjust_selection_controls.h fuzzer_pass_apply_id_synonyms.h + fuzzer_pass_construct_composites.h fuzzer_pass_copy_objects.h fuzzer_pass_obfuscate_constants.h fuzzer_pass_permute_blocks.h fuzzer_pass_split_blocks.h fuzzer_util.h id_use_descriptor.h + instruction_descriptor.h protobufs/spirvfuzz_protobufs.h pseudo_random_generator.h random_generator.h @@ -55,15 +61,20 @@ if(SPIRV_BUILD_FUZZER) transformation_add_constant_scalar.h transformation_add_dead_break.h transformation_add_dead_continue.h + transformation_add_no_contraction_decoration.h transformation_add_type_boolean.h transformation_add_type_float.h transformation_add_type_int.h transformation_add_type_pointer.h + transformation_construct_composite.h transformation_copy_object.h transformation_move_block_down.h transformation_replace_boolean_constant_with_constant_binary.h transformation_replace_constant_with_uniform.h transformation_replace_id_with_synonym.h + transformation_set_function_control.h + transformation_set_loop_control.h + transformation_set_selection_control.h transformation_split_block.h uniform_buffer_element_descriptor.h ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h @@ -76,14 +87,20 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass.cpp fuzzer_pass_add_dead_breaks.cpp fuzzer_pass_add_dead_continues.cpp + fuzzer_pass_add_no_contraction_decorations.cpp fuzzer_pass_add_useful_constructs.cpp + fuzzer_pass_adjust_function_controls.cpp + fuzzer_pass_adjust_loop_controls.cpp + fuzzer_pass_adjust_selection_controls.cpp fuzzer_pass_apply_id_synonyms.cpp + fuzzer_pass_construct_composites.cpp fuzzer_pass_copy_objects.cpp fuzzer_pass_obfuscate_constants.cpp fuzzer_pass_permute_blocks.cpp fuzzer_pass_split_blocks.cpp fuzzer_util.cpp id_use_descriptor.cpp + instruction_descriptor.cpp pseudo_random_generator.cpp random_generator.cpp replayer.cpp @@ -93,15 +110,20 @@ if(SPIRV_BUILD_FUZZER) transformation_add_constant_scalar.cpp transformation_add_dead_break.cpp transformation_add_dead_continue.cpp + transformation_add_no_contraction_decoration.cpp transformation_add_type_boolean.cpp transformation_add_type_float.cpp transformation_add_type_int.cpp transformation_add_type_pointer.cpp + transformation_construct_composite.cpp transformation_copy_object.cpp transformation_move_block_down.cpp transformation_replace_boolean_constant_with_constant_binary.cpp transformation_replace_constant_with_uniform.cpp transformation_replace_id_with_synonym.cpp + transformation_set_function_control.cpp + transformation_set_loop_control.cpp + transformation_set_selection_control.cpp transformation_split_block.cpp uniform_buffer_element_descriptor.cpp ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc diff --git a/3rdparty/spirv-tools/source/fuzz/force_render_red.cpp b/3rdparty/spirv-tools/source/fuzz/force_render_red.cpp index 3bc117249..46e23e80e 100644 --- a/3rdparty/spirv-tools/source/fuzz/force_render_red.cpp +++ b/3rdparty/spirv-tools/source/fuzz/force_render_red.cpp @@ -15,6 +15,7 @@ #include "source/fuzz/force_render_red.h" #include "source/fuzz/fact_manager.h" +#include "source/fuzz/instruction_descriptor.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation_replace_constant_with_uniform.h" #include "source/fuzz/uniform_buffer_element_descriptor.h" @@ -147,9 +148,10 @@ MakeConstantUniformReplacement(opt::IRContext* ir_context, uint32_t greater_than_instruction, uint32_t in_operand_index) { return MakeUnique( - transformation::MakeIdUseDescriptor(constant_id, SpvOpFOrdGreaterThan, - in_operand_index, - greater_than_instruction, 0), + MakeIdUseDescriptor(constant_id, + MakeInstructionDescriptor(greater_than_instruction, + SpvOpFOrdGreaterThan, 0), + in_operand_index), fact_manager.GetUniformDescriptorsForConstant(ir_context, constant_id)[0], ir_context->TakeNextId(), ir_context->TakeNextId()); } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp index 8e36a323a..d07c17345 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp @@ -22,8 +22,13 @@ #include "source/fuzz/fuzzer_context.h" #include "source/fuzz/fuzzer_pass_add_dead_breaks.h" #include "source/fuzz/fuzzer_pass_add_dead_continues.h" +#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h" #include "source/fuzz/fuzzer_pass_add_useful_constructs.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_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_obfuscate_constants.h" #include "source/fuzz/fuzzer_pass_permute_blocks.h" @@ -138,6 +143,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( MaybeAddPass(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); + MaybeAddPass(&passes, ir_context.get(), + &fact_manager, &fuzzer_context, + transformation_sequence_out); MaybeAddPass(&passes, ir_context.get(), &fact_manager, &fuzzer_context, transformation_sequence_out); @@ -162,6 +170,29 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( passes[fuzzer_context.RandomIndex(passes)]->Apply(); } + // Now apply some passes that it does not make sense to apply repeatedly, + // as they do not unlock other passes. + std::vector> final_passes; + MaybeAddPass(&passes, ir_context.get(), + &fact_manager, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass(&passes, ir_context.get(), + &fact_manager, &fuzzer_context, + transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &fact_manager, &fuzzer_context, + transformation_sequence_out); + for (auto& pass : final_passes) { + pass->Apply(); + } + + if (fuzzer_context.ChooseEven()) { + FuzzerPassAddNoContractionDecorations(ir_context.get(), &fact_manager, + &fuzzer_context, + transformation_sequence_out) + .Apply(); + } + // Encode the module as a binary. ir_context->module()->ToBinary(binary_out, false); diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp index 69c6e5688..68903d003 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp @@ -25,12 +25,25 @@ namespace { const std::pair kChanceOfAddingDeadBreak = {5, 80}; const std::pair kChanceOfAddingDeadContinue = {5, 80}; +const std::pair kChanceOfAddingNoContractionDecoration = { + 5, 70}; +const std::pair kChanceOfAdjustingFunctionControl = {20, + 70}; +const std::pair kChanceOfAdjustingLoopControl = {20, 90}; +const std::pair kChanceOfAdjustingSelectionControl = {20, + 90}; const std::pair kChanceOfCopyingObject = {20, 50}; +const std::pair kChanceOfConstructingComposite = {20, 50}; const std::pair kChanceOfMovingBlockDown = {20, 50}; const std::pair kChanceOfObfuscatingConstant = {10, 90}; const std::pair kChanceOfReplacingIdWithSynonym = {10, 90}; const std::pair kChanceOfSplittingBlock = {40, 95}; +// Default limits for various quantities that are chosen during fuzzing. +// Keep them in alphabetical order. +const uint32_t kDefaultMaxLoopControlPartialCount = 100; +const uint32_t kDefaultMaxLoopControlPeelCount = 100; + // Default functions for controlling how deep to go during recursive // generation/transformation. Keep them in alphabetical order. @@ -53,6 +66,16 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak); chance_of_adding_dead_continue_ = ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue); + chance_of_adding_no_contraction_decoration_ = + ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration); + chance_of_adjusting_function_control_ = + ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl); + chance_of_adjusting_loop_control_ = + ChooseBetweenMinAndMax(kChanceOfAdjustingLoopControl); + chance_of_adjusting_selection_control_ = + ChooseBetweenMinAndMax(kChanceOfAdjustingSelectionControl); + chance_of_constructing_composite_ = + ChooseBetweenMinAndMax(kChanceOfConstructingComposite); chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject); chance_of_moving_block_down_ = ChooseBetweenMinAndMax(kChanceOfMovingBlockDown); @@ -61,6 +84,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, chance_of_replacing_id_with_synonym_ = ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym); chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock); + max_loop_control_partial_count_ = kDefaultMaxLoopControlPartialCount; + max_loop_control_peel_count_ = kDefaultMaxLoopControlPeelCount; } FuzzerContext::~FuzzerContext() = default; diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h index a1ad66f8d..fcf2c9fe5 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h @@ -62,6 +62,21 @@ class FuzzerContext { uint32_t GetChanceOfAddingDeadContinue() { return chance_of_adding_dead_continue_; } + uint32_t GetChanceOfAddingNoContractionDecoration() { + return chance_of_adding_no_contraction_decoration_; + } + uint32_t GetChanceOfAdjustingFunctionControl() { + return chance_of_adjusting_function_control_; + } + uint32_t GetChanceOfAdjustingLoopControl() { + return chance_of_adjusting_loop_control_; + } + uint32_t GetChanceOfAdjustingSelectionControl() { + return chance_of_adjusting_selection_control_; + } + uint32_t GetChanceOfConstructingComposite() { + return chance_of_constructing_composite_; + } uint32_t GetChanceOfCopyingObject() { return chance_of_copying_object_; } uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; } uint32_t GetChanceOfObfuscatingConstant() { @@ -71,6 +86,12 @@ class FuzzerContext { return chance_of_replacing_id_with_synonym_; } uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; } + uint32_t GetRandomLoopControlPeelCount() { + return random_generator_->RandomUint32(max_loop_control_peel_count_); + } + uint32_t GetRandomLoopControlPartialCount() { + return random_generator_->RandomUint32(max_loop_control_partial_count_); + } // Functions to control how deeply to recurse. // Keep them in alphabetical order. @@ -88,12 +109,23 @@ class FuzzerContext { // Keep them in alphabetical order. uint32_t chance_of_adding_dead_break_; uint32_t chance_of_adding_dead_continue_; + uint32_t chance_of_adding_no_contraction_decoration_; + uint32_t chance_of_adjusting_function_control_; + uint32_t chance_of_adjusting_loop_control_; + uint32_t chance_of_adjusting_selection_control_; + uint32_t chance_of_constructing_composite_; uint32_t chance_of_copying_object_; uint32_t chance_of_moving_block_down_; uint32_t chance_of_obfuscating_constant_; uint32_t chance_of_replacing_id_with_synonym_; uint32_t chance_of_splitting_block_; + // Limits associated with various quantities for which random values are + // chosen during fuzzing. + // Keep them in alphabetical order. + uint32_t max_loop_control_partial_count_; + uint32_t max_loop_control_peel_count_; + // Functions to determine with what probability to go deeper when generating // or mutating constructs recursively. const std::function& diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp index 823f2e09a..cb26d79ae 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp @@ -27,5 +27,104 @@ FuzzerPass::FuzzerPass(opt::IRContext* ir_context, FactManager* fact_manager, FuzzerPass::~FuzzerPass() = default; +std::vector FuzzerPass::FindAvailableInstructions( + const opt::Function& function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + std::function + instruction_is_relevant) { + // TODO(afd) The following is (relatively) simple, but may end up being + // prohibitively inefficient, as it walks the whole dominator tree for + // every instruction that is considered. + + std::vector result; + // Consider all global declarations + for (auto& global : GetIRContext()->module()->types_values()) { + if (instruction_is_relevant(GetIRContext(), &global)) { + result.push_back(&global); + } + } + + // Consider all previous instructions in this block + for (auto prev_inst_it = block->begin(); prev_inst_it != inst_it; + ++prev_inst_it) { + if (instruction_is_relevant(GetIRContext(), &*prev_inst_it)) { + result.push_back(&*prev_inst_it); + } + } + + // Walk the dominator tree to consider all instructions from dominating + // blocks + auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(&function); + for (auto next_dominator = dominator_analysis->ImmediateDominator(block); + next_dominator != nullptr; + next_dominator = + dominator_analysis->ImmediateDominator(next_dominator)) { + for (auto& dominating_inst : *next_dominator) { + if (instruction_is_relevant(GetIRContext(), &dominating_inst)) { + result.push_back(&dominating_inst); + } + } + } + return result; +} + +void FuzzerPass::MaybeAddTransformationBeforeEachInstruction( + std::function + maybe_apply_transformation) { + // Consider every block in every function. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // We now consider every instruction in the block, randomly deciding + // whether to apply a transformation before it. + + // In order for transformations to insert new instructions, they need to + // be able to identify the instruction to insert before. We enable this + // by tracking a base instruction, which must generate a result id, and + // an offset (to allow us to identify instructions that do not generate + // result ids). + + // The initial base instruction is the block label. + uint32_t base = block.id(); + uint32_t offset = 0; + // Consider every instruction in the block. + for (auto inst_it = block.begin(); inst_it != block.end(); ++inst_it) { + if (inst_it->HasResultId()) { + // In the case that the instruction has a result id, we use the + // instruction as its own base, with zero offset. + base = inst_it->result_id(); + offset = 0; + } else { + // The instruction does not have a result id, so we need to identify + // it via the latest instruction that did have a result id (base), and + // an incremented offset. + offset++; + } + + // Invoke the provided function, which might apply a transformation. + // Its return value informs us of how many instructions it inserted. + // (This will be 0 if no transformation was applied.) + uint32_t num_instructions_inserted = + maybe_apply_transformation(function, &block, inst_it, base, offset); + + if (!inst_it->HasResultId()) { + // We are tracking the current id-less instruction via an offset, + // |offset|, from a previous instruction, |base|, that has an id. We + // increment |offset| to reflect any newly-inserted instructions. + // + // An alternative would be to reset |base| to be an id generated by + // a newly-inserted instruction, but that would be more complex, and + // sticking to a |base| that already existed before this + // transformation was applied makes the applicability of future + // transformations less tightly coupled with the presence of the just- + // applied transformation. + offset += num_instructions_inserted; + } + } + } + } +} + } // namespace fuzz } // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h index cd1b19443..d56cc534e 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h @@ -15,9 +15,13 @@ #ifndef SOURCE_FUZZ_FUZZER_PASS_H_ #define SOURCE_FUZZ_FUZZER_PASS_H_ +#include +#include + #include "source/fuzz/fact_manager.h" #include "source/fuzz/fuzzer_context.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/ir_context.h" namespace spvtools { namespace fuzz { @@ -48,6 +52,44 @@ class FuzzerPass { return transformations_; } + // Returns all instructions that are *available* at |inst_it|, which is + // required to be inside block |block| of function |function| - that is, all + // instructions at global scope and all instructions that strictly dominate + // |inst_it|. + // + // Filters said instructions to return only those that satisfy the + // |instruction_is_relevant| predicate. This, for instance, could ignore all + // instructions that have a particular decoration. + std::vector FindAvailableInstructions( + const opt::Function& function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + std::function + instruction_is_relevant); + + // A helper method that iterates through each instruction in each block, at + // all times tracking a base instruction and offset that allows that latest + // instruction to be located even if it has no result id. + // + // The code to manipulate the base and offset is a bit fiddly, and the point + // of this method is to avoiding having to duplicate it in multiple + // transformation passes. + // + // The function |maybe_apply_transformation| is invoked for each instruction + // |inst_it| in block |block| of function |function| that is encountered. The + // |base| and |offset| parameters to the function object allow |inst_it| to be + // identified. + // + // The job of |maybe_apply_transformation| is to randomly decide whether to + // try to apply some transformation, and then - if selected - to attempt to + // apply it. The function returns the number of instructions that were + // inserted before |inst_it|, so that |offset| can be updated. + // + void MaybeAddTransformationBeforeEachInstruction( + std::function + maybe_apply_transformation); + private: opt::IRContext* ir_context_; FactManager* fact_manager_; diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp new file mode 100644 index 000000000..2ab779c1b --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp @@ -0,0 +1,61 @@ +// 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/fuzzer_pass_add_no_contraction_decorations.h" + +#include "source/fuzz/transformation_add_no_contraction_decoration.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddNoContractionDecorations::FuzzerPassAddNoContractionDecorations( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations){}; + +FuzzerPassAddNoContractionDecorations:: + ~FuzzerPassAddNoContractionDecorations() = default; + +void FuzzerPassAddNoContractionDecorations::Apply() { + // Consider every instruction in every block in every function. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& inst : block) { + // Restrict attention to arithmetic instructions (as defined in the + // SPIR-V specification). + if (TransformationAddNoContractionDecoration::IsArithmetic( + inst.opcode())) { + // Randomly choose whether to apply the NoContraction decoration to + // this arithmetic instruction. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfAddingNoContractionDecoration())) { + TransformationAddNoContractionDecoration transformation( + inst.result_id()); + assert(transformation.IsApplicable(GetIRContext(), + *GetFactManager()) && + "Transformation should be applicable by construction."); + transformation.Apply(GetIRContext(), GetFactManager()); + *GetTransformations()->add_transformation() = + transformation.ToMessage(); + } + } + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h new file mode 100644 index 000000000..abe5bd7ad --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h @@ -0,0 +1,39 @@ +// 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_FUZZER_PASS_ADD_NO_CONTRACTION_DECORATIONS_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_NO_CONTRACTION_DECORATIONS_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that applies the NoContraction decoration to arithmetic instructions. +class FuzzerPassAddNoContractionDecorations : public FuzzerPass { + public: + FuzzerPassAddNoContractionDecorations( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddNoContractionDecorations() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_NO_CONTRACTION_DECORATIONS_ diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_useful_constructs.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_useful_constructs.cpp index 6ac4ae971..489a08443 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_useful_constructs.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_useful_constructs.cpp @@ -24,8 +24,6 @@ namespace spvtools { namespace fuzz { -using opt::IRContext; - FuzzerPassAddUsefulConstructs::FuzzerPassAddUsefulConstructs( opt::IRContext* ir_context, FactManager* fact_manager, FuzzerContext* fuzzer_context, diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp new file mode 100644 index 000000000..ab5ecf623 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp @@ -0,0 +1,73 @@ +// 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/fuzzer_pass_adjust_function_controls.h" + +#include "source/fuzz/transformation_set_function_control.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAdjustFunctionControls::FuzzerPassAdjustFunctionControls( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations){}; + +FuzzerPassAdjustFunctionControls::~FuzzerPassAdjustFunctionControls() = default; + +void FuzzerPassAdjustFunctionControls::Apply() { + // Consider every function in the module. + for (auto& function : *GetIRContext()->module()) { + // Randomly decide whether to adjust this function's controls. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAdjustingFunctionControl())) { + // Grab the function control mask for the function in its present form. + uint32_t existing_function_control_mask = + function.DefInst().GetSingleWordInOperand(0); + + // For the new mask, we first randomly select one of three basic masks: + // None, Inline or DontInline. These are always valid (and are mutually + // exclusive). + std::vector basic_function_control_masks = { + SpvFunctionControlMaskNone, SpvFunctionControlInlineMask, + SpvFunctionControlDontInlineMask}; + uint32_t new_function_control_mask = + basic_function_control_masks[GetFuzzerContext()->RandomIndex( + basic_function_control_masks)]; + + // We now consider the Pure and Const mask bits. If these are already + // set on the function then it's OK to keep them, but also interesting + // to consider dropping them, so we decide randomly in each case. + for (auto mask_bit : + {SpvFunctionControlPureMask, SpvFunctionControlConstMask}) { + if ((existing_function_control_mask & mask_bit) && + GetFuzzerContext()->ChooseEven()) { + new_function_control_mask |= mask_bit; + } + } + + // Create and add a transformation. + TransformationSetFunctionControl transformation( + function.DefInst().result_id(), new_function_control_mask); + assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) && + "Transformation should be applicable by construction."); + transformation.Apply(GetIRContext(), GetFactManager()); + *GetTransformations()->add_transformation() = transformation.ToMessage(); + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h new file mode 100644 index 000000000..02d36004e --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h @@ -0,0 +1,39 @@ +// 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_FUZZER_PASS_ADJUST_FUNCTION_CONTROLS_ +#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_FUNCTION_CONTROLS_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that adjusts the function controls on OpFunction instructions. +class FuzzerPassAdjustFunctionControls : public FuzzerPass { + public: + FuzzerPassAdjustFunctionControls( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAdjustFunctionControls() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_FUNCTION_CONTROLS_ diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp new file mode 100644 index 000000000..65b8d9501 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp @@ -0,0 +1,121 @@ +// 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/fuzzer_pass_adjust_loop_controls.h" + +#include "source/fuzz/transformation_set_loop_control.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAdjustLoopControls::FuzzerPassAdjustLoopControls( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations){}; + +FuzzerPassAdjustLoopControls::~FuzzerPassAdjustLoopControls() = default; + +void FuzzerPassAdjustLoopControls::Apply() { + // Consider every merge instruction in the module (via looking through all + // functions and blocks). + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + if (auto merge_inst = block.GetMergeInst()) { + // Ignore the instruction if it is not a loop merge. + if (merge_inst->opcode() != SpvOpLoopMerge) { + continue; + } + + // Decide randomly whether to adjust this loop merge. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAdjustingLoopControl())) { + continue; + } + + uint32_t existing_mask = merge_inst->GetSingleWordOperand( + TransformationSetLoopControl::kLoopControlMaskInOperandIndex); + + // First, set the new mask to one of None, Unroll or DontUnroll. + std::vector basic_masks = {SpvLoopControlMaskNone, + SpvLoopControlUnrollMask, + SpvLoopControlDontUnrollMask}; + uint32_t new_mask = + basic_masks[GetFuzzerContext()->RandomIndex(basic_masks)]; + + // For the loop controls that depend on guarantees about what the loop + // does, check which of these were present in the existing mask and + // randomly decide whether to keep them. They are just hints, so + // removing them should not change the semantics of the module. + for (auto mask_bit : + {SpvLoopControlDependencyInfiniteMask, + SpvLoopControlDependencyLengthMask, + SpvLoopControlMinIterationsMask, SpvLoopControlMaxIterationsMask, + SpvLoopControlIterationMultipleMask}) { + if ((existing_mask & mask_bit) && GetFuzzerContext()->ChooseEven()) { + // The mask bits we are considering are not available in all SPIR-V + // versions. However, we only include a mask bit if it was present + // in the original loop control mask, and we work under the + // assumption that we are transforming a valid module, thus we don't + // need to actually check whether the SPIR-V version being used + // supports these loop control mask bits. + new_mask |= mask_bit; + } + } + + // We use 0 for peel count and partial count in the case that we choose + // not to set these controls. + uint32_t peel_count = 0; + uint32_t partial_count = 0; + + // PeelCount and PartialCount are not compatible with DontUnroll, so + // we check whether DontUnroll is set. + if (!(new_mask & SpvLoopControlDontUnrollMask)) { + // If PeelCount is supported by this SPIR-V version, randomly choose + // whether to set it. If it was set in the original mask and is not + // selected for setting here, that amounts to dropping it. + if (TransformationSetLoopControl::PeelCountIsSupported( + GetIRContext()) && + GetFuzzerContext()->ChooseEven()) { + new_mask |= SpvLoopControlPeelCountMask; + // The peel count is chosen randomly - if PeelCount was already set + // this will overwrite whatever peel count was previously used. + peel_count = GetFuzzerContext()->GetRandomLoopControlPeelCount(); + } + // Similar, but for PartialCount. + if (TransformationSetLoopControl::PartialCountIsSupported( + GetIRContext()) && + GetFuzzerContext()->ChooseEven()) { + new_mask |= SpvLoopControlPartialCountMask; + partial_count = + GetFuzzerContext()->GetRandomLoopControlPartialCount(); + } + } + + // Apply the transformation and add it to the output transformation + // sequence. + TransformationSetLoopControl transformation(block.id(), new_mask, + peel_count, partial_count); + assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) && + "Transformation should be applicable by construction."); + transformation.Apply(GetIRContext(), GetFactManager()); + *GetTransformations()->add_transformation() = + transformation.ToMessage(); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h similarity index 50% rename from 3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h rename to 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h index 673049cc8..e94560673 100644 --- a/3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h @@ -12,25 +12,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_ -#define SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_ +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_ +#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_ -#include "source/reduce/reduction_opportunity_finder.h" +#include "source/fuzz/fuzzer_pass.h" namespace spvtools { -namespace reduce { +namespace fuzz { -// A finder for opportunities to remove relaxed precision decorations. -class RemoveRelaxedPrecisionDecorationOpportunityFinder - : public ReductionOpportunityFinder { +// A pass that adjusts the loop controls on OpLoopMerge instructions. +class FuzzerPassAdjustLoopControls : public FuzzerPass { public: - std::vector> GetAvailableOpportunities( - opt::IRContext* context) const override; + FuzzerPassAdjustLoopControls( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); - std::string GetName() const override; + ~FuzzerPassAdjustLoopControls() override; + + void Apply() override; }; -} // namespace reduce +} // namespace fuzz } // namespace spvtools -#endif // SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_ +#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_ diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp new file mode 100644 index 000000000..442fc4962 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp @@ -0,0 +1,76 @@ +// 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/fuzzer_pass_adjust_selection_controls.h" + +#include "source/fuzz/transformation_set_selection_control.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAdjustSelectionControls::FuzzerPassAdjustSelectionControls( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations){}; + +FuzzerPassAdjustSelectionControls::~FuzzerPassAdjustSelectionControls() = + default; + +void FuzzerPassAdjustSelectionControls::Apply() { + // Consider every merge instruction in the module (via looking through all + // functions and blocks). + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + if (auto merge_inst = block.GetMergeInst()) { + // Ignore the instruction if it is not a selection merge. + if (merge_inst->opcode() != SpvOpSelectionMerge) { + continue; + } + + // Choose randomly whether to change the selection control for this + // instruction. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAdjustingSelectionControl())) { + continue; + } + + // The choices to change the selection control to are the set of valid + // controls, minus the current control. + std::vector choices; + for (auto control : + {SpvSelectionControlMaskNone, SpvSelectionControlFlattenMask, + SpvSelectionControlDontFlattenMask}) { + if (control == merge_inst->GetSingleWordOperand(1)) { + continue; + } + choices.push_back(control); + } + + // Apply the transformation and add it to the output transformation + // sequence. + TransformationSetSelectionControl transformation( + block.id(), choices[GetFuzzerContext()->RandomIndex(choices)]); + assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) && + "Transformation should be applicable by construction."); + transformation.Apply(GetIRContext(), GetFactManager()); + *GetTransformations()->add_transformation() = + transformation.ToMessage(); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h new file mode 100644 index 000000000..b5b255ce3 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h @@ -0,0 +1,39 @@ +// 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_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_ +#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that adjusts the selection controls on OpSelectionMerge instructions. +class FuzzerPassAdjustSelectionControls : public FuzzerPass { + public: + FuzzerPassAdjustSelectionControls( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAdjustSelectionControls() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_ 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 64131f9e5..ce138379a 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 @@ -63,9 +63,6 @@ void FuzzerPassApplyIdSynonyms::Apply() { GetFuzzerContext()->RandomIndex(synonyms_to_try); auto synonym_to_try = synonyms_to_try[synonym_index]; synonyms_to_try.erase(synonyms_to_try.begin() + synonym_index); - assert(synonym_to_try->index().empty() && - "Right now we only support id == id synonyms; supporting " - "e.g. id == index-into-vector will come later"); if (!TransformationReplaceIdWithSynonym:: ReplacingUseWithSynonymIsOk(GetIRContext(), use_inst, @@ -74,10 +71,24 @@ void FuzzerPassApplyIdSynonyms::Apply() { continue; } + // At present, we generate direct id synonyms (through + // OpCopyObject), which require no indices, and id synonyms that + // require a single index (through OpCompositeConstruct). + assert(synonym_to_try->index_size() <= 1); + + // If an index is required, then we need to extract an element + // from a composite (e.g. through OpCompositeExtract), and this + // requires a fresh result id. + auto fresh_id_for_temporary = + synonym_to_try->index().empty() + ? 0 + : GetFuzzerContext()->GetFreshId(); + TransformationReplaceIdWithSynonym replace_id_transformation( - transformation::MakeIdUseDescriptorFromUse( - GetIRContext(), use_inst, use_in_operand_index), - *synonym_to_try, 0); + MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, + use_in_operand_index), + *synonym_to_try, fresh_id_for_temporary); + // The transformation should be applicable by construction. assert(replace_id_transformation.IsApplicable(GetIRContext(), *GetFactManager())); diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp new file mode 100644 index 000000000..e0d21840b --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp @@ -0,0 +1,328 @@ +// 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/fuzzer_pass_construct_composites.h" + +#include +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_construct_composite.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassConstructComposites::FuzzerPassConstructComposites( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} + +FuzzerPassConstructComposites::~FuzzerPassConstructComposites() = default; + +void FuzzerPassConstructComposites::Apply() { + // Gather up the ids of all composite types. + std::vector composite_type_ids; + for (auto& inst : GetIRContext()->types_values()) { + if (fuzzerutil::IsCompositeType( + GetIRContext()->get_type_mgr()->GetType(inst.result_id()))) { + composite_type_ids.push_back(inst.result_id()); + } + } + + MaybeAddTransformationBeforeEachInstruction( + [this, &composite_type_ids](const opt::Function& function, + opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + uint32_t base, uint32_t offset) -> uint32_t { + // Check whether it is legitimate to insert a composite construction + // before the instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpCompositeConstruct, inst_it)) { + return 0; + } + + // Randomly decide whether to try inserting an object copy here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfConstructingComposite())) { + return 0; + } + + // For each instruction that is available at this program point (i.e. an + // instruction that is global or whose definition strictly dominates the + // program point) and suitable for making a synonym of, associate it + // with the id of its result type. + TypeIdToInstructions type_id_to_available_instructions; + for (auto instruction : FindAvailableInstructions( + function, block, inst_it, fuzzerutil::CanMakeSynonymOf)) { + RecordAvailableInstruction(instruction, + &type_id_to_available_instructions); + } + + // At this point, |composite_type_ids| captures all the composite types + // we could try to create, while |type_id_to_available_instructions| + // captures all the available result ids we might use, organized by + // type. + + // Now we try to find a composite that we can construct. We might not + // manage, if there is a paucity of available ingredients in the module + // (e.g. if our only available composite was a boolean vector and we had + // no instructions generating boolean result types available). + // + // If we succeed, |chosen_composite_type| will end up being non-zero, + // and |constructor_arguments| will end up giving us result ids suitable + // for constructing a composite of that type. Otherwise these variables + // will remain 0 and null respectively. + uint32_t chosen_composite_type = 0; + std::unique_ptr> constructor_arguments = nullptr; + + // Initially, all composite type ids are available for us to try. Keep + // trying until we run out of options. + auto composites_to_try_constructing = composite_type_ids; + while (!composites_to_try_constructing.empty()) { + // Remove a composite type from the composite types left for us to + // try. + auto index = + GetFuzzerContext()->RandomIndex(composites_to_try_constructing); + auto next_composite_to_try_constructing = + composites_to_try_constructing[index]; + composites_to_try_constructing.erase( + composites_to_try_constructing.begin() + index); + + // Now try to construct a composite of this type, using an appropriate + // helper method depending on the kind of composite type. + auto composite_type = GetIRContext()->get_type_mgr()->GetType( + next_composite_to_try_constructing); + if (auto array_type = composite_type->AsArray()) { + constructor_arguments = TryConstructingArrayComposite( + *array_type, type_id_to_available_instructions); + } else if (auto matrix_type = composite_type->AsMatrix()) { + constructor_arguments = TryConstructingMatrixComposite( + *matrix_type, type_id_to_available_instructions); + } else if (auto struct_type = composite_type->AsStruct()) { + constructor_arguments = TryConstructingStructComposite( + *struct_type, type_id_to_available_instructions); + } else { + auto vector_type = composite_type->AsVector(); + assert(vector_type && + "The space of possible composite types should be covered by " + "the above cases."); + constructor_arguments = TryConstructingVectorComposite( + *vector_type, type_id_to_available_instructions); + } + if (constructor_arguments != nullptr) { + // We succeeded! Note the composite type we finally settled on, and + // exit from the loop. + chosen_composite_type = next_composite_to_try_constructing; + break; + } + } + + if (!chosen_composite_type) { + // We did not manage to make a composite; return 0 to indicate that no + // instructions were added. + assert(constructor_arguments == nullptr); + return 0; + } + assert(constructor_arguments != nullptr); + + // Make and apply a transformation. + TransformationConstructComposite transformation( + chosen_composite_type, *constructor_arguments, base, offset, + GetFuzzerContext()->GetFreshId()); + assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) && + "This transformation should be applicable by construction."); + transformation.Apply(GetIRContext(), GetFactManager()); + *GetTransformations()->add_transformation() = + transformation.ToMessage(); + // Indicate that one instruction was added. + return 1; + }); +} + +void FuzzerPassConstructComposites::RecordAvailableInstruction( + opt::Instruction* inst, + TypeIdToInstructions* type_id_to_available_instructions) { + if (type_id_to_available_instructions->count(inst->type_id()) == 0) { + (*type_id_to_available_instructions)[inst->type_id()] = {}; + } + type_id_to_available_instructions->at(inst->type_id()).push_back(inst); +} + +std::unique_ptr> +FuzzerPassConstructComposites::TryConstructingArrayComposite( + const opt::analysis::Array& array_type, + const TypeIdToInstructions& type_id_to_available_instructions) { + // TODO make these be true by construction + assert(array_type.length_info().words.size() == 2); + assert(array_type.length_info().words[0] == + opt::analysis::Array::LengthInfo::kConstant); + + auto result = MakeUnique>(); + auto element_type_id = + GetIRContext()->get_type_mgr()->GetId(array_type.element_type()); + auto available_instructions = + type_id_to_available_instructions.find(element_type_id); + if (available_instructions == type_id_to_available_instructions.cend()) { + // TODO comment infeasible + return nullptr; + } + for (uint32_t index = 0; index < array_type.length_info().words[1]; index++) { + result->push_back(available_instructions + ->second[GetFuzzerContext()->RandomIndex( + available_instructions->second)] + ->result_id()); + } + return result; +} + +std::unique_ptr> +FuzzerPassConstructComposites::TryConstructingMatrixComposite( + const opt::analysis::Matrix& matrix_type, + const TypeIdToInstructions& type_id_to_available_instructions) { + (void)(matrix_type); + (void)(type_id_to_available_instructions); + assert(false); + return nullptr; +} + +std::unique_ptr> +FuzzerPassConstructComposites::TryConstructingStructComposite( + const opt::analysis::Struct& struct_type, + const TypeIdToInstructions& type_id_to_available_instructions) { + auto result = MakeUnique>(); + for (auto element_type : struct_type.element_types()) { + auto element_type_id = GetIRContext()->get_type_mgr()->GetId(element_type); + auto available_instructions = + type_id_to_available_instructions.find(element_type_id); + if (available_instructions == type_id_to_available_instructions.cend()) { + // TODO comment infeasible + return nullptr; + } + result->push_back(available_instructions + ->second[GetFuzzerContext()->RandomIndex( + available_instructions->second)] + ->result_id()); + } + return result; +} + +std::unique_ptr> +FuzzerPassConstructComposites::TryConstructingVectorComposite( + const opt::analysis::Vector& vector_type, + const TypeIdToInstructions& type_id_to_available_instructions) { + // Get details of the type underlying the vector, and the width of the vector, + // for convenience. + auto element_type = vector_type.element_type(); + auto element_count = vector_type.element_count(); + + // Collect a mapping, from type id to width, for scalar/vector types that are + // smaller in width than |vector_type|, but that have the same underlying + // type. For example, if |vector_type| is vec4, the mapping will be { float + // -> 1, vec2 -> 2, vec3 -> 3 }. The mapping will have missing entries if + // some of these types do not exist. + + // TODO comment why we have the list as well. + std::vector smaller_vector_type_ids; + std::map smaller_vector_type_id_to_width; + // Add the underlying type. This id must exist, in order for |vector_type| to + // exist. + auto scalar_type_id = GetIRContext()->get_type_mgr()->GetId(element_type); + smaller_vector_type_ids.push_back(scalar_type_id); + smaller_vector_type_id_to_width[scalar_type_id] = 1; + + // Now add every vector type with width at least 2, and less than the width of + // |vector_type|. + for (uint32_t width = 2; width < element_count; width++) { + opt::analysis::Vector smaller_vector_type(vector_type.element_type(), + width); + auto smaller_vector_type_id = + GetIRContext()->get_type_mgr()->GetId(&smaller_vector_type); + // TODO recap why it might be 0 + if (smaller_vector_type_id) { + smaller_vector_type_ids.push_back(smaller_vector_type_id); + smaller_vector_type_id_to_width[smaller_vector_type_id] = width; + } + } + + // Now we know the types that are available to us, we set about populating a + // vector of the right length. We do this by deciding, with no order in mind, + // which instructions we will use to populate the vector, and subsequently + // randomly choosing an order. This is to avoid biasing construction of + // vectors with smaller vectors to the left and scalars to the right. That is + // a concern because, e.g. in the case of populating a vec4, if we populate + // the constructor instructions left-to-right, we can always choose a vec3 to + // construct the first three elements, but can only choose a vec3 to construct + // the last three elements if we chose a float to construct the first element + // (otherwise there will not be space left for a vec3). + + uint32_t vector_slots_used = 0; + // The instructions we will use to construct the vector, in no particular + // order at this stage. + std::vector instructions_to_use; + + while (vector_slots_used < vector_type.element_count()) { + std::vector instructions_to_choose_from; + for (auto& entry : smaller_vector_type_id_to_width) { + if (entry.second > + std::min(vector_type.element_count() - 1, + vector_type.element_count() - vector_slots_used)) { + continue; + } + auto available_instructions = + type_id_to_available_instructions.find(entry.first); + if (available_instructions == type_id_to_available_instructions.cend()) { + continue; + } + instructions_to_choose_from.insert(instructions_to_choose_from.end(), + available_instructions->second.begin(), + available_instructions->second.end()); + } + if (instructions_to_choose_from.empty()) { + // TODO comment - like fuzzed into a corner + return nullptr; + } + auto instruction_to_use = + instructions_to_choose_from[GetFuzzerContext()->RandomIndex( + instructions_to_choose_from)]; + instructions_to_use.push_back(instruction_to_use); + auto chosen_type = + GetIRContext()->get_type_mgr()->GetType(instruction_to_use->type_id()); + if (chosen_type->AsVector()) { + assert(chosen_type->AsVector()->element_type() == element_type); + assert(chosen_type->AsVector()->element_count() < element_count); + assert(chosen_type->AsVector()->element_count() <= + element_count - vector_slots_used); + vector_slots_used += chosen_type->AsVector()->element_count(); + } else { + assert(chosen_type == element_type); + vector_slots_used += 1; + } + } + assert(vector_slots_used == vector_type.element_count()); + + auto result = MakeUnique>(); + std::vector operands; + while (!instructions_to_use.empty()) { + auto index = GetFuzzerContext()->RandomIndex(instructions_to_use); + result->push_back(instructions_to_use[index]->result_id()); + instructions_to_use.erase(instructions_to_use.begin() + index); + } + assert(result->size() > 1); + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h new file mode 100644 index 000000000..99ef31ff9 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h @@ -0,0 +1,79 @@ +// 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_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +#include +#include + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for constructing composite objects from smaller objects. +class FuzzerPassConstructComposites : public FuzzerPass { + public: + FuzzerPassConstructComposites( + opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassConstructComposites(); + + void Apply() override; + + private: + // Used to map a type id to relevant instructions whose result type matches + // the type id. + typedef std::map> + TypeIdToInstructions; + + // Considers all instructions that are available at |inst| - instructions + // whose results could be packed into a composite - and updates + // |type_id_to_available_instructions| so that each such instruction is + // associated with its the id of its result type. + void RecordAvailableInstruction( + opt::Instruction* inst, + TypeIdToInstructions* type_id_to_available_instructions); + + // Attempts to find suitable instruction result ids from the values of + // |type_id_to_available_instructions| that would allow a composite of type + // |array_type| to be constructed. Returns said ids if they can be found. + // Returns |nullptr| otherwise. + std::unique_ptr> TryConstructingArrayComposite( + const opt::analysis::Array& array_type, + const TypeIdToInstructions& type_id_to_available_instructions); + + // Similar to TryConstructingArrayComposite, but for matrices. + std::unique_ptr> TryConstructingMatrixComposite( + const opt::analysis::Matrix& matrix_type, + const TypeIdToInstructions& type_id_to_available_instructions); + + // Similar to TryConstructingArrayComposite, but for structs. + std::unique_ptr> TryConstructingStructComposite( + const opt::analysis::Struct& struct_type, + const TypeIdToInstructions& type_id_to_available_instructions); + + // Similar to TryConstructingArrayComposite, but for vectors. + std::unique_ptr> TryConstructingVectorComposite( + const opt::analysis::Vector& vector_type, + const TypeIdToInstructions& type_id_to_available_instructions); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_ 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 a1b118a88..8c85a3bad 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp @@ -14,6 +14,7 @@ #include "source/fuzz/fuzzer_pass_copy_objects.h" +#include "source/fuzz/fuzzer_util.h" #include "source/fuzz/transformation_copy_object.h" namespace spvtools { @@ -28,120 +29,46 @@ FuzzerPassCopyObjects::FuzzerPassCopyObjects( FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default; void FuzzerPassCopyObjects::Apply() { - // Consider every block in every function. - for (auto& function : *GetIRContext()->module()) { - for (auto& block : function) { - // We now consider every instruction in the block, randomly deciding - // whether to add an object copy before the instruction. - - // In order to insert an object copy instruction, we need to be able to - // identify the instruction a copy should be inserted before. We do this - // by tracking a base instruction, which must generate a result id, and an - // offset (to allow us to identify instructions that do not generate - // result ids). - - // The initial base instruction is the block label. - uint32_t base = block.id(); - uint32_t offset = 0; - // Consider every instruction in the block. - for (auto inst_it = block.begin(); inst_it != block.end(); ++inst_it) { - if (inst_it->HasResultId()) { - // In the case that the instruction has a result id, we use the - // instruction as its own base, with zero offset. - base = inst_it->result_id(); - offset = 0; - } else { - // The instruction does not have a result id, so we need to identify - // it via the latest instruction that did have a result id (base), and - // an incremented offset. - offset++; - } - + MaybeAddTransformationBeforeEachInstruction( + [this](const opt::Function& function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, uint32_t base, + uint32_t offset) -> uint32_t { // Check whether it is legitimate to insert a copy before this // instruction. - if (!TransformationCopyObject::CanInsertCopyBefore(inst_it)) { - continue; + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject, + inst_it)) { + return 0; } // Randomly decide whether to try inserting an object copy here. if (!GetFuzzerContext()->ChoosePercentage( GetFuzzerContext()->GetChanceOfCopyingObject())) { - continue; + return 0; } - // Populate list of potential instructions that can be copied. - // TODO(afd) The following is (relatively) simple, but may end up being - // prohibitively inefficient, as it walks the whole dominator tree for - // every copy that is added. - std::vector copyable_instructions; + std::vector relevant_instructions = + FindAvailableInstructions(function, block, inst_it, + fuzzerutil::CanMakeSynonymOf); - // Consider all global declarations - for (auto& global : GetIRContext()->module()->types_values()) { - if (TransformationCopyObject::IsCopyable(GetIRContext(), &global)) { - copyable_instructions.push_back(&global); - } - } - - // Consider all previous instructions in this block - for (auto prev_inst_it = block.begin(); prev_inst_it != inst_it; - ++prev_inst_it) { - if (TransformationCopyObject::IsCopyable(GetIRContext(), - &*prev_inst_it)) { - copyable_instructions.push_back(&*prev_inst_it); - } - } - - // Walk the dominator tree to consider all instructions from dominating - // blocks - auto dominator_analysis = - GetIRContext()->GetDominatorAnalysis(&function); - for (auto next_dominator = - dominator_analysis->ImmediateDominator(&block); - next_dominator != nullptr; - next_dominator = - dominator_analysis->ImmediateDominator(next_dominator)) { - for (auto& dominating_inst : *next_dominator) { - if (TransformationCopyObject::IsCopyable(GetIRContext(), - &dominating_inst)) { - copyable_instructions.push_back(&dominating_inst); - } - } - } - - // At this point, |copyable_instructions| contains all the instructions + // At this point, |relevant_instructions| contains all the instructions // we might think of copying. - - if (!copyable_instructions.empty()) { - // Choose a copyable instruction at random, and create and apply an - // object copying transformation based on it. - uint32_t index = - GetFuzzerContext()->RandomIndex(copyable_instructions); - TransformationCopyObject transformation( - copyable_instructions[index]->result_id(), base, offset, - GetFuzzerContext()->GetFreshId()); - assert( - transformation.IsApplicable(GetIRContext(), *GetFactManager()) && - "This transformation should be applicable by construction."); - transformation.Apply(GetIRContext(), GetFactManager()); - *GetTransformations()->add_transformation() = - transformation.ToMessage(); - - if (!inst_it->HasResultId()) { - // We have inserted a new instruction before the current - // instruction, and we are tracking the current id-less instruction - // via an offset (offset) from a previous instruction (base) that - // has an id. We increment |offset| to reflect the newly-inserted - // instruction. - // - // This is slightly preferable to the alternative of setting |base| - // to be the result id of the new instruction, since on replay we - // might end up eliminating this copy but keeping a subsequent copy. - offset++; - } + if (relevant_instructions.empty()) { + return 0; } - } - } - } + + // Choose a copyable instruction at random, and create and apply an + // object copying transformation based on it. + uint32_t index = GetFuzzerContext()->RandomIndex(relevant_instructions); + TransformationCopyObject transformation( + relevant_instructions[index]->result_id(), base, offset, + GetFuzzerContext()->GetFreshId()); + assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) && + "This transformation should be applicable by construction."); + transformation.Apply(GetIRContext(), GetFactManager()); + *GetTransformations()->add_transformation() = + transformation.ToMessage(); + return 1; + }); } } // namespace fuzz 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 484c9b8c2..3df11aeec 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp @@ -16,6 +16,7 @@ #include +#include "source/fuzz/instruction_descriptor.h" #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h" #include "source/fuzz/transformation_replace_constant_with_uniform.h" #include "source/opt/ir_context.h" @@ -102,10 +103,11 @@ void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair( // We randomly decide, based on the current depth of obfuscation, whether // to further obfuscate this operand. if (GetFuzzerContext()->GoDeeperInConstantObfuscation(depth)) { - auto in_operand_use = transformation::MakeIdUseDescriptor( + auto in_operand_use = MakeIdUseDescriptor( binary_operator_instruction->GetSingleWordInOperand(index), - binary_operator_instruction->opcode(), index, - binary_operator_instruction->result_id(), 0); + MakeInstructionDescriptor(binary_operator_instruction->result_id(), + binary_operator_instruction->opcode(), 0), + index); ObfuscateConstant(depth + 1, in_operand_use); } } @@ -366,14 +368,17 @@ void FuzzerPassObfuscateConstants::MaybeAddConstantIdUse( // it. protobufs::IdUseDescriptor id_use_descriptor; id_use_descriptor.set_id_of_interest(operand_id); - id_use_descriptor.set_target_instruction_opcode(inst.opcode()); + id_use_descriptor.mutable_enclosing_instruction() + ->set_target_instruction_opcode(inst.opcode()); + id_use_descriptor.mutable_enclosing_instruction() + ->set_base_instruction_result_id(base_instruction_result_id); + id_use_descriptor.mutable_enclosing_instruction() + ->set_num_opcodes_to_ignore( + skipped_opcode_count.find(inst.opcode()) == + skipped_opcode_count.end() + ? 0 + : skipped_opcode_count.at(inst.opcode())); id_use_descriptor.set_in_operand_index(in_operand_index); - id_use_descriptor.set_base_instruction_result_id( - base_instruction_result_id); - id_use_descriptor.set_num_opcodes_to_ignore( - skipped_opcode_count.find(inst.opcode()) == skipped_opcode_count.end() - ? 0 - : skipped_opcode_count.at(inst.opcode())); constant_uses->push_back(id_use_descriptor); } break; default: diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp index 2ffe8154e..e61ccaeb5 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp @@ -216,6 +216,13 @@ bool NewEdgeRespectsUseDefDominance(opt::IRContext* context, return true; } + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2919): the + // solution below to determining whether a new edge respects dominance + // rules is incomplete. Test + // TransformationAddDeadContinueTest::DISABLED_Miscellaneous6 exposes the + // problem. In practice, this limitation does not bite too often, and the + // worst it does is leads to SPIR-V that spirv-val rejects. + // Let us assume that the module being manipulated is valid according to the // rules of the SPIR-V language. // @@ -307,6 +314,47 @@ bool BlockIsReachableInItsFunction(opt::IRContext* context, ->Dominates(enclosing_function->entry().get(), bb); } +bool CanInsertOpcodeBeforeInstruction( + SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block) { + if (instruction_in_block->PreviousNode() && + (instruction_in_block->PreviousNode()->opcode() == SpvOpLoopMerge || + instruction_in_block->PreviousNode()->opcode() == SpvOpSelectionMerge)) { + // We cannot insert directly after a merge instruction. + return false; + } + if (opcode != SpvOpVariable && + instruction_in_block->opcode() == SpvOpVariable) { + // We cannot insert a non-OpVariable instruction directly before a + // variable; variables in a function must be contiguous in the entry block. + return false; + } + // We cannot insert a non-OpPhi instruction directly before an OpPhi, because + // OpPhi instructions need to be contiguous at the start of a block. + return opcode == SpvOpPhi || instruction_in_block->opcode() != SpvOpPhi; +} + +bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) { + if (!inst->HasResultId()) { + // We can only make a synonym of an instruction that generates an id. + return false; + } + if (!inst->type_id()) { + // We can only make a synonym of an instruction that has a type. + return false; + } + // We do not make synonyms of objects that have decorations: if the synonym is + // not decorated analogously, using the original object vs. its synonymous + // form may not be equivalent. + return ir_context->get_decoration_mgr() + ->GetDecorationsFor(inst->result_id(), true) + .empty(); +} + +bool IsCompositeType(const opt::analysis::Type* type) { + return type && (type->AsArray() || type->AsMatrix() || type->AsStruct() || + type->AsVector()); +} + } // 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 7aebc2858..890ceac4d 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h @@ -87,6 +87,18 @@ bool NewEdgeRespectsUseDefDominance(opt::IRContext* context, bool BlockIsReachableInItsFunction(opt::IRContext* context, opt::BasicBlock* bb); +// Determines whether it is OK to insert an instruction with opcode |opcode| +// before |instruction_in_block|. +bool CanInsertOpcodeBeforeInstruction( + SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block); + +// Determines whether it is OK to make a synonym of |inst|. +bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst); + +// Determines whether the given type is a composite; that is: an array, matrix, +// struct or vector. +bool IsCompositeType(const opt::analysis::Type* type); + } // namespace fuzzerutil } // namespace fuzz diff --git a/3rdparty/spirv-tools/source/fuzz/id_use_descriptor.cpp b/3rdparty/spirv-tools/source/fuzz/id_use_descriptor.cpp index 8693a7d1b..105c31035 100644 --- a/3rdparty/spirv-tools/source/fuzz/id_use_descriptor.cpp +++ b/3rdparty/spirv-tools/source/fuzz/id_use_descriptor.cpp @@ -14,71 +14,41 @@ #include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/instruction_descriptor.h" + namespace spvtools { namespace fuzz { -opt::Instruction* transformation::FindInstruction( - const protobufs::IdUseDescriptor& descriptor, - spvtools::opt::IRContext* context) { - for (auto& function : *context->module()) { - for (auto& block : function) { - bool found_base = block.id() == descriptor.base_instruction_result_id(); - uint32_t num_ignored = 0; - for (auto& instruction : block) { - if (instruction.HasResultId() && - instruction.result_id() == - descriptor.base_instruction_result_id()) { - assert(!found_base && - "It should not be possible to find the base instruction " - "multiple times."); - found_base = true; - assert(num_ignored == 0 && - "The skipped instruction count should only be incremented " - "after the instruction base has been found."); - } - if (found_base && - instruction.opcode() == descriptor.target_instruction_opcode()) { - if (num_ignored == descriptor.num_opcodes_to_ignore()) { - if (descriptor.in_operand_index() >= instruction.NumInOperands()) { - return nullptr; - } - auto in_operand = - instruction.GetInOperand(descriptor.in_operand_index()); - if (in_operand.type != SPV_OPERAND_TYPE_ID) { - return nullptr; - } - if (in_operand.words[0] != descriptor.id_of_interest()) { - return nullptr; - } - return &instruction; - } - num_ignored++; - } - } - if (found_base) { - // We found the base instruction, but did not find the target - // instruction in the same block. - return nullptr; - } - } +opt::Instruction* FindInstructionContainingUse( + const protobufs::IdUseDescriptor& id_use_descriptor, + opt::IRContext* context) { + auto result = + FindInstruction(id_use_descriptor.enclosing_instruction(), context); + if (!result) { + return nullptr; + } + if (id_use_descriptor.in_operand_index() >= result->NumInOperands()) { + return nullptr; + } + if (result->GetSingleWordInOperand(id_use_descriptor.in_operand_index()) != + id_use_descriptor.id_of_interest()) { + return nullptr; } - return nullptr; -} - -protobufs::IdUseDescriptor transformation::MakeIdUseDescriptor( - uint32_t id_of_interest, SpvOp target_instruction_opcode, - uint32_t in_operand_index, uint32_t base_instruction_result_id, - uint32_t num_opcodes_to_ignore) { - protobufs::IdUseDescriptor result; - result.set_id_of_interest(id_of_interest); - result.set_target_instruction_opcode(target_instruction_opcode); - result.set_in_operand_index(in_operand_index); - result.set_base_instruction_result_id(base_instruction_result_id); - result.set_num_opcodes_to_ignore(num_opcodes_to_ignore); return result; } -protobufs::IdUseDescriptor transformation::MakeIdUseDescriptorFromUse( +protobufs::IdUseDescriptor MakeIdUseDescriptor( + uint32_t id_of_interest, + const protobufs::InstructionDescriptor& enclosing_instruction, + uint32_t in_operand_index) { + protobufs::IdUseDescriptor result; + result.set_id_of_interest(id_of_interest); + *result.mutable_enclosing_instruction() = enclosing_instruction; + result.set_in_operand_index(in_operand_index); + return result; +} + +protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse( opt::IRContext* context, opt::Instruction* inst, uint32_t in_operand_index) { auto in_operand = inst->GetInOperand(in_operand_index); @@ -94,9 +64,11 @@ protobufs::IdUseDescriptor transformation::MakeIdUseDescriptorFromUse( num_opcodes_to_ignore = 0; } if (&inst_in_block == inst) { - return MakeIdUseDescriptor(id_of_interest, inst->opcode(), - in_operand_index, base_instruction_result_id, - num_opcodes_to_ignore); + return MakeIdUseDescriptor( + id_of_interest, + MakeInstructionDescriptor(base_instruction_result_id, inst->opcode(), + num_opcodes_to_ignore), + in_operand_index); } if (inst_in_block.opcode() == inst->opcode()) { num_opcodes_to_ignore++; diff --git a/3rdparty/spirv-tools/source/fuzz/id_use_descriptor.h b/3rdparty/spirv-tools/source/fuzz/id_use_descriptor.h index 1657cb923..d18bb668d 100644 --- a/3rdparty/spirv-tools/source/fuzz/id_use_descriptor.h +++ b/3rdparty/spirv-tools/source/fuzz/id_use_descriptor.h @@ -12,28 +12,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef SOURCE_FUZZ_ID_USE_LOCATOR_H_ -#define SOURCE_FUZZ_ID_USE_LOCATOR_H_ +#ifndef SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_ +#define SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_ #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/opt/ir_context.h" namespace spvtools { namespace fuzz { -namespace transformation { -// Looks for an instruction in |context| such that the id use represented by -// |descriptor| is one of the operands to said instruction. Returns |nullptr| -// if no such instruction can be found. -opt::Instruction* FindInstruction(const protobufs::IdUseDescriptor& descriptor, - opt::IRContext* context); +// Looks for an instruction in |context| that contains a use +// identified by |id_use_descriptor|. +// Returns |nullptr| if no such instruction can be found. +opt::Instruction* FindInstructionContainingUse( + const protobufs::IdUseDescriptor& id_use_descriptor, + opt::IRContext* context); // Creates an IdUseDescriptor protobuf message from the given components. // See the protobuf definition for details of what these components mean. protobufs::IdUseDescriptor MakeIdUseDescriptor( - uint32_t id_of_interest, SpvOp target_instruction_opcode, - uint32_t in_operand_index, uint32_t base_instruction_result_id, - uint32_t num_opcodes_to_ignore); + uint32_t id_of_interest, + const protobufs::InstructionDescriptor& enclosing_instruction, + uint32_t in_operand_index); // Given an id use, represented by the instruction |inst| that uses the id, and // the input operand index |in_operand_index| associated with the usage, returns @@ -41,8 +41,7 @@ protobufs::IdUseDescriptor MakeIdUseDescriptor( protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse( opt::IRContext* context, opt::Instruction* inst, uint32_t in_operand_index); -} // namespace transformation } // namespace fuzz } // namespace spvtools -#endif // SOURCE_FUZZ_ID_USE_LOCATOR_H_ +#endif // SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/instruction_descriptor.cpp b/3rdparty/spirv-tools/source/fuzz/instruction_descriptor.cpp new file mode 100644 index 000000000..bd48d4896 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/instruction_descriptor.cpp @@ -0,0 +1,70 @@ +// 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/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +opt::Instruction* FindInstruction( + const protobufs::InstructionDescriptor& instruction_descriptor, + spvtools::opt::IRContext* context) { + for (auto& function : *context->module()) { + for (auto& block : function) { + bool found_base = + block.id() == instruction_descriptor.base_instruction_result_id(); + uint32_t num_ignored = 0; + for (auto& instruction : block) { + if (instruction.HasResultId() && + instruction.result_id() == + instruction_descriptor.base_instruction_result_id()) { + assert(!found_base && + "It should not be possible to find the base instruction " + "multiple times."); + found_base = true; + assert(num_ignored == 0 && + "The skipped instruction count should only be incremented " + "after the instruction base has been found."); + } + if (found_base && + instruction.opcode() == + instruction_descriptor.target_instruction_opcode()) { + if (num_ignored == instruction_descriptor.num_opcodes_to_ignore()) { + return &instruction; + } + num_ignored++; + } + } + if (found_base) { + // We found the base instruction, but did not find the target + // instruction in the same block. + return nullptr; + } + } + } + return nullptr; +} + +protobufs::InstructionDescriptor MakeInstructionDescriptor( + uint32_t base_instruction_result_id, SpvOp target_instruction_opcode, + uint32_t num_opcodes_to_ignore) { + protobufs::InstructionDescriptor result; + result.set_base_instruction_result_id(base_instruction_result_id); + result.set_target_instruction_opcode(target_instruction_opcode); + result.set_num_opcodes_to_ignore(num_opcodes_to_ignore); + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/instruction_descriptor.h b/3rdparty/spirv-tools/source/fuzz/instruction_descriptor.h new file mode 100644 index 000000000..116431839 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/instruction_descriptor.h @@ -0,0 +1,40 @@ +// 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_INSTRUCTION_DESCRIPTOR_H_ +#define SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Looks for an instruction in |context| corresponding to |descriptor|. +// Returns |nullptr| if no such instruction can be found. +opt::Instruction* FindInstruction( + const protobufs::InstructionDescriptor& instruction_descriptor, + opt::IRContext* context); + +// Creates an InstructionDescriptor protobuf message from the given +// components. See the protobuf definition for details of what these +// components mean. +protobufs::InstructionDescriptor MakeInstructionDescriptor( + uint32_t base_instruction_result_id, SpvOp target_instruction_opcode, + uint32_t num_opcodes_to_ignore); + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto index 64ccd8efa..1e4527c88 100644 --- a/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -21,17 +21,37 @@ syntax = "proto3"; package spvtools.fuzz.protobufs; +message InstructionDescriptor { + + // Describes an instruction in some block of a function with respect to a + // base instruction. + + // The id of an instruction after which the instruction being described is + // believed to be located. It might be the using instruction itself. + uint32 base_instruction_result_id = 1; + + // The opcode for the instruction being described. + uint32 target_instruction_opcode = 2; + + // The number of matching opcodes to skip over when searching from the base + // instruction to the instruction being described. + uint32 num_opcodes_to_ignore = 3; + +} + message IdUseDescriptor { - // Describes a use of an id as an input operand to an instruction in some block - // of a function. + // Describes a use of an id as an input operand to an instruction in some + // block of a function. // Example: // - id_of_interest = 42 - // - target_instruction_opcode = OpStore + // - enclosing_instruction = ( + // base_instruction_result_id = 50, + // target_instruction_opcode = OpStore + // num_opcodes_to_ignore = 7 + // ) // - in_operand_index = 1 - // - base_instruction_result_id = 50 - // - num_opcodes_to_ignore = 7 // represents a use of id 42 as input operand 1 to an OpStore instruction, // such that the OpStore instruction can be found in the same basic block as // the instruction with result id 50, and in particular is the 8th OpStore @@ -41,20 +61,11 @@ message IdUseDescriptor { // An id that we would like to be able to find a use of. uint32 id_of_interest = 1; - // The opcode for the instruction that uses the id. - uint32 target_instruction_opcode = 2; - // The input operand index at which the use is expected. + InstructionDescriptor enclosing_instruction = 2; + uint32 in_operand_index = 3; - // The id of an instruction after which the instruction that contains the use - // is believed to occur; it might be the using instruction itself. - uint32 base_instruction_result_id = 4; - - // The number of matching opcodes to skip over when searching for the using - // instruction from the base instruction. - uint32 num_opcodes_to_ignore = 5; - } message DataDescriptor { @@ -167,12 +178,18 @@ message Transformation { TransformationAddTypeFloat add_type_float = 6; TransformationAddTypeInt add_type_int = 7; TransformationAddDeadBreak add_dead_break = 8; - TransformationReplaceBooleanConstantWithConstantBinary replace_boolean_constant_with_constant_binary = 9; + TransformationReplaceBooleanConstantWithConstantBinary + replace_boolean_constant_with_constant_binary = 9; 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; + TransformationConstructComposite construct_composite = 16; + TransformationSetLoopControl set_loop_control = 17; + TransformationSetFunctionControl set_function_control = 18; + TransformationAddNoContractionDecoration add_no_contraction_decoration = 19; // Add additional option using the next available number. } } @@ -245,6 +262,15 @@ message TransformationAddDeadContinue { } +message TransformationAddNoContractionDecoration { + + // Applies OpDecorate NoContraction to the given result id + + // Result id to be decorated + uint32 result_id = 1; + +} + message TransformationAddTypeBoolean { // Adds OpTypeBool to the module @@ -317,6 +343,30 @@ message TransformationCopyObject { } +message TransformationConstructComposite { + + // A transformation that introduces an OpCompositeConstruct instruction to + // make a composite object. + + // Id of the type of the composite that is to be constructed + uint32 composite_type_id = 1; + + // Ids of the objects that will form the components of the composite + repeated uint32 component = 2; + + // The id of an instruction in a block + uint32 base_instruction_id = 3; + + // An offset, such that OpCompositeConstruct instruction should be inserted + // right before the instruction |offset| instructions after + // |base_instruction_id| + uint32 offset = 4; + + // A fresh id for the composite object + uint32 fresh_id = 5; + +} + message TransformationMoveBlockDown { // A transformation that moves a basic block to be one position lower in @@ -384,6 +434,59 @@ message TransformationReplaceIdWithSynonym { uint32 fresh_id_for_temporary = 3; } +message TransformationSetFunctionControl { + + // A transformation that sets the function control operand of an OpFunction + // instruction. + + // The result id of an OpFunction instruction + uint32 function_id = 1; + + // The value to which the 'function control' operand should be set. + uint32 function_control = 2; + +} + +message TransformationSetLoopControl { + + // A transformation that sets the loop control operand of an OpLoopMerge + // instruction. + + // The id of a basic block that should contain OpLoopMerge + uint32 block_id = 1; + + // The value to which the 'loop control' operand should be set. + // This must be a legal loop control mask. + uint32 loop_control = 2; + + // Provides a peel count value for the loop. Used if and only if the + // PeelCount bit is set. Must be zero if the PeelCount bit is not set (can + // still be zero if this bit is set). + uint32 peel_count = 3; + + // Provides a partial count value for the loop. Used if and only if the + // PartialCount bit is set. Must be zero if the PartialCount bit is not set + // (can still be zero if this bit is set). + uint32 partial_count = 4; + +} + +message TransformationSetSelectionControl { + + // A transformation that sets the selection control operand of an + // OpSelectionMerge instruction. + + // The id of a basic block that should contain OpSelectionMerge + uint32 block_id = 1; + + // The value to which the 'selection control' operand should be set. + // Although technically 'selection control' is a literal mask that can be + // some combination of 'None', 'Flatten' and 'DontFlatten', the combination + // 'Flatten | DontFlatten' does not make sense and is not allowed here. + uint32 selection_control = 2; + +} + message TransformationSplitBlock { // A transformation that splits a basic block into two basic blocks diff --git a/3rdparty/spirv-tools/source/fuzz/transformation.cpp b/3rdparty/spirv-tools/source/fuzz/transformation.cpp index 65966f350..9d0841f5b 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation.cpp @@ -16,21 +16,26 @@ #include +#include "source/fuzz/transformation_add_constant_boolean.h" +#include "source/fuzz/transformation_add_constant_scalar.h" +#include "source/fuzz/transformation_add_dead_break.h" +#include "source/fuzz/transformation_add_dead_continue.h" +#include "source/fuzz/transformation_add_no_contraction_decoration.h" +#include "source/fuzz/transformation_add_type_boolean.h" +#include "source/fuzz/transformation_add_type_float.h" +#include "source/fuzz/transformation_add_type_int.h" +#include "source/fuzz/transformation_add_type_pointer.h" +#include "source/fuzz/transformation_construct_composite.h" +#include "source/fuzz/transformation_copy_object.h" +#include "source/fuzz/transformation_move_block_down.h" +#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h" +#include "source/fuzz/transformation_replace_constant_with_uniform.h" +#include "source/fuzz/transformation_replace_id_with_synonym.h" +#include "source/fuzz/transformation_set_function_control.h" +#include "source/fuzz/transformation_set_loop_control.h" +#include "source/fuzz/transformation_set_selection_control.h" +#include "source/fuzz/transformation_split_block.h" #include "source/util/make_unique.h" -#include "transformation_add_constant_boolean.h" -#include "transformation_add_constant_scalar.h" -#include "transformation_add_dead_break.h" -#include "transformation_add_dead_continue.h" -#include "transformation_add_type_boolean.h" -#include "transformation_add_type_float.h" -#include "transformation_add_type_int.h" -#include "transformation_add_type_pointer.h" -#include "transformation_copy_object.h" -#include "transformation_move_block_down.h" -#include "transformation_replace_boolean_constant_with_constant_binary.h" -#include "transformation_replace_constant_with_uniform.h" -#include "transformation_replace_id_with_synonym.h" -#include "transformation_split_block.h" namespace spvtools { namespace fuzz { @@ -51,6 +56,10 @@ std::unique_ptr Transformation::FromMessage( case protobufs::Transformation::TransformationCase::kAddDeadContinue: return MakeUnique( message.add_dead_continue()); + case protobufs::Transformation::TransformationCase:: + kAddNoContractionDecoration: + return MakeUnique( + message.add_no_contraction_decoration()); case protobufs::Transformation::TransformationCase::kAddTypeBoolean: return MakeUnique( message.add_type_boolean()); @@ -61,6 +70,9 @@ std::unique_ptr Transformation::FromMessage( case protobufs::Transformation::TransformationCase::kAddTypePointer: return MakeUnique( message.add_type_pointer()); + case protobufs::Transformation::TransformationCase::kConstructComposite: + return MakeUnique( + message.construct_composite()); case protobufs::Transformation::TransformationCase::kCopyObject: return MakeUnique(message.copy_object()); case protobufs::Transformation::TransformationCase::kMoveBlockDown: @@ -76,6 +88,15 @@ std::unique_ptr Transformation::FromMessage( case protobufs::Transformation::TransformationCase::kReplaceIdWithSynonym: return MakeUnique( message.replace_id_with_synonym()); + case protobufs::Transformation::TransformationCase::kSetFunctionControl: + return MakeUnique( + message.set_function_control()); + case protobufs::Transformation::TransformationCase::kSetLoopControl: + return MakeUnique( + message.set_loop_control()); + case protobufs::Transformation::TransformationCase::kSetSelectionControl: + return MakeUnique( + message.set_selection_control()); case protobufs::Transformation::TransformationCase::kSplitBlock: return MakeUnique(message.split_block()); case protobufs::Transformation::TRANSFORMATION_NOT_SET: diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.cpp new file mode 100644 index 000000000..7f22cc22c --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.cpp @@ -0,0 +1,110 @@ +// 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_add_no_contraction_decoration.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddNoContractionDecoration:: + TransformationAddNoContractionDecoration( + const spvtools::fuzz::protobufs:: + TransformationAddNoContractionDecoration& message) + : message_(message) {} + +TransformationAddNoContractionDecoration:: + TransformationAddNoContractionDecoration(uint32_t result_id) { + message_.set_result_id(result_id); +} + +bool TransformationAddNoContractionDecoration::IsApplicable( + opt::IRContext* context, + const spvtools::fuzz::FactManager& /*unused*/) const { + // |message_.result_id| must be the id of an instruction. + auto instr = context->get_def_use_mgr()->GetDef(message_.result_id()); + if (!instr) { + return false; + } + // The instruction must be arithmetic. + return IsArithmetic(instr->opcode()); +} + +void TransformationAddNoContractionDecoration::Apply( + opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { + // Add a NoContraction decoration targeting |message_.result_id|. + context->get_decoration_mgr()->AddDecoration(message_.result_id(), + SpvDecorationNoContraction); +} + +protobufs::Transformation TransformationAddNoContractionDecoration::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_add_no_contraction_decoration() = message_; + return result; +} + +bool TransformationAddNoContractionDecoration::IsArithmetic(uint32_t opcode) { + switch (opcode) { + case SpvOpSNegate: + case SpvOpFNegate: + case SpvOpIAdd: + case SpvOpFAdd: + case SpvOpISub: + case SpvOpFSub: + case SpvOpIMul: + case SpvOpFMul: + case SpvOpUDiv: + case SpvOpSDiv: + case SpvOpFDiv: + case SpvOpUMod: + case SpvOpSRem: + case SpvOpSMod: + case SpvOpFRem: + case SpvOpFMod: + case SpvOpVectorTimesScalar: + case SpvOpMatrixTimesScalar: + case SpvOpVectorTimesMatrix: + case SpvOpMatrixTimesVector: + case SpvOpMatrixTimesMatrix: + case SpvOpOuterProduct: + case SpvOpDot: + case SpvOpIAddCarry: + case SpvOpISubBorrow: + case SpvOpUMulExtended: + case SpvOpSMulExtended: + case SpvOpAny: + case SpvOpAll: + case SpvOpIsNan: + case SpvOpIsInf: + case SpvOpIsFinite: + case SpvOpIsNormal: + case SpvOpSignBitSet: + case SpvOpLessOrGreater: + case SpvOpOrdered: + case SpvOpUnordered: + case SpvOpLogicalEqual: + case SpvOpLogicalNotEqual: + case SpvOpLogicalOr: + case SpvOpLogicalAnd: + case SpvOpLogicalNot: + return true; + default: + return false; + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.h b/3rdparty/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.h new file mode 100644 index 000000000..cec1b2cde --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.h @@ -0,0 +1,58 @@ +// 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_ADD_NO_CONTRACTION_DECORATION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_ + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddNoContractionDecoration : public Transformation { + public: + explicit TransformationAddNoContractionDecoration( + const protobufs::TransformationAddNoContractionDecoration& message); + + explicit TransformationAddNoContractionDecoration(uint32_t fresh_id); + + // - |message_.result_id| must be the result id of an arithmetic instruction, + // as defined by the SPIR-V specification. + // - It does not matter whether this instruction is already annotated with the + // NoContraction decoration. + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Adds a decoration of the form: + // 'OpDecoration |message_.result_id| NoContraction' + // to the module. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if and only if |opcode| is the opcode of an arithmetic + // instruction, as defined by the SPIR-V specification. + static bool IsArithmetic(uint32_t opcode); + + private: + protobufs::TransformationAddNoContractionDecoration message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.cpp new file mode 100644 index 000000000..3c6c956a0 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.cpp @@ -0,0 +1,310 @@ +// 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_construct_composite.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationConstructComposite::TransformationConstructComposite( + const protobufs::TransformationConstructComposite& message) + : message_(message) {} + +TransformationConstructComposite::TransformationConstructComposite( + uint32_t composite_type_id, std::vector component, + uint32_t base_instruction_id, uint32_t offset, uint32_t fresh_id) { + message_.set_composite_type_id(composite_type_id); + for (auto a_component : component) { + message_.add_component(a_component); + } + message_.set_base_instruction_id(base_instruction_id); + message_.set_offset(offset); + message_.set_fresh_id(fresh_id); +} + +bool TransformationConstructComposite::IsApplicable( + opt::IRContext* context, const FactManager& /*fact_manager*/) const { + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + // We require the id for the composite constructor to be unused. + return false; + } + + auto base_instruction = + context->get_def_use_mgr()->GetDef(message_.base_instruction_id()); + if (!base_instruction) { + // The given id to insert after is not defined. + return false; + } + + auto destination_block = context->get_instr_block(base_instruction); + if (!destination_block) { + // The given id to insert after is not in a block. + return false; + } + + auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset( + destination_block, base_instruction, message_.offset()); + + if (insert_before == destination_block->end()) { + // The offset was inappropriate. + return false; + } + + auto composite_type = + context->get_type_mgr()->GetType(message_.composite_type_id()); + + if (!fuzzerutil::IsCompositeType(composite_type)) { + // The type must actually be a composite. + return false; + } + + // If the type is an array, matrix, struct or vector, the components need to + // be suitable for constructing something of that type. + if (composite_type->AsArray() && !ComponentsForArrayConstructionAreOK( + context, *composite_type->AsArray())) { + return false; + } + if (composite_type->AsMatrix() && !ComponentsForMatrixConstructionAreOK( + context, *composite_type->AsMatrix())) { + return false; + } + if (composite_type->AsStruct() && !ComponentsForStructConstructionAreOK( + context, *composite_type->AsStruct())) { + return false; + } + if (composite_type->AsVector() && !ComponentsForVectorConstructionAreOK( + context, *composite_type->AsVector())) { + return false; + } + + // Now check whether every component being used to initialize the composite is + // available at the desired program point. + for (auto& component : message_.component()) { + auto component_inst = context->get_def_use_mgr()->GetDef(component); + if (!context->get_instr_block(component)) { + // The component does not have a block; that means it is in global scope, + // which is OK. (Whether the component actually corresponds to an + // instruction is checked above when determining whether types are + // suitable.) + continue; + } + // Check whether the component is available. + if (insert_before->HasResultId() && + insert_before->result_id() == component) { + // This constitutes trying to use an id right before it is defined. The + // special case is needed due to an instruction always dominating itself. + return false; + } + if (!context + ->GetDominatorAnalysis( + context->get_instr_block(&*insert_before)->GetParent()) + ->Dominates(component_inst, &*insert_before)) { + // The instruction defining the component must dominate the instruction we + // wish to insert the composite before. + return false; + } + } + + return true; +} + +void TransformationConstructComposite::Apply(opt::IRContext* context, + FactManager* fact_manager) const { + // Use the base and offset information from the transformation to determine + // where in the module a new instruction should be inserted. + auto base_instruction = + context->get_def_use_mgr()->GetDef(message_.base_instruction_id()); + auto destination_block = context->get_instr_block(base_instruction); + auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset( + destination_block, base_instruction, message_.offset()); + + // Prepare the input operands for an OpCompositeConstruct instruction. + opt::Instruction::OperandList in_operands; + for (auto& component_id : message_.component()) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {component_id}}); + } + + // Insert an OpCompositeConstruct instruction. + insert_before.InsertBefore(MakeUnique( + context, SpvOpCompositeConstruct, message_.composite_type_id(), + message_.fresh_id(), in_operands)); + + // Inform the fact manager that we now have new synonyms: every component of + // the composite is synonymous with the id used to construct that component. + auto composite_type = + context->get_type_mgr()->GetType(message_.composite_type_id()); + uint32_t index = 0; + for (auto component : message_.component()) { + protobufs::Fact fact; + fact.mutable_id_synonym_fact()->set_id(component); + fact.mutable_id_synonym_fact()->mutable_data_descriptor()->set_object( + message_.fresh_id()); + fact.mutable_id_synonym_fact()->mutable_data_descriptor()->add_index(index); + fact_manager->AddFact(fact, context); + if (composite_type->AsVector()) { + // The vector case is a bit fiddly, because one argument to a vector + // constructor can cover more than one element. + auto component_type = context->get_type_mgr()->GetType( + context->get_def_use_mgr()->GetDef(component)->type_id()); + if (component_type->AsVector()) { + assert(component_type->AsVector()->element_type() == + composite_type->AsVector()->element_type()); + index += component_type->AsVector()->element_count(); + } else { + assert(component_type == composite_type->AsVector()->element_type()); + index++; + } + } else { + // The non-vector cases are all easy: the constructor has exactly the same + // number of arguments as the number of sub-components, so we can just + // increment the index. + index++; + } + } + + fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +bool TransformationConstructComposite::ComponentsForArrayConstructionAreOK( + opt::IRContext* context, const opt::analysis::Array& array_type) const { + if (array_type.length_info().words[0] != + opt::analysis::Array::LengthInfo::kConstant) { + // We only handle constant-sized arrays. + return false; + } + if (array_type.length_info().words.size() != 2) { + // We only handle the case where the array size can be captured in a single + // word. + return false; + } + // Get the array size. + auto array_size = array_type.length_info().words[1]; + if (static_cast(message_.component().size()) != array_size) { + // The number of components must match the array size. + return false; + } + // Check that each component is the result id of an instruction whose type is + // the array's element type. + for (auto component_id : message_.component()) { + auto inst = context->get_def_use_mgr()->GetDef(component_id); + if (inst == nullptr || !inst->type_id()) { + // The component does not correspond to an instruction with a result + // type. + return false; + } + auto component_type = context->get_type_mgr()->GetType(inst->type_id()); + assert(component_type); + if (component_type != array_type.element_type()) { + // The component's type does not match the array's element type. + return false; + } + } + return true; +} + +bool TransformationConstructComposite::ComponentsForMatrixConstructionAreOK( + opt::IRContext* context, const opt::analysis::Matrix& matrix_type) const { + if (static_cast(message_.component().size()) != + matrix_type.element_count()) { + // The number of components must match the number of columns of the matrix. + return false; + } + // Check that each component is the result id of an instruction whose type is + // the matrix's column type. + for (auto component_id : message_.component()) { + auto inst = context->get_def_use_mgr()->GetDef(component_id); + if (inst == nullptr || !inst->type_id()) { + // The component does not correspond to an instruction with a result + // type. + return false; + } + auto component_type = context->get_type_mgr()->GetType(inst->type_id()); + assert(component_type); + if (component_type != matrix_type.element_type()) { + // The component's type does not match the matrix's column type. + return false; + } + } + return true; +} + +bool TransformationConstructComposite::ComponentsForStructConstructionAreOK( + opt::IRContext* context, const opt::analysis::Struct& struct_type) const { + if (static_cast(message_.component().size()) != + struct_type.element_types().size()) { + // The number of components must match the number of fields of the struct. + return false; + } + // Check that each component is the result id of an instruction those type + // matches the associated field type. + for (uint32_t field_index = 0; + field_index < struct_type.element_types().size(); field_index++) { + auto inst = + context->get_def_use_mgr()->GetDef(message_.component()[field_index]); + if (inst == nullptr || !inst->type_id()) { + // The component does not correspond to an instruction with a result + // type. + return false; + } + auto component_type = context->get_type_mgr()->GetType(inst->type_id()); + assert(component_type); + if (component_type != struct_type.element_types()[field_index]) { + // The component's type does not match the corresponding field type. + return false; + } + } + return true; +} + +bool TransformationConstructComposite::ComponentsForVectorConstructionAreOK( + opt::IRContext* context, const opt::analysis::Vector& vector_type) const { + uint32_t base_element_count = 0; + auto element_type = vector_type.element_type(); + for (auto& component_id : message_.component()) { + auto inst = context->get_def_use_mgr()->GetDef(component_id); + if (inst == nullptr || !inst->type_id()) { + // The component does not correspond to an instruction with a result + // type. + return false; + } + auto component_type = context->get_type_mgr()->GetType(inst->type_id()); + assert(component_type); + if (component_type == element_type) { + base_element_count++; + } else if (component_type->AsVector() && + component_type->AsVector()->element_type() == element_type) { + base_element_count += component_type->AsVector()->element_count(); + } else { + // The component was not appropriate; e.g. no type corresponding to the + // given id was found, or the type that was found was not compatible + // with the vector being constructed. + return false; + } + } + // The number of components provided (when vector components are flattened + // out) needs to match the length of the vector being constructed. + return base_element_count == vector_type.element_count(); +} + +protobufs::Transformation TransformationConstructComposite::ToMessage() const { + protobufs::Transformation result; + *result.mutable_construct_composite() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.h b/3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.h new file mode 100644 index 000000000..b7124570e --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.h @@ -0,0 +1,88 @@ +// 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_CONSTRUCT_COMPOSITE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_CONSTRUCT_COMPOSITE_H_ + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationConstructComposite : public Transformation { + public: + explicit TransformationConstructComposite( + const protobufs::TransformationConstructComposite& message); + + TransformationConstructComposite(uint32_t composite_type_id, + std::vector component, + uint32_t base_instruction_id, + uint32_t offset, uint32_t fresh_id); + + // - |message_.fresh_id| must not be used by the module. + // - |message_.composite_type_id| must be the id of a composite type + // - The elements of |message_.component| must be result ids that are + // suitable for constructing an element of the given composite type, in + // order + // - The elements of |message_.component| must not be the target of any + // decorations. + // - |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 OpCompositeConstruct instruction directly + // before 'inst'. + // - Each element of |message_.component| must be available directly before + // 'inst'. + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // Inserts a new OpCompositeConstruct instruction, with id + // |message_.fresh_id|, directly before the instruction identified by + // |message_.base_instruction_id| and |message_.offset|. The instruction + // creates a composite of type |message_.composite_type_id| using the ids of + // |message_.component|. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Helper to decide whether the components of the transformation are suitable + // for constructing an array of the given type. + bool ComponentsForArrayConstructionAreOK( + opt::IRContext* context, const opt::analysis::Array& array_type) const; + + // Similar, but for matrices. + bool ComponentsForMatrixConstructionAreOK( + opt::IRContext* context, const opt::analysis::Matrix& matrix_type) const; + + // Similar, but for structs. + bool ComponentsForStructConstructionAreOK( + opt::IRContext* context, const opt::analysis::Struct& struct_type) const; + + // Similar, but for vectors. + bool ComponentsForVectorConstructionAreOK( + opt::IRContext* context, const opt::analysis::Vector& vector_type) const; + + protobufs::TransformationConstructComposite message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_CONSTRUCT_COMPOSITE_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_copy_object.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_copy_object.cpp index cfd97aeb8..2c6c2fdcf 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_copy_object.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_copy_object.cpp @@ -46,7 +46,7 @@ bool TransformationCopyObject::IsApplicable( if (!object_inst) { return false; } - if (!IsCopyable(context, object_inst)) { + if (!fuzzerutil::CanMakeSynonymOf(context, object_inst)) { return false; } @@ -71,7 +71,8 @@ bool TransformationCopyObject::IsApplicable( return false; } - if (!CanInsertCopyBefore(insert_before)) { + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject, + insert_before)) { return false; } @@ -91,13 +92,6 @@ bool TransformationCopyObject::IsApplicable( void TransformationCopyObject::Apply(opt::IRContext* context, FactManager* fact_manager) const { - // - 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. - // The id of the object to be copied must exist auto object_inst = context->get_def_use_mgr()->GetDef(message_.object()); assert(object_inst && "The object to be copied must exist."); auto base_instruction = @@ -132,43 +126,5 @@ protobufs::Transformation TransformationCopyObject::ToMessage() const { return result; } -bool TransformationCopyObject::IsCopyable(opt::IRContext* ir_context, - opt::Instruction* inst) { - if (!inst->HasResultId()) { - // We can only apply OpCopyObject to instructions that generate ids. - return false; - } - if (!inst->type_id()) { - // We can only apply OpCopyObject to instructions that have types. - return false; - } - // We do not copy objects that have decorations: if the copy is not - // decorated analogously, using the original object vs. its copy may not be - // equivalent. - // TODO(afd): it would be possible to make the copy but not add an id - // synonym. - return ir_context->get_decoration_mgr() - ->GetDecorationsFor(inst->result_id(), true) - .empty(); -} - -bool TransformationCopyObject::CanInsertCopyBefore( - const opt::BasicBlock::iterator& instruction_in_block) { - if (instruction_in_block->PreviousNode() && - (instruction_in_block->PreviousNode()->opcode() == SpvOpLoopMerge || - instruction_in_block->PreviousNode()->opcode() == SpvOpSelectionMerge)) { - // We cannot insert a copy directly after a merge instruction. - return false; - } - if (instruction_in_block->opcode() == SpvOpVariable) { - // We cannot insert a copy directly before a variable; variables in a - // function must be contiguous in the entry block. - return false; - } - // We cannot insert a copy directly before OpPhi, because OpPhi instructions - // need to be contiguous at the start of a block. - return instruction_in_block->opcode() != SpvOpPhi; -} - } // 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 index 20e58749b..1b4272652 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_copy_object.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_copy_object.h @@ -37,14 +37,14 @@ class TransformationCopyObject : public Transformation { // has a result type // - |message_.object| must not be the target of any decoration. // TODO(afd): consider copying decorations along with objects. - // - |message_.insert_after_id| must be the result id of an instruction + // - |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 be available directly before 'inst'. bool IsApplicable(opt::IRContext* context, const FactManager& fact_manager) const override; @@ -58,14 +58,6 @@ class TransformationCopyObject : public Transformation { protobufs::Transformation ToMessage() const override; - // Determines whether it is OK to make a copy of |inst|. - static bool IsCopyable(opt::IRContext* ir_context, opt::Instruction* inst); - - // Determines whether it is OK to insert a copy instruction before the given - // instruction. - static bool CanInsertCopyBefore( - const opt::BasicBlock::iterator& instruction_in_block); - private: protobufs::TransformationCopyObject message_; }; 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 032681465..b097767fb 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 @@ -238,7 +238,7 @@ bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable( // The id use descriptor must identify some instruction auto instruction = - transformation::FindInstruction(message_.id_use_descriptor(), context); + FindInstructionContainingUse(message_.id_use_descriptor(), context); if (instruction == nullptr) { return false; } @@ -268,7 +268,7 @@ TransformationReplaceBooleanConstantWithConstantBinary::ApplyWithResult( message_.fresh_id_for_binary_operation(), operands); opt::Instruction* result = binary_instruction.get(); auto instruction_containing_constant_use = - transformation::FindInstruction(message_.id_use_descriptor(), context); + FindInstructionContainingUse(message_.id_use_descriptor(), context); // 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 diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp index 48334ba96..405776ec0 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp @@ -149,7 +149,7 @@ bool TransformationReplaceConstantWithUniform::IsApplicable( // The id use descriptor must identify some instruction with respect to the // module. auto instruction_using_constant = - transformation::FindInstruction(message_.id_use_descriptor(), context); + FindInstructionContainingUse(message_.id_use_descriptor(), context); if (!instruction_using_constant) { return false; } @@ -188,7 +188,7 @@ void TransformationReplaceConstantWithUniform::Apply( spvtools::fuzz::FactManager* /*unused*/) const { // Get the instruction that contains the id use we wish to replace. auto instruction_containing_constant_use = - transformation::FindInstruction(message_.id_use_descriptor(), context); + FindInstructionContainingUse(message_.id_use_descriptor(), context); assert(instruction_containing_constant_use && "Precondition requires that the id use can be found."); assert(instruction_containing_constant_use->GetSingleWordInOperand( diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp index 0f28540b3..3eb66b9b3 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp @@ -20,6 +20,7 @@ #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/id_use_descriptor.h" #include "source/opt/types.h" +#include "source/util/make_unique.h" namespace spvtools { namespace fuzz { @@ -33,9 +34,7 @@ TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym( protobufs::IdUseDescriptor id_use_descriptor, protobufs::DataDescriptor data_descriptor, uint32_t fresh_id_for_temporary) { - assert(fresh_id_for_temporary == 0 && data_descriptor.index().size() == 0 && - "At present we do not support making an id that is synonymous with an " - "index into a composite."); + assert((fresh_id_for_temporary == 0) == (data_descriptor.index().empty())); *message_.mutable_id_use_descriptor() = std::move(id_use_descriptor); *message_.mutable_data_descriptor() = std::move(data_descriptor); message_.set_fresh_id_for_temporary(fresh_id_for_temporary); @@ -61,12 +60,15 @@ bool TransformationReplaceIdWithSynonym::IsApplicable( return false; } + // Does the id use descriptor in the transformation identify an instruction? auto use_instruction = - transformation::FindInstruction(message_.id_use_descriptor(), context); + FindInstructionContainingUse(message_.id_use_descriptor(), context); if (!use_instruction) { return false; } + // Is it legitimate to replace the use identified by the id use descriptor + // with a synonym? if (!ReplacingUseWithSynonymIsOk( context, use_instruction, message_.id_use_descriptor().in_operand_index(), @@ -74,8 +76,23 @@ bool TransformationReplaceIdWithSynonym::IsApplicable( return false; } - assert(message_.fresh_id_for_temporary() == 0); - assert(message_.data_descriptor().index().empty()); + if (message_.fresh_id_for_temporary() == 0) { + if (!message_.data_descriptor().index().empty()) { + // If we have no id to use as a temporary variable, we should not have any + // indices to extract from. + return false; + } + } else { + if (!fuzzerutil::IsFreshId(context, message_.fresh_id_for_temporary())) { + // The id to be used as a temporary needs to be fresh. + return false; + } + if (message_.data_descriptor().index_size() != 1) { + // At present we support just a single index to allow extracting directly + // from a composite. + return false; + } + } return true; } @@ -83,12 +100,79 @@ bool TransformationReplaceIdWithSynonym::IsApplicable( void TransformationReplaceIdWithSynonym::Apply( spvtools::opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const { - assert(message_.data_descriptor().index().empty()); auto instruction_to_change = - transformation::FindInstruction(message_.id_use_descriptor(), context); + FindInstructionContainingUse(message_.id_use_descriptor(), context); + + // Ultimately we are going to replace the id use identified in the + // transformation with |replacement_id|, which will either be the synonym's + // id, or the id of a temporary used to extract the synonym from a composite. + uint32_t replacement_id; + + if (message_.fresh_id_for_temporary()) { + // The transformation having a temporary variable means that we need to + // extract the synonym from a composite. + + uint32_t type_id_of_id_to_be_replaced = + context->get_def_use_mgr() + ->GetDef(message_.id_use_descriptor().id_of_interest()) + ->type_id(); + opt::analysis::Type* type_of_id_to_be_replaced = + context->get_type_mgr()->GetType(type_id_of_id_to_be_replaced); + opt::analysis::Type* type_of_composite = context->get_type_mgr()->GetType( + context->get_def_use_mgr() + ->GetDef(message_.data_descriptor().object()) + ->type_id()); + + // Intuitively we want to make an OpCompositeExtract instruction, to get the + // synonym out of the composite. But in the case of a vector, the synonym + // might involve multiple vector indices; e.g. the y and z components of a + // vec4 might be synonymous with a vec2, and in that case OpCompositeExtract + // doesn't give us what we want; we need to use OpVectorShuffle instead. + std::unique_ptr new_instruction; + if (type_of_composite->AsVector() && + type_of_composite->AsVector()->element_type() != + type_of_id_to_be_replaced) { + // We need to extract a vector from inside a vector, so we will need to + // use OpVectorShuffle. + + assert(type_of_id_to_be_replaced->AsVector()); + assert(type_of_id_to_be_replaced->AsVector()->element_type() == + type_of_composite->AsVector()->element_type()); + opt::Instruction::OperandList shuffle_operands = { + {SPV_OPERAND_TYPE_ID, {message_.data_descriptor().object()}}, + {SPV_OPERAND_TYPE_ID, {message_.data_descriptor().object()}}}; + for (uint32_t i = 0; + i < type_of_id_to_be_replaced->AsVector()->element_count(); i++) { + shuffle_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, + {message_.data_descriptor().index(0) + i}}); + } + new_instruction = MakeUnique( + context, SpvOpVectorShuffle, type_id_of_id_to_be_replaced, + message_.fresh_id_for_temporary(), shuffle_operands); + } else { + // We are either extracting from a non-vector, or extracting a scalar from + // a vector, so we can use OpCompositeExtract. + opt::Instruction::OperandList extract_operands = { + {SPV_OPERAND_TYPE_ID, {message_.data_descriptor().object()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {message_.data_descriptor().index(0)}}}; + new_instruction = MakeUnique( + context, SpvOpCompositeExtract, type_id_of_id_to_be_replaced, + message_.fresh_id_for_temporary(), extract_operands); + } + instruction_to_change->InsertBefore(std::move(new_instruction)); + + // The replacement id is the temporary variable we used to extract the + // synonym from a composite. + replacement_id = message_.fresh_id_for_temporary(); + fuzzerutil::UpdateModuleIdBound(context, replacement_id); + } else { + // The replacement id is the synonym's id. + replacement_id = message_.data_descriptor().object(); + } + instruction_to_change->SetInOperand( - message_.id_use_descriptor().in_operand_index(), - {message_.data_descriptor().object()}); + message_.id_use_descriptor().in_operand_index(), {replacement_id}); context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); } diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_set_function_control.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_set_function_control.cpp new file mode 100644 index 000000000..d2b61f198 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_set_function_control.cpp @@ -0,0 +1,100 @@ +// 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_set_function_control.h" + +namespace spvtools { +namespace fuzz { + +TransformationSetFunctionControl::TransformationSetFunctionControl( + const spvtools::fuzz::protobufs::TransformationSetFunctionControl& message) + : message_(message) {} + +TransformationSetFunctionControl::TransformationSetFunctionControl( + uint32_t function_id, uint32_t function_control) { + message_.set_function_id(function_id); + message_.set_function_control(function_control); +} + +bool TransformationSetFunctionControl::IsApplicable( + opt::IRContext* context, const FactManager& /*unused*/) const { + opt::Instruction* function_def_instruction = + FindFunctionDefInstruction(context); + if (!function_def_instruction) { + // The given function id does not correspond to any function. + return false; + } + uint32_t existing_function_control_mask = + function_def_instruction->GetSingleWordInOperand(0); + + // Check (via an assertion) that function control mask doesn't have any bad + // bits set. + uint32_t acceptable_function_control_bits = + SpvFunctionControlInlineMask | SpvFunctionControlDontInlineMask | + SpvFunctionControlPureMask | SpvFunctionControlConstMask; + // The following is to keep release-mode compilers happy as this variable is + // only used in an assertion. + (void)(acceptable_function_control_bits); + assert(!(message_.function_control() & ~acceptable_function_control_bits) && + "Nonsensical loop control bits were found."); + + // Check (via an assertion) that function control mask does not have both + // Inline and DontInline bits set. + assert(!((message_.function_control() & SpvFunctionControlInlineMask) && + (message_.function_control() & SpvFunctionControlDontInlineMask)) && + "It is not OK to set both the 'Inline' and 'DontInline' bits of a " + "function control mask"); + + // Check that Const and Pure are only present if they were present on the + // original function + for (auto mask_bit : + {SpvFunctionControlPureMask, SpvFunctionControlConstMask}) { + if ((message_.function_control() & mask_bit) && + !(existing_function_control_mask & mask_bit)) { + return false; + } + } + + return true; +} + +void TransformationSetFunctionControl::Apply(opt::IRContext* context, + FactManager* /*unused*/) const { + opt::Instruction* function_def_instruction = + FindFunctionDefInstruction(context); + function_def_instruction->SetInOperand(0, {message_.function_control()}); +} + +protobufs::Transformation TransformationSetFunctionControl::ToMessage() const { + protobufs::Transformation result; + *result.mutable_set_function_control() = message_; + return result; +} + +opt::Instruction* TransformationSetFunctionControl ::FindFunctionDefInstruction( + opt::IRContext* context) const { + // Look through all functions for a function whose defining instruction's + // result id matches |message_.function_id|, returning the defining + // instruction if found. + for (auto& function : *context->module()) { + if (function.DefInst().result_id() == message_.function_id()) { + return &function.DefInst(); + } + } + // A nullptr result indicates that no match was found. + return nullptr; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_set_function_control.h b/3rdparty/spirv-tools/source/fuzz/transformation_set_function_control.h new file mode 100644 index 000000000..0526bb9c5 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_set_function_control.h @@ -0,0 +1,58 @@ +// 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_SET_FUNCTION_CONTROL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_ + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSetFunctionControl : public Transformation { + public: + explicit TransformationSetFunctionControl( + const protobufs::TransformationSetFunctionControl& message); + + TransformationSetFunctionControl(uint32_t function_id, + uint32_t function_control); + + // - |message_.function_id| must be the result id of an OpFunction + // instruction. + // - |message_.function_control| must be a function control mask that sets + // at most one of 'Inline' or 'DontInline', and that may not contain 'Pure' + // (respectively 'Const') unless the existing function control mask contains + // 'Pure' (respectively 'Const'). + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // The function control operand of instruction |message_.function_id| is + // over-written with |message_.function_control|. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + opt::Instruction* FindFunctionDefInstruction(opt::IRContext* context) const; + + protobufs::TransformationSetFunctionControl message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.cpp new file mode 100644 index 000000000..9062f174a --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.cpp @@ -0,0 +1,216 @@ +// 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_set_loop_control.h" + +namespace spvtools { +namespace fuzz { + +TransformationSetLoopControl::TransformationSetLoopControl( + const spvtools::fuzz::protobufs::TransformationSetLoopControl& message) + : message_(message) {} + +TransformationSetLoopControl::TransformationSetLoopControl( + uint32_t block_id, uint32_t loop_control, uint32_t peel_count, + uint32_t partial_count) { + message_.set_block_id(block_id); + message_.set_loop_control(loop_control); + message_.set_peel_count(peel_count); + message_.set_partial_count(partial_count); +} + +bool TransformationSetLoopControl::IsApplicable( + opt::IRContext* context, const FactManager& /*unused*/) const { + // |message_.block_id| must identify a block that ends with OpLoopMerge. + auto block = context->get_instr_block(message_.block_id()); + if (!block) { + return false; + } + auto merge_inst = block->GetMergeInst(); + if (!merge_inst || merge_inst->opcode() != SpvOpLoopMerge) { + return false; + } + + // We sanity-check that the transformation does not try to set any meaningless + // bits of the loop control mask. + uint32_t all_loop_control_mask_bits_set = + SpvLoopControlUnrollMask | SpvLoopControlDontUnrollMask | + SpvLoopControlDependencyInfiniteMask | + SpvLoopControlDependencyLengthMask | SpvLoopControlMinIterationsMask | + SpvLoopControlMaxIterationsMask | SpvLoopControlIterationMultipleMask | + SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask; + + // The variable is only used in an assertion; the following keeps release-mode + // compilers happy. + (void)(all_loop_control_mask_bits_set); + + // No additional bits should be set. + assert(!(message_.loop_control() & ~all_loop_control_mask_bits_set)); + + // Grab the loop control mask currently associated with the OpLoopMerge + // instruction. + auto existing_loop_control_mask = + merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex); + + // Check that there is no attempt to set one of the loop controls that + // requires guarantees to hold. + for (SpvLoopControlMask mask : + {SpvLoopControlDependencyInfiniteMask, + SpvLoopControlDependencyLengthMask, SpvLoopControlMinIterationsMask, + SpvLoopControlMaxIterationsMask, SpvLoopControlIterationMultipleMask}) { + // We have a problem if this loop control bit was not set in the original + // loop control mask but is set by the transformation. + if (LoopControlBitIsAddedByTransformation(mask, + existing_loop_control_mask)) { + return false; + } + } + + if ((message_.loop_control() & + (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)) && + !(PeelCountIsSupported(context) && PartialCountIsSupported(context))) { + // At least one of PeelCount or PartialCount is used, but the SPIR-V version + // in question does not support these loop controls. + return false; + } + + if (message_.peel_count() > 0 && + !(message_.loop_control() & SpvLoopControlPeelCountMask)) { + // Peel count provided, but peel count mask bit not set. + return false; + } + + if (message_.partial_count() > 0 && + !(message_.loop_control() & SpvLoopControlPartialCountMask)) { + // Partial count provided, but partial count mask bit not set. + return false; + } + + // We must not set both 'don't unroll' and one of 'peel count' or 'partial + // count'. + return !((message_.loop_control() & SpvLoopControlDontUnrollMask) && + (message_.loop_control() & + (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask))); +} + +void TransformationSetLoopControl::Apply(opt::IRContext* context, + FactManager* /*unused*/) const { + // Grab the loop merge instruction and its associated loop control mask. + auto merge_inst = + context->get_instr_block(message_.block_id())->GetMergeInst(); + auto existing_loop_control_mask = + merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex); + + // We are going to replace the OpLoopMerge's operands with this list. + opt::Instruction::OperandList new_operands; + // We add the existing merge block and continue target ids. + new_operands.push_back(merge_inst->GetInOperand(0)); + new_operands.push_back(merge_inst->GetInOperand(1)); + // We use the loop control mask from the transformation. + new_operands.push_back( + {SPV_OPERAND_TYPE_LOOP_CONTROL, {message_.loop_control()}}); + + // It remains to determine what literals to provide, in association with + // the new loop control mask. + // + // For the loop controls that require guarantees to hold about the number + // of loop iterations, we need to keep, from the original OpLoopMerge, any + // literals associated with loop control bits that are still set. + + uint32_t literal_index = 0; // Indexes into the literals from the original + // instruction. + for (SpvLoopControlMask mask : + {SpvLoopControlDependencyLengthMask, SpvLoopControlMinIterationsMask, + SpvLoopControlMaxIterationsMask, SpvLoopControlIterationMultipleMask}) { + // Check whether the bit was set in the original loop control mask. + if (existing_loop_control_mask & mask) { + // Check whether the bit is set in the new loop control mask. + if (message_.loop_control() & mask) { + // Add the associated literal to our sequence of replacement operands. + new_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {merge_inst->GetSingleWordInOperand( + kLoopControlFirstLiteralInOperandIndex + literal_index)}}); + } + // Increment our index into the original loop control mask's literals, + // whether or not the bit was set in the new mask. + literal_index++; + } + } + + // If PeelCount is set in the new mask, |message_.peel_count| provides the + // associated peel count. + if (message_.loop_control() & SpvLoopControlPeelCountMask) { + new_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.peel_count()}}); + } + + // Similar, but for PartialCount. + if (message_.loop_control() & SpvLoopControlPartialCountMask) { + new_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.partial_count()}}); + } + + // Replace the input operands of the OpLoopMerge with the new operands we have + // accumulated. + merge_inst->SetInOperands(std::move(new_operands)); +} + +protobufs::Transformation TransformationSetLoopControl::ToMessage() const { + protobufs::Transformation result; + *result.mutable_set_loop_control() = message_; + return result; +} + +bool TransformationSetLoopControl::LoopControlBitIsAddedByTransformation( + SpvLoopControlMask loop_control_single_bit_mask, + uint32_t existing_loop_control_mask) const { + return !(loop_control_single_bit_mask & existing_loop_control_mask) && + (loop_control_single_bit_mask & message_.loop_control()); +} + +bool TransformationSetLoopControl::PartialCountIsSupported( + opt::IRContext* context) { + // TODO(afd): We capture the universal environments for which this loop + // control is definitely not supported. The check should be refined on + // demand for other target environments. + switch (context->grammar().target_env()) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + return false; + default: + return true; + } +} + +bool TransformationSetLoopControl::PeelCountIsSupported( + opt::IRContext* context) { + // TODO(afd): We capture the universal environments for which this loop + // control is definitely not supported. The check should be refined on + // demand for other target environments. + switch (context->grammar().target_env()) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + return false; + default: + return true; + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.h b/3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.h new file mode 100644 index 000000000..28b148cad --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.h @@ -0,0 +1,79 @@ +// 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_SET_LOOP_CONTROL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_ + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSetLoopControl : public Transformation { + public: + const static uint32_t kLoopControlMaskInOperandIndex = 2; + const static uint32_t kLoopControlFirstLiteralInOperandIndex = 3; + + explicit TransformationSetLoopControl( + const protobufs::TransformationSetLoopControl& message); + + TransformationSetLoopControl(uint32_t block_id, uint32_t loop_control, + uint32_t peel_count, uint32_t partial_count); + + // - |message_.block_id| must be a block containing an OpLoopMerge + // instruction. + // - |message_.loop_control| must be a legal loop control mask that + // only uses controls available in the SPIR-V version associated with + // |context|, and must not add loop controls that are only valid in the + // presence of guarantees about what the loop does (e.g. MinIterations). + // - |message_.peel_count| (respectively |message_.partial_count|) must be + // zero PeelCount (respectively PartialCount) is set in + // |message_.loop_control|. + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // - The loop control operand of the OpLoopMergeInstruction in + // |message_.block_id| is overwritten with |message_.loop_control|. + // - The literals associated with the loop control are updated to reflect any + // controls with associated literals that have been removed (e.g. + // MinIterations), and any that have been added (PeelCount and/or + // PartialCount). + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + // Does the version of SPIR-V being used support the PartialCount loop + // control? + static bool PartialCountIsSupported(opt::IRContext* context); + + // Does the version of SPIR-V being used support the PeelCount loop control? + static bool PeelCountIsSupported(opt::IRContext* context); + + private: + // Returns true if and only if |loop_single_bit_mask| is *not* set in + // |existing_loop_control| but *is* set in |message_.loop_control|. + bool LoopControlBitIsAddedByTransformation( + SpvLoopControlMask loop_control_single_bit_mask, + uint32_t existing_loop_control_mask) const; + + protobufs::TransformationSetLoopControl message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_set_selection_control.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_set_selection_control.cpp new file mode 100644 index 000000000..ebabdef20 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_set_selection_control.cpp @@ -0,0 +1,60 @@ +// 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_set_selection_control.h" + +namespace spvtools { +namespace fuzz { + +TransformationSetSelectionControl::TransformationSetSelectionControl( + const spvtools::fuzz::protobufs::TransformationSetSelectionControl& message) + : message_(message) {} + +TransformationSetSelectionControl::TransformationSetSelectionControl( + uint32_t block_id, uint32_t selection_control) { + message_.set_block_id(block_id); + message_.set_selection_control(selection_control); +} + +bool TransformationSetSelectionControl::IsApplicable( + opt::IRContext* context, const FactManager& /*unused*/) const { + assert((message_.selection_control() == SpvSelectionControlMaskNone || + message_.selection_control() == SpvSelectionControlFlattenMask || + message_.selection_control() == SpvSelectionControlDontFlattenMask) && + "Selection control should never be set to something other than " + "'None', 'Flatten' or 'DontFlatten'"); + if (auto block = context->get_instr_block(message_.block_id())) { + if (auto merge_inst = block->GetMergeInst()) { + return merge_inst->opcode() == SpvOpSelectionMerge; + } + } + // Either the block did not exit, or did not end with OpSelectionMerge. + return false; +} + +void TransformationSetSelectionControl::Apply(opt::IRContext* context, + FactManager* /*unused*/) const { + context->get_instr_block(message_.block_id()) + ->GetMergeInst() + ->SetInOperand(1, {message_.selection_control()}); +} + +protobufs::Transformation TransformationSetSelectionControl::ToMessage() const { + protobufs::Transformation result; + *result.mutable_set_selection_control() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_set_selection_control.h b/3rdparty/spirv-tools/source/fuzz/transformation_set_selection_control.h new file mode 100644 index 000000000..19e0c3cfd --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_set_selection_control.h @@ -0,0 +1,54 @@ +// 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_SET_SELECTION_CONTROL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_ + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSetSelectionControl : public Transformation { + public: + explicit TransformationSetSelectionControl( + const protobufs::TransformationSetSelectionControl& message); + + TransformationSetSelectionControl(uint32_t block_id, + uint32_t selection_control); + + // - |message_.block_id| must be a block containing an OpSelectionMerge + // instruction. + // - |message_.selection_control| must be one of None, Flatten or + // DontFlatten. + bool IsApplicable(opt::IRContext* context, + const FactManager& fact_manager) const override; + + // - The selection control operand of the OpSelectionMergeInstruction in + // |message_.block_id| is overwritten with |message_.selection_control|. + void Apply(opt::IRContext* context, FactManager* fact_manager) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationSetSelectionControl message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_ diff --git a/3rdparty/spirv-tools/source/opt/ir_context.cpp b/3rdparty/spirv-tools/source/opt/ir_context.cpp index 1c747b7a3..d940180da 100644 --- a/3rdparty/spirv-tools/source/opt/ir_context.cpp +++ b/3rdparty/spirv-tools/source/opt/ir_context.cpp @@ -94,6 +94,14 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) { if (analyses_to_invalidate & kAnalysisTypes) { analyses_to_invalidate |= kAnalysisConstants; } + + // The dominator analysis hold the psuedo entry and exit nodes from the CFG. + // Also if the CFG change the dominators many changed as well, so the + // dominator analysis should be invalidated as well. + if (analyses_to_invalidate & kAnalysisCFG) { + analyses_to_invalidate |= kAnalysisDominatorAnalysis; + } + if (analyses_to_invalidate & kAnalysisDefUse) { def_use_mgr_.reset(nullptr); } diff --git a/3rdparty/spirv-tools/source/opt/type_manager.cpp b/3rdparty/spirv-tools/source/opt/type_manager.cpp index d34948194..166b8281f 100644 --- a/3rdparty/spirv-tools/source/opt/type_manager.cpp +++ b/3rdparty/spirv-tools/source/opt/type_manager.cpp @@ -409,6 +409,22 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { {static_cast( type->AsForwardPointer()->storage_class())}}}); break; + case Type::kCooperativeMatrixNV: { + auto coop_mat = type->AsCooperativeMatrixNV(); + uint32_t const component_type = + GetTypeInstruction(coop_mat->component_type()); + if (component_type == 0) { + return 0; + } + typeInst = MakeUnique( + context(), SpvOpTypeCooperativeMatrixNV, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {component_type}}, + {SPV_OPERAND_TYPE_SCOPE_ID, {coop_mat->scope_id()}}, + {SPV_OPERAND_TYPE_ID, {coop_mat->rows_id()}}, + {SPV_OPERAND_TYPE_ID, {coop_mat->columns_id()}}}); + break; + } default: assert(false && "Unexpected type"); break; @@ -604,6 +620,14 @@ Type* TypeManager::RebuildType(const Type& type) { } break; } + case Type::kCooperativeMatrixNV: { + const CooperativeMatrixNV* cm_type = type.AsCooperativeMatrixNV(); + const Type* component_type = cm_type->component_type(); + rebuilt_ty = MakeUnique( + RebuildType(*component_type), cm_type->scope_id(), cm_type->rows_id(), + cm_type->columns_id()); + break; + } default: assert(false && "Unhandled type"); return nullptr; @@ -832,6 +856,12 @@ Type* TypeManager::RecordIfTypeDefinition(const Instruction& inst) { case SpvOpTypeAccelerationStructureNV: type = new AccelerationStructureNV(); break; + case SpvOpTypeCooperativeMatrixNV: + type = new CooperativeMatrixNV(GetType(inst.GetSingleWordInOperand(0)), + inst.GetSingleWordInOperand(1), + inst.GetSingleWordInOperand(2), + inst.GetSingleWordInOperand(3)); + break; default: SPIRV_UNIMPLEMENTED(consumer_, "unhandled type"); break; diff --git a/3rdparty/spirv-tools/source/opt/types.cpp b/3rdparty/spirv-tools/source/opt/types.cpp index 4f7150fc3..17f8fe920 100644 --- a/3rdparty/spirv-tools/source/opt/types.cpp +++ b/3rdparty/spirv-tools/source/opt/types.cpp @@ -127,6 +127,7 @@ std::unique_ptr Type::Clone() const { DeclareKindCase(PipeStorage); DeclareKindCase(NamedBarrier); DeclareKindCase(AccelerationStructureNV); + DeclareKindCase(CooperativeMatrixNV); #undef DeclareKindCase default: assert(false && "Unhandled type"); @@ -171,6 +172,7 @@ bool Type::operator==(const Type& other) const { DeclareKindCase(PipeStorage); DeclareKindCase(NamedBarrier); DeclareKindCase(AccelerationStructureNV); + DeclareKindCase(CooperativeMatrixNV); #undef DeclareKindCase default: assert(false && "Unhandled type"); @@ -220,6 +222,7 @@ void Type::GetHashWords(std::vector* words, DeclareKindCase(PipeStorage); DeclareKindCase(NamedBarrier); DeclareKindCase(AccelerationStructureNV); + DeclareKindCase(CooperativeMatrixNV); #undef DeclareKindCase default: assert(false && "Unhandled type"); @@ -654,6 +657,44 @@ void ForwardPointer::GetExtraHashWords( if (pointer_) pointer_->GetHashWords(words, seen); } +CooperativeMatrixNV::CooperativeMatrixNV(const Type* type, const uint32_t scope, + const uint32_t rows, + const uint32_t columns) + : Type(kCooperativeMatrixNV), + component_type_(type), + scope_id_(scope), + rows_id_(rows), + columns_id_(columns) { + assert(type != nullptr); + assert(scope != 0); + assert(rows != 0); + assert(columns != 0); +} + +std::string CooperativeMatrixNV::str() const { + std::ostringstream oss; + oss << "<" << component_type_->str() << ", " << scope_id_ << ", " << rows_id_ + << ", " << columns_id_ << ">"; + return oss.str(); +} + +void CooperativeMatrixNV::GetExtraHashWords( + std::vector* words, std::unordered_set* pSet) const { + component_type_->GetHashWords(words, pSet); + words->push_back(scope_id_); + words->push_back(rows_id_); + words->push_back(columns_id_); +} + +bool CooperativeMatrixNV::IsSameImpl(const Type* that, + IsSameCache* seen) const { + const CooperativeMatrixNV* mt = that->AsCooperativeMatrixNV(); + if (!mt) return false; + return component_type_->IsSameImpl(mt->component_type_, seen) && + scope_id_ == mt->scope_id_ && rows_id_ == mt->rows_id_ && + columns_id_ == mt->columns_id_ && HasSameDecorations(that); +} + } // namespace analysis } // namespace opt } // namespace spvtools diff --git a/3rdparty/spirv-tools/source/opt/types.h b/3rdparty/spirv-tools/source/opt/types.h index 57920df96..69071ea17 100644 --- a/3rdparty/spirv-tools/source/opt/types.h +++ b/3rdparty/spirv-tools/source/opt/types.h @@ -58,6 +58,7 @@ class ForwardPointer; class PipeStorage; class NamedBarrier; class AccelerationStructureNV; +class CooperativeMatrixNV; // Abstract class for a SPIR-V type. It has a bunch of As() methods, // which is used as a way to probe the actual . @@ -93,6 +94,7 @@ class Type { kPipeStorage, kNamedBarrier, kAccelerationStructureNV, + kCooperativeMatrixNV }; Type(Kind k) : kind_(k) {} @@ -196,6 +198,7 @@ class Type { DeclareCastMethod(PipeStorage) DeclareCastMethod(NamedBarrier) DeclareCastMethod(AccelerationStructureNV) + DeclareCastMethod(CooperativeMatrixNV) #undef DeclareCastMethod protected: @@ -597,6 +600,36 @@ class ForwardPointer : public Type { const Pointer* pointer_; }; +class CooperativeMatrixNV : public Type { + public: + CooperativeMatrixNV(const Type* type, const uint32_t scope, + const uint32_t rows, const uint32_t columns); + CooperativeMatrixNV(const CooperativeMatrixNV&) = default; + + std::string str() const override; + + CooperativeMatrixNV* AsCooperativeMatrixNV() override { return this; } + const CooperativeMatrixNV* AsCooperativeMatrixNV() const override { + return this; + } + + void GetExtraHashWords(std::vector*, + std::unordered_set*) const override; + + const Type* component_type() const { return component_type_; } + uint32_t scope_id() const { return scope_id_; } + uint32_t rows_id() const { return rows_id_; } + uint32_t columns_id() const { return columns_id_; } + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* component_type_; + const uint32_t scope_id_; + const uint32_t rows_id_; + const uint32_t columns_id_; +}; + #define DefineParameterlessType(type, name) \ class type : public Type { \ public: \ diff --git a/3rdparty/spirv-tools/source/print.cpp b/3rdparty/spirv-tools/source/print.cpp index f75e2d457..24ef5199f 100644 --- a/3rdparty/spirv-tools/source/print.cpp +++ b/3rdparty/spirv-tools/source/print.cpp @@ -15,7 +15,7 @@ #include "source/print.h" #if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \ - defined(SPIRV_FREEBSD) + defined(SPIRV_FREEBSD) || defined(SPIRV_EMSCRIPTEN) namespace spvtools { clr::reset::operator const char*() { return "\x1b[0m"; } diff --git a/3rdparty/spirv-tools/source/reduce/CMakeLists.txt b/3rdparty/spirv-tools/source/reduce/CMakeLists.txt index 35acf3fd4..00909ee30 100644 --- a/3rdparty/spirv-tools/source/reduce/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/reduce/CMakeLists.txt @@ -29,8 +29,6 @@ set(SPIRV_TOOLS_REDUCE_SOURCES remove_instruction_reduction_opportunity.h remove_function_reduction_opportunity.h remove_function_reduction_opportunity_finder.h - remove_opname_instruction_reduction_opportunity_finder.h - remove_relaxed_precision_decoration_opportunity_finder.h remove_selection_reduction_opportunity.h remove_selection_reduction_opportunity_finder.h remove_unreferenced_instruction_reduction_opportunity_finder.h @@ -57,11 +55,9 @@ set(SPIRV_TOOLS_REDUCE_SOURCES remove_function_reduction_opportunity.cpp remove_function_reduction_opportunity_finder.cpp remove_instruction_reduction_opportunity.cpp - remove_relaxed_precision_decoration_opportunity_finder.cpp remove_selection_reduction_opportunity.cpp remove_selection_reduction_opportunity_finder.cpp remove_unreferenced_instruction_reduction_opportunity_finder.cpp - remove_opname_instruction_reduction_opportunity_finder.cpp structured_loop_to_selection_reduction_opportunity.cpp structured_loop_to_selection_reduction_opportunity_finder.cpp conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp diff --git a/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp b/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp index 2feca4acc..0bd93b9b4 100644 --- a/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp +++ b/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp @@ -73,7 +73,7 @@ ConditionalBranchToSimpleConditionalBranchOpportunityFinder:: result.push_back( MakeUnique< ConditionalBranchToSimpleConditionalBranchReductionOpportunity>( - block.terminator(), redirect_to_true)); + context, block.terminator(), redirect_to_true)); } } } diff --git a/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp b/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp index 92c621e08..d744773bd 100644 --- a/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp +++ b/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp @@ -19,12 +19,15 @@ namespace spvtools { namespace reduce { +using opt::IRContext; using opt::Instruction; ConditionalBranchToSimpleConditionalBranchReductionOpportunity:: ConditionalBranchToSimpleConditionalBranchReductionOpportunity( - Instruction* conditional_branch_instruction, bool redirect_to_true) - : conditional_branch_instruction_(conditional_branch_instruction), + IRContext* context, Instruction* conditional_branch_instruction, + bool redirect_to_true) + : context_(context), + conditional_branch_instruction_(conditional_branch_instruction), redirect_to_true_(redirect_to_true) {} bool ConditionalBranchToSimpleConditionalBranchReductionOpportunity:: @@ -43,11 +46,24 @@ void ConditionalBranchToSimpleConditionalBranchReductionOpportunity::Apply() { uint32_t operand_to_copy = redirect_to_true_ ? kTrueBranchOperandIndex : kFalseBranchOperandIndex; + auto old_successor_block_id = + conditional_branch_instruction_->GetSingleWordInOperand( + operand_to_modify); + // Do the branch redirection. conditional_branch_instruction_->SetInOperand( operand_to_modify, {conditional_branch_instruction_->GetSingleWordInOperand( operand_to_copy)}); + + // The old successor block may have phi instructions; these will need to + // respect the change in edges. + AdaptPhiInstructionsForRemovedEdge( + context_->get_instr_block(conditional_branch_instruction_)->id(), + context_->cfg()->block(old_successor_block_id)); + + // We have changed the CFG. + context_->InvalidateAnalysesExceptFor(IRContext::Analysis::kAnalysisNone); } } // namespace reduce diff --git a/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h b/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h index 421906b44..1f9cb6d18 100644 --- a/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h +++ b/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h @@ -31,7 +31,8 @@ class ConditionalBranchToSimpleConditionalBranchReductionOpportunity // to the true target; otherwise, the true target will be changed to also // point to the false target. explicit ConditionalBranchToSimpleConditionalBranchReductionOpportunity( - opt::Instruction* conditional_branch_instruction, bool redirect_to_true); + opt::IRContext* context, opt::Instruction* conditional_branch_instruction, + bool redirect_to_true); bool PreconditionHolds() override; @@ -39,6 +40,7 @@ class ConditionalBranchToSimpleConditionalBranchReductionOpportunity void Apply() override; private: + opt::IRContext* context_; opt::Instruction* conditional_branch_instruction_; // If true, the false target will be changed to point to the true target; diff --git a/3rdparty/spirv-tools/source/reduce/reducer.cpp b/3rdparty/spirv-tools/source/reduce/reducer.cpp index ebb5d471e..bda41ce94 100644 --- a/3rdparty/spirv-tools/source/reduce/reducer.cpp +++ b/3rdparty/spirv-tools/source/reduce/reducer.cpp @@ -24,8 +24,6 @@ #include "source/reduce/operand_to_undef_reduction_opportunity_finder.h" #include "source/reduce/remove_block_reduction_opportunity_finder.h" #include "source/reduce/remove_function_reduction_opportunity_finder.h" -#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h" -#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h" #include "source/reduce/remove_selection_reduction_opportunity_finder.h" #include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h" #include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h" @@ -35,41 +33,32 @@ namespace spvtools { namespace reduce { -struct Reducer::Impl { - explicit Impl(spv_target_env env) : target_env(env) {} - - bool ReachedStepLimit(uint32_t current_step, - spv_const_reducer_options options); - - const spv_target_env target_env; // Target environment. - MessageConsumer consumer; // Message consumer. - InterestingnessFunction interestingness_function; - std::vector> passes; -}; - -Reducer::Reducer(spv_target_env env) : impl_(MakeUnique(env)) {} +Reducer::Reducer(spv_target_env target_env) : target_env_(target_env) {} Reducer::~Reducer() = default; void Reducer::SetMessageConsumer(MessageConsumer c) { - for (auto& pass : impl_->passes) { + for (auto& pass : passes_) { pass->SetMessageConsumer(c); } - impl_->consumer = std::move(c); + for (auto& pass : cleanup_passes_) { + pass->SetMessageConsumer(c); + } + consumer_ = std::move(c); } void Reducer::SetInterestingnessFunction( Reducer::InterestingnessFunction interestingness_function) { - impl_->interestingness_function = std::move(interestingness_function); + interestingness_function_ = std::move(interestingness_function); } Reducer::ReductionResultStatus Reducer::Run( std::vector&& binary_in, std::vector* binary_out, spv_const_reducer_options options, - spv_validator_options validator_options) const { + spv_validator_options validator_options) { std::vector current_binary(std::move(binary_in)); - spvtools::SpirvTools tools(impl_->target_env); + spvtools::SpirvTools tools(target_env_); assert(tools.IsValid() && "Failed to create SPIRV-Tools interface"); // Keeps track of how many reduction attempts have been tried. Reduction @@ -79,113 +68,49 @@ Reducer::ReductionResultStatus Reducer::Run( // Initial state should be valid. if (!tools.Validate(¤t_binary[0], current_binary.size(), validator_options)) { - impl_->consumer(SPV_MSG_INFO, nullptr, {}, - "Initial binary is invalid; stopping."); + consumer_(SPV_MSG_INFO, nullptr, {}, + "Initial binary is invalid; stopping."); return Reducer::ReductionResultStatus::kInitialStateInvalid; } // Initial state should be interesting. - if (!impl_->interestingness_function(current_binary, reductions_applied)) { - impl_->consumer(SPV_MSG_INFO, nullptr, {}, - "Initial state was not interesting; stopping."); + if (!interestingness_function_(current_binary, reductions_applied)) { + consumer_(SPV_MSG_INFO, nullptr, {}, + "Initial state was not interesting; stopping."); return Reducer::ReductionResultStatus::kInitialStateNotInteresting; } - // Determines whether, on completing one round of reduction passes, it is - // worthwhile trying a further round. - bool another_round_worthwhile = true; + Reducer::ReductionResultStatus result = + RunPasses(&passes_, options, validator_options, tools, ¤t_binary, + &reductions_applied); - // Apply round after round of reduction passes until we hit the reduction - // step limit, or deem that another round is not going to be worthwhile. - while (!impl_->ReachedStepLimit(reductions_applied, options) && - another_round_worthwhile) { - // At the start of a round of reduction passes, assume another round will - // not be worthwhile unless we find evidence to the contrary. - another_round_worthwhile = false; - - // Iterate through the available passes - for (auto& pass : impl_->passes) { - // If this pass hasn't reached its minimum granularity then it's - // worth eventually doing another round of reductions, in order to - // try this pass at a finer granularity. - another_round_worthwhile |= !pass->ReachedMinimumGranularity(); - - // Keep applying this pass at its current granularity until it stops - // working or we hit the reduction step limit. - impl_->consumer(SPV_MSG_INFO, nullptr, {}, - ("Trying pass " + pass->GetName() + ".").c_str()); - do { - auto maybe_result = pass->TryApplyReduction(current_binary); - if (maybe_result.empty()) { - // For this round, the pass has no more opportunities (chunks) to - // apply, so move on to the next pass. - impl_->consumer( - SPV_MSG_INFO, nullptr, {}, - ("Pass " + pass->GetName() + " did not make a reduction step.") - .c_str()); - break; - } - bool interesting = false; - std::stringstream stringstream; - reductions_applied++; - stringstream << "Pass " << pass->GetName() << " made reduction step " - << reductions_applied << "."; - impl_->consumer(SPV_MSG_INFO, nullptr, {}, - (stringstream.str().c_str())); - if (!tools.Validate(&maybe_result[0], maybe_result.size(), - validator_options)) { - // The reduction step went wrong and an invalid binary was produced. - // By design, this shouldn't happen; this is a safeguard to stop an - // invalid binary from being regarded as interesting. - impl_->consumer(SPV_MSG_INFO, nullptr, {}, - "Reduction step produced an invalid binary."); - if (options->fail_on_validation_error) { - return Reducer::ReductionResultStatus::kStateInvalid; - } - } else if (impl_->interestingness_function(maybe_result, - reductions_applied)) { - // Success! The binary produced by this reduction step is - // interesting, so make it the binary of interest henceforth, and - // note that it's worth doing another round of reduction passes. - impl_->consumer(SPV_MSG_INFO, nullptr, {}, - "Reduction step succeeded."); - current_binary = std::move(maybe_result); - interesting = true; - another_round_worthwhile = true; - } - // We must call this before the next call to TryApplyReduction. - pass->NotifyInteresting(interesting); - // Bail out if the reduction step limit has been reached. - } while (!impl_->ReachedStepLimit(reductions_applied, options)); - } + if (result == Reducer::ReductionResultStatus::kComplete) { + // Cleanup passes. + result = RunPasses(&cleanup_passes_, options, validator_options, tools, + ¤t_binary, &reductions_applied); } + if (result == Reducer::ReductionResultStatus::kComplete) { + consumer_(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping."); + } + + // Even if the reduction has failed by this point (e.g. due to producing an + // invalid binary), we still update the output binary for better debugging. *binary_out = std::move(current_binary); - // Report whether reduction completed, or bailed out early due to reaching - // the step limit. - if (impl_->ReachedStepLimit(reductions_applied, options)) { - impl_->consumer(SPV_MSG_INFO, nullptr, {}, - "Reached reduction step limit; stopping."); - return Reducer::ReductionResultStatus::kReachedStepLimit; - } - impl_->consumer(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping."); - return Reducer::ReductionResultStatus::kComplete; + return result; } void Reducer::AddDefaultReductionPasses() { - AddReductionPass(spvtools::MakeUnique< - RemoveOpNameInstructionReductionOpportunityFinder>()); - AddReductionPass(spvtools::MakeUnique< - RemoveRelaxedPrecisionDecorationOpportunityFinder>()); + AddReductionPass( + spvtools::MakeUnique< + RemoveUnreferencedInstructionReductionOpportunityFinder>(false)); AddReductionPass( spvtools::MakeUnique()); AddReductionPass( spvtools::MakeUnique()); AddReductionPass( spvtools::MakeUnique()); - AddReductionPass(spvtools::MakeUnique< - RemoveUnreferencedInstructionReductionOpportunityFinder>()); AddReductionPass(spvtools::MakeUnique< StructuredLoopToSelectionReductionOpportunityFinder>()); AddReductionPass( @@ -201,18 +126,117 @@ void Reducer::AddDefaultReductionPasses() { ConditionalBranchToSimpleConditionalBranchOpportunityFinder>()); AddReductionPass( spvtools::MakeUnique()); + + // Cleanup passes. + + AddCleanupReductionPass( + spvtools::MakeUnique< + RemoveUnreferencedInstructionReductionOpportunityFinder>(true)); } void Reducer::AddReductionPass( std::unique_ptr&& finder) { - impl_->passes.push_back(spvtools::MakeUnique( - impl_->target_env, std::move(finder))); + passes_.push_back( + spvtools::MakeUnique(target_env_, std::move(finder))); } -bool Reducer::Impl::ReachedStepLimit(uint32_t current_step, - spv_const_reducer_options options) { +void Reducer::AddCleanupReductionPass( + std::unique_ptr&& finder) { + cleanup_passes_.push_back( + spvtools::MakeUnique(target_env_, std::move(finder))); +} + +bool Reducer::ReachedStepLimit(uint32_t current_step, + spv_const_reducer_options options) { return current_step >= options->step_limit; } +Reducer::ReductionResultStatus Reducer::RunPasses( + std::vector>* passes, + spv_const_reducer_options options, spv_validator_options validator_options, + const SpirvTools& tools, std::vector* current_binary, + uint32_t* const reductions_applied) { + // Determines whether, on completing one round of reduction passes, it is + // worthwhile trying a further round. + bool another_round_worthwhile = true; + + // Apply round after round of reduction passes until we hit the reduction + // step limit, or deem that another round is not going to be worthwhile. + while (!ReachedStepLimit(*reductions_applied, options) && + another_round_worthwhile) { + // At the start of a round of reduction passes, assume another round will + // not be worthwhile unless we find evidence to the contrary. + another_round_worthwhile = false; + + // Iterate through the available passes. + for (auto& pass : *passes) { + // If this pass hasn't reached its minimum granularity then it's + // worth eventually doing another round of reductions, in order to + // try this pass at a finer granularity. + another_round_worthwhile |= !pass->ReachedMinimumGranularity(); + + // Keep applying this pass at its current granularity until it stops + // working or we hit the reduction step limit. + consumer_(SPV_MSG_INFO, nullptr, {}, + ("Trying pass " + pass->GetName() + ".").c_str()); + do { + auto maybe_result = pass->TryApplyReduction(*current_binary); + if (maybe_result.empty()) { + // For this round, the pass has no more opportunities (chunks) to + // apply, so move on to the next pass. + consumer_( + SPV_MSG_INFO, nullptr, {}, + ("Pass " + pass->GetName() + " did not make a reduction step.") + .c_str()); + break; + } + bool interesting = false; + std::stringstream stringstream; + (*reductions_applied)++; + stringstream << "Pass " << pass->GetName() << " made reduction step " + << *reductions_applied << "."; + consumer_(SPV_MSG_INFO, nullptr, {}, (stringstream.str().c_str())); + if (!tools.Validate(&maybe_result[0], maybe_result.size(), + validator_options)) { + // The reduction step went wrong and an invalid binary was produced. + // By design, this shouldn't happen; this is a safeguard to stop an + // invalid binary from being regarded as interesting. + consumer_(SPV_MSG_INFO, nullptr, {}, + "Reduction step produced an invalid binary."); + if (options->fail_on_validation_error) { + // In this mode, we fail, so we update the current binary so it is + // output for debugging. + *current_binary = std::move(maybe_result); + return Reducer::ReductionResultStatus::kStateInvalid; + } + } else if (interestingness_function_(maybe_result, + *reductions_applied)) { + // Success! The binary produced by this reduction step is + // interesting, so make it the binary of interest henceforth, and + // note that it's worth doing another round of reduction passes. + consumer_(SPV_MSG_INFO, nullptr, {}, "Reduction step succeeded."); + *current_binary = std::move(maybe_result); + interesting = true; + another_round_worthwhile = true; + } + // We must call this before the next call to TryApplyReduction. + pass->NotifyInteresting(interesting); + // Bail out if the reduction step limit has been reached. + } while (!ReachedStepLimit(*reductions_applied, options)); + } + } + + // Report whether reduction completed, or bailed out early due to reaching + // the step limit. + if (ReachedStepLimit(*reductions_applied, options)) { + consumer_(SPV_MSG_INFO, nullptr, {}, + "Reached reduction step limit; stopping."); + return Reducer::ReductionResultStatus::kReachedStepLimit; + } + + // The passes completed successfully, although we may still run more passes. + return Reducer::ReductionResultStatus::kComplete; +} + } // namespace reduce } // namespace spvtools diff --git a/3rdparty/spirv-tools/source/reduce/reducer.h b/3rdparty/spirv-tools/source/reduce/reducer.h index a9b28c381..864ce7570 100644 --- a/3rdparty/spirv-tools/source/reduce/reducer.h +++ b/3rdparty/spirv-tools/source/reduce/reducer.h @@ -50,7 +50,7 @@ class Reducer { using InterestingnessFunction = std::function&, uint32_t)>; - // Constructs an instance with the given target |env|, which is used to + // Constructs an instance with the given target |target_env|, which is used to // decode the binary to be reduced later. // // The constructed instance will have an empty message consumer, which just @@ -59,7 +59,7 @@ class Reducer { // // The constructed instance also needs to have an interestingness function // set and some reduction passes added to it in order to be useful. - explicit Reducer(spv_target_env env); + explicit Reducer(spv_target_env target_env); // Disables copy/move constructor/assignment operations. Reducer(const Reducer&) = delete; @@ -86,17 +86,34 @@ class Reducer { // that will be iterated over. void AddReductionPass(std::unique_ptr&& finder); + // Adds a cleanup reduction pass based on the given finder to the sequence of + // passes that will run after other passes. + void AddCleanupReductionPass( + std::unique_ptr&& finder); + // Reduces the given SPIR-V module |binary_out|. // The reduced binary ends up in |binary_out|. // A status is returned. ReductionResultStatus Run(std::vector&& binary_in, std::vector* binary_out, spv_const_reducer_options options, - spv_validator_options validator_options) const; + spv_validator_options validator_options); private: - struct Impl; // Opaque struct for holding internal data. - std::unique_ptr impl_; // Unique pointer to internal data. + static bool ReachedStepLimit(uint32_t current_step, + spv_const_reducer_options options); + + ReductionResultStatus RunPasses( + std::vector>* passes, + spv_const_reducer_options options, + spv_validator_options validator_options, const SpirvTools& tools, + std::vector* current_binary, uint32_t* reductions_applied); + + const spv_target_env target_env_; + MessageConsumer consumer_; + InterestingnessFunction interestingness_function_; + std::vector> passes_; + std::vector> cleanup_passes_; }; } // namespace reduce diff --git a/3rdparty/spirv-tools/source/reduce/reduction_util.cpp b/3rdparty/spirv-tools/source/reduce/reduction_util.cpp index 2b2b7e610..6f128dcb3 100644 --- a/3rdparty/spirv-tools/source/reduce/reduction_util.cpp +++ b/3rdparty/spirv-tools/source/reduce/reduction_util.cpp @@ -44,5 +44,22 @@ uint32_t FindOrCreateGlobalUndef(IRContext* context, uint32_t type_id) { return undef_id; } +void AdaptPhiInstructionsForRemovedEdge(uint32_t from_id, + opt::BasicBlock* to_block) { + to_block->ForEachPhiInst([&from_id](Instruction* phi_inst) { + Instruction::OperandList new_in_operands; + // Go through the OpPhi's input operands in (variable, parent) pairs. + for (uint32_t index = 0; index < phi_inst->NumInOperands(); index += 2) { + // Keep all pairs where the parent is not the block from which the edge + // is being removed. + if (phi_inst->GetInOperand(index + 1).words[0] != from_id) { + new_in_operands.push_back(phi_inst->GetInOperand(index)); + new_in_operands.push_back(phi_inst->GetInOperand(index + 1)); + } + } + phi_inst->SetInOperands(std::move(new_in_operands)); + }); +} + } // namespace reduce } // namespace spvtools diff --git a/3rdparty/spirv-tools/source/reduce/reduction_util.h b/3rdparty/spirv-tools/source/reduce/reduction_util.h index b8ffb6e46..7e7e153ab 100644 --- a/3rdparty/spirv-tools/source/reduce/reduction_util.h +++ b/3rdparty/spirv-tools/source/reduce/reduction_util.h @@ -30,6 +30,11 @@ extern const uint32_t kFalseBranchOperandIndex; // adding one if it does not exist. uint32_t FindOrCreateGlobalUndef(opt::IRContext* context, uint32_t type_id); +// Removes any components of |to_block|'s phi instructions relating to +// |from_id|. +void AdaptPhiInstructionsForRemovedEdge(uint32_t from_id, + opt::BasicBlock* to_block); + } // namespace reduce } // namespace spvtools diff --git a/3rdparty/spirv-tools/source/reduce/remove_opname_instruction_reduction_opportunity_finder.cpp b/3rdparty/spirv-tools/source/reduce/remove_opname_instruction_reduction_opportunity_finder.cpp deleted file mode 100644 index f687d716f..000000000 --- a/3rdparty/spirv-tools/source/reduce/remove_opname_instruction_reduction_opportunity_finder.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2018 Google Inc. -// -// 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/reduce/remove_opname_instruction_reduction_opportunity_finder.h" - -#include "source/opcode.h" -#include "source/opt/instruction.h" -#include "source/reduce/remove_instruction_reduction_opportunity.h" - -namespace spvtools { -namespace reduce { - -using opt::IRContext; - -std::vector> -RemoveOpNameInstructionReductionOpportunityFinder::GetAvailableOpportunities( - IRContext* context) const { - std::vector> result; - - for (auto& inst : context->module()->debugs2()) { - if (inst.opcode() == SpvOpName || inst.opcode() == SpvOpMemberName) { - result.push_back( - MakeUnique(&inst)); - } - } - return result; -} - -std::string RemoveOpNameInstructionReductionOpportunityFinder::GetName() const { - return "RemoveOpNameInstructionReductionOpportunityFinder"; -} - -} // namespace reduce -} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/reduce/remove_opname_instruction_reduction_opportunity_finder.h b/3rdparty/spirv-tools/source/reduce/remove_opname_instruction_reduction_opportunity_finder.h deleted file mode 100644 index 8b9fd6f02..000000000 --- a/3rdparty/spirv-tools/source/reduce/remove_opname_instruction_reduction_opportunity_finder.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2018 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_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_ -#define SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_ - -#include "source/reduce/reduction_opportunity_finder.h" - -namespace spvtools { -namespace reduce { - -// A finder for opportunities to remove OpName instructions. As well as making -// the module smaller, removing an OpName instruction may create opportunities -// for subsequently removing the instructions that create the ids to which the -// OpName applies. -class RemoveOpNameInstructionReductionOpportunityFinder - : public ReductionOpportunityFinder { - public: - RemoveOpNameInstructionReductionOpportunityFinder() = default; - - ~RemoveOpNameInstructionReductionOpportunityFinder() override = default; - - std::string GetName() const final; - - std::vector> GetAvailableOpportunities( - opt::IRContext* context) const final; - - private: -}; - -} // namespace reduce -} // namespace spvtools - -#endif // SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp b/3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp deleted file mode 100644 index 352cefb68..000000000 --- a/3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2018 Google Inc. -// -// 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/reduce/remove_relaxed_precision_decoration_opportunity_finder.h" - -#include "source/reduce/remove_instruction_reduction_opportunity.h" - -namespace spvtools { -namespace reduce { - -std::vector> -RemoveRelaxedPrecisionDecorationOpportunityFinder::GetAvailableOpportunities( - opt::IRContext* context) const { - std::vector> result; - - // Consider all annotation instructions - for (auto& inst : context->module()->annotations()) { - // We are interested in removing instructions of the form: - // SpvOpDecorate %id RelaxedPrecision - // and - // SpvOpMemberDecorate %id member RelaxedPrecision - if ((inst.opcode() == SpvOpDecorate && - inst.GetSingleWordInOperand(1) == SpvDecorationRelaxedPrecision) || - (inst.opcode() == SpvOpMemberDecorate && - inst.GetSingleWordInOperand(2) == SpvDecorationRelaxedPrecision)) { - result.push_back( - MakeUnique(&inst)); - } - } - return result; -} - -std::string RemoveRelaxedPrecisionDecorationOpportunityFinder::GetName() const { - return "RemoveRelaxedPrecisionDecorationOpportunityFinder"; -} - -} // namespace reduce -} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp b/3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp index dabee50cd..da61c8dba 100644 --- a/3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp +++ b/3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp @@ -21,28 +21,109 @@ namespace spvtools { namespace reduce { +RemoveUnreferencedInstructionReductionOpportunityFinder:: + RemoveUnreferencedInstructionReductionOpportunityFinder( + bool remove_constants_and_undefs) + : remove_constants_and_undefs_(remove_constants_and_undefs) {} + std::vector> RemoveUnreferencedInstructionReductionOpportunityFinder:: GetAvailableOpportunities(opt::IRContext* context) const { std::vector> result; + for (auto& inst : context->module()->debugs1()) { + if (context->get_def_use_mgr()->NumUses(&inst) > 0) { + continue; + } + result.push_back(MakeUnique(&inst)); + } + + for (auto& inst : context->module()->debugs2()) { + if (context->get_def_use_mgr()->NumUses(&inst) > 0) { + continue; + } + result.push_back(MakeUnique(&inst)); + } + + for (auto& inst : context->module()->debugs3()) { + if (context->get_def_use_mgr()->NumUses(&inst) > 0) { + continue; + } + result.push_back(MakeUnique(&inst)); + } + + for (auto& inst : context->module()->types_values()) { + if (context->get_def_use_mgr()->NumUsers(&inst) > 0) { + continue; + } + if (!remove_constants_and_undefs_ && + spvOpcodeIsConstantOrUndef(inst.opcode())) { + continue; + } + result.push_back(MakeUnique(&inst)); + } + + for (auto& inst : context->module()->annotations()) { + if (context->get_def_use_mgr()->NumUsers(&inst) > 0) { + continue; + } + + uint32_t decoration = SpvDecorationMax; + switch (inst.opcode()) { + case SpvOpDecorate: + case SpvOpDecorateId: + case SpvOpDecorateString: + decoration = inst.GetSingleWordInOperand(1u); + break; + case SpvOpMemberDecorate: + case SpvOpMemberDecorateString: + decoration = inst.GetSingleWordInOperand(2u); + break; + default: + break; + } + + // We conservatively only remove specific decorations that we believe will + // not change the shader interface, will not make the shader invalid, will + // actually be found in practice, etc. + + switch (decoration) { + case SpvDecorationRelaxedPrecision: + case SpvDecorationNoSignedWrap: + case SpvDecorationNoContraction: + case SpvDecorationNoUnsignedWrap: + case SpvDecorationUserSemantic: + break; + default: + // Give up. + continue; + } + + result.push_back(MakeUnique(&inst)); + } + for (auto& function : *context->module()) { for (auto& block : function) { for (auto& inst : block) { if (context->get_def_use_mgr()->NumUses(&inst) > 0) { continue; } + if (!remove_constants_and_undefs_ && + spvOpcodeIsConstantOrUndef(inst.opcode())) { + continue; + } if (spvOpcodeIsBlockTerminator(inst.opcode()) || inst.opcode() == SpvOpSelectionMerge || inst.opcode() == SpvOpLoopMerge) { - // In this reduction pass we do not want to affect static control - // flow. + // In this reduction pass we do not want to affect static + // control flow. continue; } - // Given that we're in a block, we should only get here if the - // instruction is not directly related to control flow; i.e., it's - // some straightforward instruction with an unused result, like an - // arithmetic operation or function call. + // Given that we're in a block, we should only get here if + // the instruction is not directly related to control flow; + // i.e., it's some straightforward instruction with an + // unused result, like an arithmetic operation or function + // call. result.push_back( MakeUnique(&inst)); } diff --git a/3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h b/3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h index 30f460bc1..bc4f137f1 100644 --- a/3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h +++ b/3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h @@ -28,7 +28,8 @@ namespace reduce { class RemoveUnreferencedInstructionReductionOpportunityFinder : public ReductionOpportunityFinder { public: - RemoveUnreferencedInstructionReductionOpportunityFinder() = default; + explicit RemoveUnreferencedInstructionReductionOpportunityFinder( + bool remove_constants_and_undefs); ~RemoveUnreferencedInstructionReductionOpportunityFinder() override = default; @@ -38,6 +39,7 @@ class RemoveUnreferencedInstructionReductionOpportunityFinder opt::IRContext* context) const final; private: + bool remove_constants_and_undefs_; }; } // namespace reduce diff --git a/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp b/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp index afc1298db..88ea38e7f 100644 --- a/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp +++ b/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp @@ -167,23 +167,6 @@ void StructuredLoopToSelectionReductionOpportunity::RedirectEdge( context_->cfg()->block(new_target_id)); } -void StructuredLoopToSelectionReductionOpportunity:: - AdaptPhiInstructionsForRemovedEdge(uint32_t from_id, BasicBlock* to_block) { - to_block->ForEachPhiInst([&from_id](Instruction* phi_inst) { - Instruction::OperandList new_in_operands; - // Go through the OpPhi's input operands in (variable, parent) pairs. - for (uint32_t index = 0; index < phi_inst->NumInOperands(); index += 2) { - // Keep all pairs where the parent is not the block from which the edge - // is being removed. - if (phi_inst->GetInOperand(index + 1).words[0] != from_id) { - new_in_operands.push_back(phi_inst->GetInOperand(index)); - new_in_operands.push_back(phi_inst->GetInOperand(index + 1)); - } - } - phi_inst->SetInOperands(std::move(new_in_operands)); - }); -} - void StructuredLoopToSelectionReductionOpportunity:: AdaptPhiInstructionsForAddedEdge(uint32_t from_id, BasicBlock* to_block) { to_block->ForEachPhiInst([this, &from_id](Instruction* phi_inst) { diff --git a/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h b/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h index f6c065b87..564811f10 100644 --- a/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h +++ b/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h @@ -62,11 +62,6 @@ class StructuredLoopToSelectionReductionOpportunity void RedirectEdge(uint32_t source_id, uint32_t original_target_id, uint32_t new_target_id); - // Removes any components of |to_block|'s phi instructions relating to - // |from_id|. - void AdaptPhiInstructionsForRemovedEdge(uint32_t from_id, - opt::BasicBlock* to_block); - // Adds components to |to_block|'s phi instructions to account for a new // incoming edge from |from_id|. void AdaptPhiInstructionsForAddedEdge(uint32_t from_id, diff --git a/3rdparty/spirv-tools/source/val/validate_cfg.cpp b/3rdparty/spirv-tools/source/val/validate_cfg.cpp index 8d8839e2f..4801fc58a 100644 --- a/3rdparty/spirv-tools/source/val/validate_cfg.cpp +++ b/3rdparty/spirv-tools/source/val/validate_cfg.cpp @@ -590,9 +590,68 @@ spv_result_t StructuredSwitchChecks(ValidationState_t& _, Function* function, return SPV_SUCCESS; } +// Validates that all CFG divergences (i.e. conditional branch or switch) are +// structured correctly. Either divergence is preceded by a merge instruction +// or the divergence introduces at most one unseen label. +spv_result_t ValidateStructuredSelections( + ValidationState_t& _, const std::vector& postorder) { + std::unordered_set seen; + for (auto iter = postorder.rbegin(); iter != postorder.rend(); ++iter) { + const auto* block = *iter; + const auto* terminator = block->terminator(); + if (!terminator) continue; + const auto index = terminator - &_.ordered_instructions()[0]; + auto* merge = &_.ordered_instructions()[index - 1]; + // Marks merges and continues as seen. + if (merge->opcode() == SpvOpSelectionMerge) { + seen.insert(merge->GetOperandAs(0)); + } else if (merge->opcode() == SpvOpLoopMerge) { + seen.insert(merge->GetOperandAs(0)); + seen.insert(merge->GetOperandAs(1)); + } else { + // Only track the pointer if it is a merge instruction. + merge = nullptr; + } + + // Skip unreachable blocks. + if (!block->reachable()) continue; + + if (terminator->opcode() == SpvOpBranchConditional) { + const auto true_label = terminator->GetOperandAs(1); + const auto false_label = terminator->GetOperandAs(2); + // Mark the upcoming blocks as seen now, but only error out if this block + // was missing a merge instruction and both labels hadn't been seen + // previously. + const bool both_unseen = + seen.insert(true_label).second && seen.insert(false_label).second; + if (!merge && both_unseen) { + return _.diag(SPV_ERROR_INVALID_CFG, terminator) + << "Selection must be structured"; + } + } else if (terminator->opcode() == SpvOpSwitch) { + uint32_t count = 0; + // Mark the targets as seen now, but only error out if this block was + // missing a merge instruction and there were multiple unseen labels. + for (uint32_t i = 1; i < terminator->operands().size(); i += 2) { + const auto target = terminator->GetOperandAs(i); + if (seen.insert(target).second) { + count++; + } + } + if (!merge && count > 1) { + return _.diag(SPV_ERROR_INVALID_CFG, terminator) + << "Selection must be structured"; + } + } + } + + return SPV_SUCCESS; +} + spv_result_t StructuredControlFlowChecks( ValidationState_t& _, Function* function, - const std::vector>& back_edges) { + const std::vector>& back_edges, + const std::vector& postorder) { /// Check all backedges target only loop headers and have exactly one /// back-edge branching to it @@ -709,6 +768,10 @@ spv_result_t StructuredControlFlowChecks( } } + if (auto error = ValidateStructuredSelections(_, postorder)) { + return error; + } + return SPV_SUCCESS; } @@ -931,7 +994,8 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) { /// Structured control flow checks are only required for shader capabilities if (_.HasCapability(SpvCapabilityShader)) { - if (auto error = StructuredControlFlowChecks(_, &function, back_edges)) + if (auto error = + StructuredControlFlowChecks(_, &function, back_edges, postorder)) return error; } } diff --git a/3rdparty/spirv-tools/source/val/validate_misc.cpp b/3rdparty/spirv-tools/source/val/validate_misc.cpp index 7542f32c5..883893d57 100644 --- a/3rdparty/spirv-tools/source/val/validate_misc.cpp +++ b/3rdparty/spirv-tools/source/val/validate_misc.cpp @@ -42,10 +42,13 @@ spv_result_t ValidateUndef(ValidationState_t& _, const Instruction* inst) { spv_result_t ValidateShaderClock(ValidationState_t& _, const Instruction* inst) { +// #2952: disabled until scope discussion is resolved. +#if 0 const uint32_t execution_scope = inst->word(3); if (auto error = ValidateExecutionScope(_, inst, execution_scope)) { return error; } +#endif // Result Type must be a 64 - bit unsigned integer type or // a vector of two - components of 32 - diff --git a/3rdparty/spirv-tools/test/fuzz/CMakeLists.txt b/3rdparty/spirv-tools/test/fuzz/CMakeLists.txt index 1d9a8fd6b..cd22426a6 100644 --- a/3rdparty/spirv-tools/test/fuzz/CMakeLists.txt +++ b/3rdparty/spirv-tools/test/fuzz/CMakeLists.txt @@ -24,15 +24,20 @@ if (${SPIRV_BUILD_FUZZER}) transformation_add_constant_scalar_test.cpp transformation_add_dead_break_test.cpp transformation_add_dead_continue_test.cpp + transformation_add_no_contraction_decoration_test.cpp transformation_add_type_boolean_test.cpp transformation_add_type_float_test.cpp transformation_add_type_int_test.cpp transformation_add_type_pointer_test.cpp + transformation_construct_composite_test.cpp transformation_copy_object_test.cpp transformation_move_block_down_test.cpp transformation_replace_boolean_constant_with_constant_binary_test.cpp transformation_replace_constant_with_uniform_test.cpp transformation_replace_id_with_synonym_test.cpp + transformation_set_function_control_test.cpp + transformation_set_loop_control_test.cpp + transformation_set_selection_control_test.cpp transformation_split_block_test.cpp uniform_buffer_element_descriptor_test.cpp) diff --git a/3rdparty/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp b/3rdparty/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp index 5fb71b0c5..fed6cfeef 100644 --- a/3rdparty/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp +++ b/3rdparty/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp @@ -21,6 +21,8 @@ namespace spvtools { namespace fuzz { namespace { +const uint32_t kNumFuzzerRuns = 20; + // Assembles the given |shader| text, and then runs the fuzzer |num_runs| // times, using successive seeds starting from |initial_seed|. Checks that // the binary produced after each fuzzer run is valid, and that replaying @@ -29,7 +31,7 @@ namespace { void RunFuzzerAndReplayer(const std::string& shader, const protobufs::FactSequence& initial_facts, uint32_t initial_seed, uint32_t num_runs) { - const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto env = SPV_ENV_UNIVERSAL_1_5; std::vector binary_in; SpirvTools t(env); @@ -240,9 +242,9 @@ TEST(FuzzerReplayerTest, Miscellaneous1) { OpFunctionEnd )"; - // Do 5 fuzzer runs, starting from an initial seed of 0 (seed value chosen + // Do some fuzzer runs, starting from an initial seed of 0 (seed value chosen // arbitrarily). - RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 0, 5); + RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 0, kNumFuzzerRuns); } TEST(FuzzerReplayerTest, Miscellaneous2) { @@ -303,7 +305,7 @@ TEST(FuzzerReplayerTest, Miscellaneous2) { OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" %16 %139 + OpEntryPoint Fragment %4 "main" %16 %139 %25 %68 OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 OpName %4 "main" @@ -485,9 +487,9 @@ TEST(FuzzerReplayerTest, Miscellaneous2) { OpFunctionEnd )"; - // Do 5 fuzzer runs, starting from an initial seed of 10 (seed value chosen + // Do some fuzzer runs, starting from an initial seed of 10 (seed value chosen // arbitrarily). - RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 10, 5); + RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 10, kNumFuzzerRuns); } TEST(FuzzerReplayerTest, Miscellaneous3) { @@ -603,7 +605,7 @@ TEST(FuzzerReplayerTest, Miscellaneous3) { OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" %68 %100 + OpEntryPoint Fragment %4 "main" %68 %100 %24 OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 OpName %4 "main" @@ -970,9 +972,9 @@ TEST(FuzzerReplayerTest, Miscellaneous3) { *facts.mutable_fact()->Add() = temp; } - // Do 5 fuzzer runs, starting from an initial seed of 94 (seed value chosen + // Do some fuzzer runs, starting from an initial seed of 94 (seed value chosen // arbitrarily). - RunFuzzerAndReplayer(shader, facts, 94, 5); + RunFuzzerAndReplayer(shader, facts, 94, kNumFuzzerRuns); } } // namespace diff --git a/3rdparty/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp b/3rdparty/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp index 84608f39f..648507031 100644 --- a/3rdparty/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp +++ b/3rdparty/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp @@ -154,7 +154,7 @@ void RunAndCheckShrinker( void RunFuzzerAndShrinker(const std::string& shader, const protobufs::FactSequence& initial_facts, uint32_t seed) { - const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto env = SPV_ENV_UNIVERSAL_1_5; std::vector binary_in; SpirvTools t(env); @@ -434,7 +434,7 @@ TEST(FuzzerShrinkerTest, Miscellaneous2) { OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" %16 %139 + OpEntryPoint Fragment %4 "main" %16 %139 %25 %68 OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 OpName %4 "main" @@ -732,7 +732,7 @@ TEST(FuzzerShrinkerTest, Miscellaneous3) { OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" %68 %100 + OpEntryPoint Fragment %4 "main" %68 %100 %24 OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 OpName %4 "main" diff --git a/3rdparty/spirv-tools/test/fuzz/transformation_add_dead_continue_test.cpp b/3rdparty/spirv-tools/test/fuzz/transformation_add_dead_continue_test.cpp index a15ea531c..8173e7269 100644 --- a/3rdparty/spirv-tools/test/fuzz/transformation_add_dead_continue_test.cpp +++ b/3rdparty/spirv-tools/test/fuzz/transformation_add_dead_continue_test.cpp @@ -1626,6 +1626,52 @@ TEST(TransformationAddDeadContinueTest, Miscellaneous5) { ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); } +TEST(TransformationAddDeadContinueTest, DISABLED_Miscellaneous6) { + // A miscellaneous test that exposing a known bug in spirv-fuzz. + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2919): re-enable + // this test as an when the known issue is fixed. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %9 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %13 %12 None + OpBranchConditional %9 %13 %11 + %11 = OpLabel + %20 = OpCopyObject %6 %9 + OpBranch %12 + %12 = OpLabel + OpBranchConditional %9 %10 %13 + %13 = OpLabel + %21 = OpCopyObject %6 %20 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + auto bad_transformation = TransformationAddDeadContinue(10, true, {}); + + ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager)); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/fuzz/transformation_add_no_contraction_decoration_test.cpp b/3rdparty/spirv-tools/test/fuzz/transformation_add_no_contraction_decoration_test.cpp new file mode 100644 index 000000000..b1a87ead3 --- /dev/null +++ b/3rdparty/spirv-tools/test/fuzz/transformation_add_no_contraction_decoration_test.cpp @@ -0,0 +1,194 @@ +// 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_add_no_contraction_decoration.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddNoContractionDecorationTest, BasicScenarios) { + // This is a simple transformation and this test handles the main cases. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + OpName %14 "i" + OpDecorate %32 NoContraction + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %11 = OpConstant %6 2 + %12 = OpTypeInt 32 1 + %13 = OpTypePointer Function %12 + %15 = OpConstant %12 0 + %22 = OpConstant %12 10 + %23 = OpTypeBool + %31 = OpConstant %6 3.5999999 + %38 = OpConstant %12 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %14 %15 + OpBranch %16 + %16 = OpLabel + OpLoopMerge %18 %19 None + OpBranch %20 + %20 = OpLabel + %21 = OpLoad %12 %14 + %24 = OpSLessThan %23 %21 %22 + OpBranchConditional %24 %17 %18 + %17 = OpLabel + %25 = OpLoad %6 %10 + %26 = OpLoad %6 %10 + %27 = OpFMul %6 %25 %26 + %28 = OpLoad %6 %8 + %29 = OpFAdd %6 %28 %27 + OpStore %8 %29 + %30 = OpLoad %6 %10 + %32 = OpFDiv %6 %30 %31 + OpStore %10 %32 + %33 = OpLoad %12 %14 + %34 = OpConvertSToF %6 %33 + %35 = OpLoad %6 %8 + %36 = OpFAdd %6 %35 %34 + OpStore %8 %36 + OpBranch %19 + %19 = OpLabel + %37 = OpLoad %12 %14 + %39 = OpIAdd %12 %37 %38 + OpStore %14 %39 + OpBranch %16 + %18 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + FactManager fact_manager; + + // Invalid: 200 is not an id + ASSERT_FALSE(TransformationAddNoContractionDecoration(200).IsApplicable( + context.get(), fact_manager)); + // Invalid: 17 is a block id + ASSERT_FALSE(TransformationAddNoContractionDecoration(17).IsApplicable( + context.get(), fact_manager)); + // Invalid: 24 is not arithmetic + ASSERT_FALSE(TransformationAddNoContractionDecoration(24).IsApplicable( + context.get(), fact_manager)); + + // It is valid to add NoContraction to each of these ids (and it's fine to + // have duplicates of the decoration, in the case of 32). + for (uint32_t result_id : {32u, 32u, 27u, 29u, 39u}) { + TransformationAddNoContractionDecoration transformation(result_id); + ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); + transformation.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + OpName %14 "i" + OpDecorate %32 NoContraction + OpDecorate %32 NoContraction + OpDecorate %32 NoContraction + OpDecorate %27 NoContraction + OpDecorate %29 NoContraction + OpDecorate %39 NoContraction + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %11 = OpConstant %6 2 + %12 = OpTypeInt 32 1 + %13 = OpTypePointer Function %12 + %15 = OpConstant %12 0 + %22 = OpConstant %12 10 + %23 = OpTypeBool + %31 = OpConstant %6 3.5999999 + %38 = OpConstant %12 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %14 %15 + OpBranch %16 + %16 = OpLabel + OpLoopMerge %18 %19 None + OpBranch %20 + %20 = OpLabel + %21 = OpLoad %12 %14 + %24 = OpSLessThan %23 %21 %22 + OpBranchConditional %24 %17 %18 + %17 = OpLabel + %25 = OpLoad %6 %10 + %26 = OpLoad %6 %10 + %27 = OpFMul %6 %25 %26 + %28 = OpLoad %6 %8 + %29 = OpFAdd %6 %28 %27 + OpStore %8 %29 + %30 = OpLoad %6 %10 + %32 = OpFDiv %6 %30 %31 + OpStore %10 %32 + %33 = OpLoad %12 %14 + %34 = OpConvertSToF %6 %33 + %35 = OpLoad %6 %8 + %36 = OpFAdd %6 %35 %34 + OpStore %8 %36 + OpBranch %19 + %19 = OpLabel + %37 = OpLoad %12 %14 + %39 = OpIAdd %12 %37 %38 + OpStore %14 %39 + OpBranch %16 + %18 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/fuzz/transformation_construct_composite_test.cpp b/3rdparty/spirv-tools/test/fuzz/transformation_construct_composite_test.cpp new file mode 100644 index 000000000..26988fbd1 --- /dev/null +++ b/3rdparty/spirv-tools/test/fuzz/transformation_construct_composite_test.cpp @@ -0,0 +1,1248 @@ +// 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_construct_composite.h" +#include "source/fuzz/data_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +bool SynonymFactHolds(const FactManager& fact_manager, uint32_t id, + uint32_t synonym_base_id, + std::vector&& synonym_indices) { + if (fact_manager.GetIdsForWhichSynonymsAreKnown().count(id) == 0) { + return false; + } + auto synonyms = fact_manager.GetSynonymsForId(id); + auto temp = MakeDataDescriptor(synonym_base_id, std::move(synonym_indices)); + return std::find_if(synonyms.begin(), synonyms.end(), + [&temp](protobufs::DataDescriptor dd) -> bool { + return DataDescriptorEquals()(&dd, &temp); + }) != synonyms.end(); +} + +TEST(TransformationConstructCompositeTest, ConstructArrays) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %11 "floats" + OpName %22 "x" + OpName %39 "vecs" + OpName %49 "bools" + OpName %60 "many_uvec3s" + OpDecorate %60 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 2 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 0 + %14 = OpConstant %6 1 + %15 = OpTypePointer Function %6 + %17 = OpConstant %12 1 + %18 = OpConstant %6 2 + %20 = OpTypeVector %6 2 + %21 = OpTypePointer Function %20 + %32 = OpTypeBool + %36 = OpConstant %7 3 + %37 = OpTypeArray %20 %36 + %38 = OpTypePointer Private %37 + %39 = OpVariable %38 Private + %40 = OpConstant %6 3 + %41 = OpConstantComposite %20 %40 %40 + %42 = OpTypePointer Private %20 + %44 = OpConstant %12 2 + %47 = OpTypeArray %32 %36 + %48 = OpTypePointer Function %47 + %50 = OpConstantTrue %32 + %51 = OpTypePointer Function %32 + %56 = OpTypeVector %7 3 + %57 = OpTypeArray %56 %8 + %58 = OpTypeArray %57 %8 + %59 = OpTypePointer Function %58 + %61 = OpConstant %7 4 + %62 = OpConstantComposite %56 %61 %61 %61 + %63 = OpTypePointer Function %56 + %65 = OpConstant %7 5 + %66 = OpConstantComposite %56 %65 %65 %65 + %67 = OpConstant %7 6 + %68 = OpConstantComposite %56 %67 %67 %67 + %69 = OpConstantComposite %57 %66 %68 + %100 = OpUndef %57 + %70 = OpTypePointer Function %57 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %22 = OpVariable %21 Function + %49 = OpVariable %48 Function + %60 = OpVariable %59 Function + %16 = OpAccessChain %15 %11 %13 + OpStore %16 %14 + %19 = OpAccessChain %15 %11 %17 + OpStore %19 %18 + %23 = OpAccessChain %15 %11 %13 + %24 = OpLoad %6 %23 + %25 = OpAccessChain %15 %11 %17 + %26 = OpLoad %6 %25 + %27 = OpCompositeConstruct %20 %24 %26 + OpStore %22 %27 + %28 = OpAccessChain %15 %11 %13 + %29 = OpLoad %6 %28 + %30 = OpAccessChain %15 %11 %17 + %31 = OpLoad %6 %30 + %33 = OpFOrdGreaterThan %32 %29 %31 + OpSelectionMerge %35 None + OpBranchConditional %33 %34 %35 + %34 = OpLabel + %43 = OpAccessChain %42 %39 %17 + OpStore %43 %41 + %45 = OpLoad %20 %22 + %46 = OpAccessChain %42 %39 %44 + OpStore %46 %45 + OpBranch %35 + %35 = OpLabel + %52 = OpAccessChain %51 %49 %13 + OpStore %52 %50 + %53 = OpAccessChain %51 %49 %13 + %54 = OpLoad %32 %53 + %55 = OpAccessChain %51 %49 %17 + OpStore %55 %54 + %64 = OpAccessChain %63 %60 %13 %13 + OpStore %64 %62 + %71 = OpAccessChain %70 %60 %17 + OpStore %71 %69 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + // Make a vec2[3] + TransformationConstructComposite make_vec2_array_length_3(37, {41, 45, 27}, + 46, 0, 200); + // Bad: there are too many components + TransformationConstructComposite make_vec2_array_length_3_bad( + 37, {41, 45, 27, 27}, 46, 0, 200); + ASSERT_TRUE( + make_vec2_array_length_3.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + make_vec2_array_length_3_bad.IsApplicable(context.get(), fact_manager)); + make_vec2_array_length_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 41, 200, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 45, 200, {1})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 27, 200, {2})); + + // Make a float[2] + TransformationConstructComposite make_float_array_length_2(9, {24, 40}, 71, 1, + 201); + // Bad: %41 does not have type float + TransformationConstructComposite make_float_array_length_2_bad(9, {41, 40}, + 71, 1, 201); + ASSERT_TRUE( + make_float_array_length_2.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + make_float_array_length_2_bad.IsApplicable(context.get(), fact_manager)); + make_float_array_length_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 24, 201, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 40, 201, {1})); + + // Make a bool[3] + TransformationConstructComposite make_bool_array_length_3(47, {33, 50, 50}, + 33, 1, 202); + // Bad: %54 is not available at the desired program point. + TransformationConstructComposite make_bool_array_length_3_bad( + 47, {33, 54, 50}, 33, 1, 202); + ASSERT_TRUE( + make_bool_array_length_3.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + make_bool_array_length_3_bad.IsApplicable(context.get(), fact_manager)); + make_bool_array_length_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 33, 202, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 50, 202, {1})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 50, 202, {2})); + + // make a uvec3[2][2] + TransformationConstructComposite make_uvec3_array_length_2_2(58, {69, 100}, + 64, 1, 203); + // Bad: Offset 100 is too large. + TransformationConstructComposite make_uvec3_array_length_2_2_bad( + 58, {33, 54}, 64, 100, 203); + ASSERT_TRUE( + make_uvec3_array_length_2_2.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_uvec3_array_length_2_2_bad.IsApplicable(context.get(), + fact_manager)); + make_uvec3_array_length_2_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 69, 203, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 100, 203, {1})); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %11 "floats" + OpName %22 "x" + OpName %39 "vecs" + OpName %49 "bools" + OpName %60 "many_uvec3s" + OpDecorate %60 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 2 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 0 + %14 = OpConstant %6 1 + %15 = OpTypePointer Function %6 + %17 = OpConstant %12 1 + %18 = OpConstant %6 2 + %20 = OpTypeVector %6 2 + %21 = OpTypePointer Function %20 + %32 = OpTypeBool + %36 = OpConstant %7 3 + %37 = OpTypeArray %20 %36 + %38 = OpTypePointer Private %37 + %39 = OpVariable %38 Private + %40 = OpConstant %6 3 + %41 = OpConstantComposite %20 %40 %40 + %42 = OpTypePointer Private %20 + %44 = OpConstant %12 2 + %47 = OpTypeArray %32 %36 + %48 = OpTypePointer Function %47 + %50 = OpConstantTrue %32 + %51 = OpTypePointer Function %32 + %56 = OpTypeVector %7 3 + %57 = OpTypeArray %56 %8 + %58 = OpTypeArray %57 %8 + %59 = OpTypePointer Function %58 + %61 = OpConstant %7 4 + %62 = OpConstantComposite %56 %61 %61 %61 + %63 = OpTypePointer Function %56 + %65 = OpConstant %7 5 + %66 = OpConstantComposite %56 %65 %65 %65 + %67 = OpConstant %7 6 + %68 = OpConstantComposite %56 %67 %67 %67 + %69 = OpConstantComposite %57 %66 %68 + %100 = OpUndef %57 + %70 = OpTypePointer Function %57 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %22 = OpVariable %21 Function + %49 = OpVariable %48 Function + %60 = OpVariable %59 Function + %16 = OpAccessChain %15 %11 %13 + OpStore %16 %14 + %19 = OpAccessChain %15 %11 %17 + OpStore %19 %18 + %23 = OpAccessChain %15 %11 %13 + %24 = OpLoad %6 %23 + %25 = OpAccessChain %15 %11 %17 + %26 = OpLoad %6 %25 + %27 = OpCompositeConstruct %20 %24 %26 + OpStore %22 %27 + %28 = OpAccessChain %15 %11 %13 + %29 = OpLoad %6 %28 + %30 = OpAccessChain %15 %11 %17 + %31 = OpLoad %6 %30 + %33 = OpFOrdGreaterThan %32 %29 %31 + %202 = OpCompositeConstruct %47 %33 %50 %50 + OpSelectionMerge %35 None + OpBranchConditional %33 %34 %35 + %34 = OpLabel + %43 = OpAccessChain %42 %39 %17 + OpStore %43 %41 + %45 = OpLoad %20 %22 + %200 = OpCompositeConstruct %37 %41 %45 %27 + %46 = OpAccessChain %42 %39 %44 + OpStore %46 %45 + OpBranch %35 + %35 = OpLabel + %52 = OpAccessChain %51 %49 %13 + OpStore %52 %50 + %53 = OpAccessChain %51 %49 %13 + %54 = OpLoad %32 %53 + %55 = OpAccessChain %51 %49 %17 + OpStore %55 %54 + %64 = OpAccessChain %63 %60 %13 %13 + %203 = OpCompositeConstruct %58 %69 %100 + OpStore %64 %62 + %71 = OpAccessChain %70 %60 %17 + %201 = OpCompositeConstruct %9 %24 %40 + OpStore %71 %69 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationConstructCompositeTest, ConstructMatrices) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "v1" + OpName %12 "v2" + OpName %14 "v3" + OpName %19 "v4" + OpName %26 "v5" + OpName %29 "v6" + OpName %34 "m34" + OpName %37 "m43" + OpName %43 "vecs" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 3 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstantComposite %7 %10 %10 %10 + %17 = OpTypeVector %6 4 + %18 = OpTypePointer Function %17 + %21 = OpConstant %6 2 + %32 = OpTypeMatrix %17 3 + %33 = OpTypePointer Private %32 + %34 = OpVariable %33 Private + %35 = OpTypeMatrix %7 4 + %36 = OpTypePointer Private %35 + %37 = OpVariable %36 Private + %38 = OpTypeVector %6 2 + %39 = OpTypeInt 32 0 + %40 = OpConstant %39 3 + %41 = OpTypeArray %38 %40 + %42 = OpTypePointer Private %41 + %43 = OpVariable %42 Private + %100 = OpUndef %7 + %101 = OpUndef %17 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + %12 = OpVariable %8 Function + %14 = OpVariable %8 Function + %19 = OpVariable %18 Function + %26 = OpVariable %18 Function + %29 = OpVariable %18 Function + OpStore %9 %11 + %13 = OpLoad %7 %9 + OpStore %12 %13 + %15 = OpLoad %7 %12 + %16 = OpVectorShuffle %7 %15 %15 2 1 0 + OpStore %14 %16 + %20 = OpLoad %7 %14 + %22 = OpCompositeExtract %6 %20 0 + %23 = OpCompositeExtract %6 %20 1 + %24 = OpCompositeExtract %6 %20 2 + %25 = OpCompositeConstruct %17 %22 %23 %24 %21 + OpStore %19 %25 + %27 = OpLoad %17 %19 + %28 = OpVectorShuffle %17 %27 %27 3 2 1 0 + OpStore %26 %28 + %30 = OpLoad %7 %9 + %31 = OpVectorShuffle %17 %30 %30 0 0 1 1 + OpStore %29 %31 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + // make a mat3x4 + TransformationConstructComposite make_mat34(32, {25, 28, 31}, 31, 2, 200); + // Bad: %35 is mat4x3, not mat3x4. + TransformationConstructComposite make_mat34_bad(35, {25, 28, 31}, 31, 2, 200); + ASSERT_TRUE(make_mat34.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_mat34_bad.IsApplicable(context.get(), fact_manager)); + make_mat34.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 25, 200, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 28, 200, {1})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 31, 200, {2})); + + // make a mat4x3 + TransformationConstructComposite make_mat43(35, {11, 13, 16, 100}, 31, 1, + 201); + // Bad: %25 does not match the matrix's column type. + TransformationConstructComposite make_mat43_bad(35, {25, 13, 16, 100}, 31, 1, + 201); + ASSERT_TRUE(make_mat43.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_mat43_bad.IsApplicable(context.get(), fact_manager)); + make_mat43.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 11, 201, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 13, 201, {1})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 16, 201, {2})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 100, 201, {3})); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "v1" + OpName %12 "v2" + OpName %14 "v3" + OpName %19 "v4" + OpName %26 "v5" + OpName %29 "v6" + OpName %34 "m34" + OpName %37 "m43" + OpName %43 "vecs" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 3 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstantComposite %7 %10 %10 %10 + %17 = OpTypeVector %6 4 + %18 = OpTypePointer Function %17 + %21 = OpConstant %6 2 + %32 = OpTypeMatrix %17 3 + %33 = OpTypePointer Private %32 + %34 = OpVariable %33 Private + %35 = OpTypeMatrix %7 4 + %36 = OpTypePointer Private %35 + %37 = OpVariable %36 Private + %38 = OpTypeVector %6 2 + %39 = OpTypeInt 32 0 + %40 = OpConstant %39 3 + %41 = OpTypeArray %38 %40 + %42 = OpTypePointer Private %41 + %43 = OpVariable %42 Private + %100 = OpUndef %7 + %101 = OpUndef %17 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + %12 = OpVariable %8 Function + %14 = OpVariable %8 Function + %19 = OpVariable %18 Function + %26 = OpVariable %18 Function + %29 = OpVariable %18 Function + OpStore %9 %11 + %13 = OpLoad %7 %9 + OpStore %12 %13 + %15 = OpLoad %7 %12 + %16 = OpVectorShuffle %7 %15 %15 2 1 0 + OpStore %14 %16 + %20 = OpLoad %7 %14 + %22 = OpCompositeExtract %6 %20 0 + %23 = OpCompositeExtract %6 %20 1 + %24 = OpCompositeExtract %6 %20 2 + %25 = OpCompositeConstruct %17 %22 %23 %24 %21 + OpStore %19 %25 + %27 = OpLoad %17 %19 + %28 = OpVectorShuffle %17 %27 %27 3 2 1 0 + OpStore %26 %28 + %30 = OpLoad %7 %9 + %31 = OpVectorShuffle %17 %30 %30 0 0 1 1 + %201 = OpCompositeConstruct %35 %11 %13 %16 %100 + OpStore %29 %31 + %200 = OpCompositeConstruct %32 %25 %28 %31 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationConstructCompositeTest, ConstructStructs) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "Inner" + OpMemberName %9 0 "a" + OpMemberName %9 1 "b" + OpName %11 "i1" + OpName %22 "i2" + OpName %33 "Outer" + OpMemberName %33 0 "c" + OpMemberName %33 1 "d" + OpMemberName %33 2 "e" + OpName %35 "o" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypeInt 32 1 + %9 = OpTypeStruct %7 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %8 0 + %13 = OpConstant %6 2 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 0 + %16 = OpTypePointer Function %6 + %18 = OpConstant %8 1 + %19 = OpConstant %8 3 + %20 = OpTypePointer Function %8 + %23 = OpTypePointer Function %7 + %31 = OpConstant %14 2 + %32 = OpTypeArray %9 %31 + %33 = OpTypeStruct %32 %9 %6 + %34 = OpTypePointer Function %33 + %36 = OpConstant %6 1 + %37 = OpConstantComposite %7 %36 %13 + %38 = OpConstant %8 2 + %39 = OpConstantComposite %9 %37 %38 + %40 = OpConstant %6 3 + %41 = OpConstant %6 4 + %42 = OpConstantComposite %7 %40 %41 + %56 = OpConstant %6 5 + %100 = OpUndef %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %22 = OpVariable %10 Function + %35 = OpVariable %34 Function + %17 = OpAccessChain %16 %11 %12 %15 + OpStore %17 %13 + %21 = OpAccessChain %20 %11 %18 + OpStore %21 %19 + %24 = OpAccessChain %23 %11 %12 + %25 = OpLoad %7 %24 + %26 = OpAccessChain %23 %22 %12 + OpStore %26 %25 + %27 = OpAccessChain %20 %11 %18 + %28 = OpLoad %8 %27 + %29 = OpIAdd %8 %28 %18 + %30 = OpAccessChain %20 %22 %18 + OpStore %30 %29 + %43 = OpAccessChain %20 %11 %18 + %44 = OpLoad %8 %43 + %45 = OpCompositeConstruct %9 %42 %44 + %46 = OpCompositeConstruct %32 %39 %45 + %47 = OpLoad %9 %22 + %48 = OpCompositeConstruct %33 %46 %47 %40 + OpStore %35 %48 + %49 = OpLoad %9 %11 + %50 = OpAccessChain %10 %35 %12 %12 + OpStore %50 %49 + %51 = OpLoad %9 %22 + %52 = OpAccessChain %10 %35 %12 %18 + OpStore %52 %51 + %53 = OpAccessChain %10 %35 %12 %12 + %54 = OpLoad %9 %53 + %55 = OpAccessChain %10 %35 %18 + OpStore %55 %54 + %57 = OpAccessChain %16 %35 %38 + OpStore %57 %56 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + // make an Inner + TransformationConstructComposite make_inner(9, {25, 19}, 57, 0, 200); + // Bad: Too few fields to make the struct. + TransformationConstructComposite make_inner_bad(9, {25}, 57, 0, 200); + ASSERT_TRUE(make_inner.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_inner_bad.IsApplicable(context.get(), fact_manager)); + make_inner.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 25, 200, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 19, 200, {1})); + + // make an Outer + TransformationConstructComposite make_outer(33, {46, 200, 56}, 200, 1, 201); + // Bad: %200 is not available at the desired program point. + TransformationConstructComposite make_outer_bad(33, {46, 200, 56}, 200, 0, + 201); + ASSERT_TRUE(make_outer.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_outer_bad.IsApplicable(context.get(), fact_manager)); + make_outer.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 46, 201, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 200, 201, {1})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 56, 201, {2})); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "Inner" + OpMemberName %9 0 "a" + OpMemberName %9 1 "b" + OpName %11 "i1" + OpName %22 "i2" + OpName %33 "Outer" + OpMemberName %33 0 "c" + OpMemberName %33 1 "d" + OpMemberName %33 2 "e" + OpName %35 "o" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypeInt 32 1 + %9 = OpTypeStruct %7 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %8 0 + %13 = OpConstant %6 2 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 0 + %16 = OpTypePointer Function %6 + %18 = OpConstant %8 1 + %19 = OpConstant %8 3 + %20 = OpTypePointer Function %8 + %23 = OpTypePointer Function %7 + %31 = OpConstant %14 2 + %32 = OpTypeArray %9 %31 + %33 = OpTypeStruct %32 %9 %6 + %34 = OpTypePointer Function %33 + %36 = OpConstant %6 1 + %37 = OpConstantComposite %7 %36 %13 + %38 = OpConstant %8 2 + %39 = OpConstantComposite %9 %37 %38 + %40 = OpConstant %6 3 + %41 = OpConstant %6 4 + %42 = OpConstantComposite %7 %40 %41 + %56 = OpConstant %6 5 + %100 = OpUndef %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %22 = OpVariable %10 Function + %35 = OpVariable %34 Function + %17 = OpAccessChain %16 %11 %12 %15 + OpStore %17 %13 + %21 = OpAccessChain %20 %11 %18 + OpStore %21 %19 + %24 = OpAccessChain %23 %11 %12 + %25 = OpLoad %7 %24 + %26 = OpAccessChain %23 %22 %12 + OpStore %26 %25 + %27 = OpAccessChain %20 %11 %18 + %28 = OpLoad %8 %27 + %29 = OpIAdd %8 %28 %18 + %30 = OpAccessChain %20 %22 %18 + OpStore %30 %29 + %43 = OpAccessChain %20 %11 %18 + %44 = OpLoad %8 %43 + %45 = OpCompositeConstruct %9 %42 %44 + %46 = OpCompositeConstruct %32 %39 %45 + %47 = OpLoad %9 %22 + %48 = OpCompositeConstruct %33 %46 %47 %40 + OpStore %35 %48 + %49 = OpLoad %9 %11 + %50 = OpAccessChain %10 %35 %12 %12 + OpStore %50 %49 + %51 = OpLoad %9 %22 + %52 = OpAccessChain %10 %35 %12 %18 + OpStore %52 %51 + %53 = OpAccessChain %10 %35 %12 %12 + %54 = OpLoad %9 %53 + %55 = OpAccessChain %10 %35 %18 + OpStore %55 %54 + %200 = OpCompositeConstruct %9 %25 %19 + %201 = OpCompositeConstruct %33 %46 %200 %56 + %57 = OpAccessChain %16 %35 %38 + OpStore %57 %56 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationConstructCompositeTest, ConstructVectors) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "v2" + OpName %27 "v3" + OpName %46 "v4" + OpName %53 "iv2" + OpName %61 "uv3" + OpName %72 "bv4" + OpName %88 "uv2" + OpName %95 "bv3" + OpName %104 "bv2" + OpName %116 "iv3" + OpName %124 "iv4" + OpName %133 "uv4" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpConstantComposite %7 %10 %11 + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 0 + %15 = OpTypePointer Function %6 + %18 = OpConstant %13 1 + %21 = OpTypeBool + %25 = OpTypeVector %6 3 + %26 = OpTypePointer Function %25 + %33 = OpConstant %6 3 + %34 = OpConstant %6 -0.756802499 + %38 = OpConstant %13 2 + %44 = OpTypeVector %6 4 + %45 = OpTypePointer Function %44 + %50 = OpTypeInt 32 1 + %51 = OpTypeVector %50 2 + %52 = OpTypePointer Function %51 + %57 = OpTypePointer Function %50 + %59 = OpTypeVector %13 3 + %60 = OpTypePointer Function %59 + %65 = OpConstant %13 3 + %67 = OpTypePointer Function %13 + %70 = OpTypeVector %21 4 + %71 = OpTypePointer Function %70 + %73 = OpConstantTrue %21 + %74 = OpTypePointer Function %21 + %86 = OpTypeVector %13 2 + %87 = OpTypePointer Function %86 + %93 = OpTypeVector %21 3 + %94 = OpTypePointer Function %93 + %102 = OpTypeVector %21 2 + %103 = OpTypePointer Function %102 + %111 = OpConstantFalse %21 + %114 = OpTypeVector %50 3 + %115 = OpTypePointer Function %114 + %117 = OpConstant %50 3 + %122 = OpTypeVector %50 4 + %123 = OpTypePointer Function %122 + %131 = OpTypeVector %13 4 + %132 = OpTypePointer Function %131 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + %27 = OpVariable %26 Function + %46 = OpVariable %45 Function + %53 = OpVariable %52 Function + %61 = OpVariable %60 Function + %72 = OpVariable %71 Function + %88 = OpVariable %87 Function + %95 = OpVariable %94 Function + %104 = OpVariable %103 Function + %116 = OpVariable %115 Function + %124 = OpVariable %123 Function + %133 = OpVariable %132 Function + OpStore %9 %12 + %16 = OpAccessChain %15 %9 %14 + %17 = OpLoad %6 %16 + %19 = OpAccessChain %15 %9 %18 + %20 = OpLoad %6 %19 + %22 = OpFOrdGreaterThan %21 %17 %20 + OpSelectionMerge %24 None + OpBranchConditional %22 %23 %101 + %23 = OpLabel + %28 = OpAccessChain %15 %9 %14 + %29 = OpLoad %6 %28 + %30 = OpAccessChain %15 %9 %18 + %31 = OpLoad %6 %30 + %32 = OpFAdd %6 %29 %31 + %35 = OpCompositeConstruct %25 %32 %33 %34 + OpStore %27 %35 + %36 = OpAccessChain %15 %27 %14 + %37 = OpLoad %6 %36 + %39 = OpAccessChain %15 %27 %38 + %40 = OpLoad %6 %39 + %41 = OpFOrdLessThan %21 %37 %40 + OpSelectionMerge %43 None + OpBranchConditional %41 %42 %69 + %42 = OpLabel + %47 = OpAccessChain %15 %9 %18 + %48 = OpLoad %6 %47 + %49 = OpAccessChain %15 %46 %14 + OpStore %49 %48 + %54 = OpAccessChain %15 %27 %38 + %55 = OpLoad %6 %54 + %56 = OpConvertFToS %50 %55 + %58 = OpAccessChain %57 %53 %14 + OpStore %58 %56 + %62 = OpAccessChain %15 %46 %14 + %63 = OpLoad %6 %62 + %64 = OpConvertFToU %13 %63 + %66 = OpIAdd %13 %64 %65 + %68 = OpAccessChain %67 %61 %14 + OpStore %68 %66 + OpBranch %43 + %69 = OpLabel + %75 = OpAccessChain %74 %72 %14 + OpStore %75 %73 + %76 = OpAccessChain %74 %72 %14 + %77 = OpLoad %21 %76 + %78 = OpLogicalNot %21 %77 + %79 = OpAccessChain %74 %72 %18 + OpStore %79 %78 + %80 = OpAccessChain %74 %72 %14 + %81 = OpLoad %21 %80 + %82 = OpAccessChain %74 %72 %18 + %83 = OpLoad %21 %82 + %84 = OpLogicalAnd %21 %81 %83 + %85 = OpAccessChain %74 %72 %38 + OpStore %85 %84 + %89 = OpAccessChain %67 %88 %14 + %90 = OpLoad %13 %89 + %91 = OpINotEqual %21 %90 %14 + %92 = OpAccessChain %74 %72 %65 + OpStore %92 %91 + OpBranch %43 + %43 = OpLabel + %96 = OpLoad %70 %72 + %97 = OpCompositeExtract %21 %96 0 + %98 = OpCompositeExtract %21 %96 1 + %99 = OpCompositeExtract %21 %96 2 + %100 = OpCompositeConstruct %93 %97 %98 %99 + OpStore %95 %100 + OpBranch %24 + %101 = OpLabel + %105 = OpAccessChain %67 %88 %14 + %106 = OpLoad %13 %105 + %107 = OpINotEqual %21 %106 %14 + %108 = OpCompositeConstruct %102 %107 %107 + OpStore %104 %108 + OpBranch %24 + %24 = OpLabel + %109 = OpAccessChain %74 %104 %18 + %110 = OpLoad %21 %109 + %112 = OpLogicalOr %21 %110 %111 + %113 = OpAccessChain %74 %104 %14 + OpStore %113 %112 + %118 = OpAccessChain %57 %116 %14 + OpStore %118 %117 + %119 = OpAccessChain %57 %116 %14 + %120 = OpLoad %50 %119 + %121 = OpAccessChain %57 %53 %18 + OpStore %121 %120 + %125 = OpAccessChain %57 %116 %14 + %126 = OpLoad %50 %125 + %127 = OpAccessChain %57 %53 %18 + %128 = OpLoad %50 %127 + %129 = OpIAdd %50 %126 %128 + %130 = OpAccessChain %57 %124 %65 + OpStore %130 %129 + %134 = OpAccessChain %57 %116 %14 + %135 = OpLoad %50 %134 + %136 = OpBitcast %13 %135 + %137 = OpAccessChain %67 %133 %14 + OpStore %137 %136 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + TransformationConstructComposite make_vec2(7, {17, 11}, 100, 1, 200); + // Bad: not enough data for a vec2 + TransformationConstructComposite make_vec2_bad(7, {11}, 100, 1, 200); + ASSERT_TRUE(make_vec2.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_vec2_bad.IsApplicable(context.get(), fact_manager)); + make_vec2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 17, 200, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 11, 200, {1})); + + TransformationConstructComposite make_vec3(25, {12, 32}, 35, 0, 201); + // Bad: too much data for a vec3 + TransformationConstructComposite make_vec3_bad(25, {12, 32, 32}, 35, 0, 201); + ASSERT_TRUE(make_vec3.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_vec3_bad.IsApplicable(context.get(), fact_manager)); + make_vec3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 12, 201, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 32, 201, {2})); + + TransformationConstructComposite make_vec4(44, {32, 32, 10, 11}, 75, 0, 202); + // Bad: id 48 is not available at the insertion points + TransformationConstructComposite make_vec4_bad(44, {48, 32, 10, 11}, 75, 0, + 202); + ASSERT_TRUE(make_vec4.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_vec4_bad.IsApplicable(context.get(), fact_manager)); + make_vec4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 32, 202, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 32, 202, {1})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 10, 202, {2})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 11, 202, {3})); + + TransformationConstructComposite make_ivec2(51, {126, 120}, 128, 0, 203); + // Bad: if 128 is not available at the instruction that defines 128 + TransformationConstructComposite make_ivec2_bad(51, {128, 120}, 128, 0, 203); + ASSERT_TRUE(make_ivec2.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_ivec2_bad.IsApplicable(context.get(), fact_manager)); + make_ivec2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 126, 203, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 120, 203, {1})); + + TransformationConstructComposite make_ivec3(114, {56, 117, 56}, 66, 1, 204); + // Bad because 1300 is not an id + TransformationConstructComposite make_ivec3_bad(114, {56, 117, 1300}, 66, 1, + 204); + ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_ivec3_bad.IsApplicable(context.get(), fact_manager)); + make_ivec3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 56, 204, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 204, {1})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 56, 204, {2})); + + TransformationConstructComposite make_ivec4(122, {56, 117, 117, 117}, 66, 0, + 205); + // Bad because 86 is the wrong type. + TransformationConstructComposite make_ivec4_bad(86, {56, 117, 117, 117}, 66, + 0, 205); + ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_ivec4_bad.IsApplicable(context.get(), fact_manager)); + make_ivec4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 56, 205, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 205, {1})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 205, {2})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 205, {3})); + + TransformationConstructComposite make_uvec2(86, {18, 38}, 133, 2, 206); + TransformationConstructComposite make_uvec2_bad(86, {18, 38}, 133, 200, 206); + ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_uvec2_bad.IsApplicable(context.get(), fact_manager)); + make_uvec2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 18, 206, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 38, 206, {1})); + + TransformationConstructComposite make_uvec3(59, {14, 18, 136}, 137, 2, 207); + // Bad because 1300 is not an id + TransformationConstructComposite make_uvec3_bad(59, {14, 18, 1300}, 137, 2, + 207); + ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_uvec3_bad.IsApplicable(context.get(), fact_manager)); + make_uvec3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 14, 207, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 18, 207, {1})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 207, {2})); + + TransformationConstructComposite make_uvec4(131, {14, 18, 136, 136}, 137, 0, + 208); + // Bad because 86 is the wrong type. + TransformationConstructComposite make_uvec4_bad(86, {14, 18, 136, 136}, 137, + 0, 208); + ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_uvec4_bad.IsApplicable(context.get(), fact_manager)); + make_uvec4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 14, 208, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 18, 208, {1})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 208, {2})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 208, {3})); + + TransformationConstructComposite make_bvec2(102, + { + 111, + 41, + }, + 75, 0, 209); + // Bad because 0 is not a valid base instruction id + TransformationConstructComposite make_bvec2_bad(102, + { + 111, + 41, + }, + 0, 0, 209); + ASSERT_TRUE(make_bvec2.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_bvec2_bad.IsApplicable(context.get(), fact_manager)); + make_bvec2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 111, 209, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 41, 209, {1})); + + TransformationConstructComposite make_bvec3(93, {108, 73}, 108, 1, 210); + // Bad because there are too many components for a bvec3 + TransformationConstructComposite make_bvec3_bad(93, {108, 108}, 108, 1, 210); + ASSERT_TRUE(make_bvec3.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_bvec3_bad.IsApplicable(context.get(), fact_manager)); + make_bvec3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 108, 210, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 73, 210, {2})); + + TransformationConstructComposite make_bvec4(70, {108, 108}, 108, 3, 211); + // Bad because 21 is a type, not a result id + TransformationConstructComposite make_bvec4_bad(70, {21, 108}, 108, 3, 211); + ASSERT_TRUE(make_bvec4.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(make_bvec4_bad.IsApplicable(context.get(), fact_manager)); + make_bvec4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 108, 211, {0})); + ASSERT_TRUE(SynonymFactHolds(fact_manager, 108, 211, {2})); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "v2" + OpName %27 "v3" + OpName %46 "v4" + OpName %53 "iv2" + OpName %61 "uv3" + OpName %72 "bv4" + OpName %88 "uv2" + OpName %95 "bv3" + OpName %104 "bv2" + OpName %116 "iv3" + OpName %124 "iv4" + OpName %133 "uv4" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpConstantComposite %7 %10 %11 + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 0 + %15 = OpTypePointer Function %6 + %18 = OpConstant %13 1 + %21 = OpTypeBool + %25 = OpTypeVector %6 3 + %26 = OpTypePointer Function %25 + %33 = OpConstant %6 3 + %34 = OpConstant %6 -0.756802499 + %38 = OpConstant %13 2 + %44 = OpTypeVector %6 4 + %45 = OpTypePointer Function %44 + %50 = OpTypeInt 32 1 + %51 = OpTypeVector %50 2 + %52 = OpTypePointer Function %51 + %57 = OpTypePointer Function %50 + %59 = OpTypeVector %13 3 + %60 = OpTypePointer Function %59 + %65 = OpConstant %13 3 + %67 = OpTypePointer Function %13 + %70 = OpTypeVector %21 4 + %71 = OpTypePointer Function %70 + %73 = OpConstantTrue %21 + %74 = OpTypePointer Function %21 + %86 = OpTypeVector %13 2 + %87 = OpTypePointer Function %86 + %93 = OpTypeVector %21 3 + %94 = OpTypePointer Function %93 + %102 = OpTypeVector %21 2 + %103 = OpTypePointer Function %102 + %111 = OpConstantFalse %21 + %114 = OpTypeVector %50 3 + %115 = OpTypePointer Function %114 + %117 = OpConstant %50 3 + %122 = OpTypeVector %50 4 + %123 = OpTypePointer Function %122 + %131 = OpTypeVector %13 4 + %132 = OpTypePointer Function %131 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + %27 = OpVariable %26 Function + %46 = OpVariable %45 Function + %53 = OpVariable %52 Function + %61 = OpVariable %60 Function + %72 = OpVariable %71 Function + %88 = OpVariable %87 Function + %95 = OpVariable %94 Function + %104 = OpVariable %103 Function + %116 = OpVariable %115 Function + %124 = OpVariable %123 Function + %133 = OpVariable %132 Function + OpStore %9 %12 + %206 = OpCompositeConstruct %86 %18 %38 + %16 = OpAccessChain %15 %9 %14 + %17 = OpLoad %6 %16 + %19 = OpAccessChain %15 %9 %18 + %20 = OpLoad %6 %19 + %22 = OpFOrdGreaterThan %21 %17 %20 + OpSelectionMerge %24 None + OpBranchConditional %22 %23 %101 + %23 = OpLabel + %28 = OpAccessChain %15 %9 %14 + %29 = OpLoad %6 %28 + %30 = OpAccessChain %15 %9 %18 + %31 = OpLoad %6 %30 + %32 = OpFAdd %6 %29 %31 + %201 = OpCompositeConstruct %25 %12 %32 + %35 = OpCompositeConstruct %25 %32 %33 %34 + OpStore %27 %35 + %36 = OpAccessChain %15 %27 %14 + %37 = OpLoad %6 %36 + %39 = OpAccessChain %15 %27 %38 + %40 = OpLoad %6 %39 + %41 = OpFOrdLessThan %21 %37 %40 + OpSelectionMerge %43 None + OpBranchConditional %41 %42 %69 + %42 = OpLabel + %47 = OpAccessChain %15 %9 %18 + %48 = OpLoad %6 %47 + %49 = OpAccessChain %15 %46 %14 + OpStore %49 %48 + %54 = OpAccessChain %15 %27 %38 + %55 = OpLoad %6 %54 + %56 = OpConvertFToS %50 %55 + %58 = OpAccessChain %57 %53 %14 + OpStore %58 %56 + %62 = OpAccessChain %15 %46 %14 + %63 = OpLoad %6 %62 + %64 = OpConvertFToU %13 %63 + %205 = OpCompositeConstruct %122 %56 %117 %117 %117 + %66 = OpIAdd %13 %64 %65 + %204 = OpCompositeConstruct %114 %56 %117 %56 + %68 = OpAccessChain %67 %61 %14 + OpStore %68 %66 + OpBranch %43 + %69 = OpLabel + %202 = OpCompositeConstruct %44 %32 %32 %10 %11 + %209 = OpCompositeConstruct %102 %111 %41 + %75 = OpAccessChain %74 %72 %14 + OpStore %75 %73 + %76 = OpAccessChain %74 %72 %14 + %77 = OpLoad %21 %76 + %78 = OpLogicalNot %21 %77 + %79 = OpAccessChain %74 %72 %18 + OpStore %79 %78 + %80 = OpAccessChain %74 %72 %14 + %81 = OpLoad %21 %80 + %82 = OpAccessChain %74 %72 %18 + %83 = OpLoad %21 %82 + %84 = OpLogicalAnd %21 %81 %83 + %85 = OpAccessChain %74 %72 %38 + OpStore %85 %84 + %89 = OpAccessChain %67 %88 %14 + %90 = OpLoad %13 %89 + %91 = OpINotEqual %21 %90 %14 + %92 = OpAccessChain %74 %72 %65 + OpStore %92 %91 + OpBranch %43 + %43 = OpLabel + %96 = OpLoad %70 %72 + %97 = OpCompositeExtract %21 %96 0 + %98 = OpCompositeExtract %21 %96 1 + %99 = OpCompositeExtract %21 %96 2 + %100 = OpCompositeConstruct %93 %97 %98 %99 + %200 = OpCompositeConstruct %7 %17 %11 + OpStore %95 %100 + OpBranch %24 + %101 = OpLabel + %105 = OpAccessChain %67 %88 %14 + %106 = OpLoad %13 %105 + %107 = OpINotEqual %21 %106 %14 + %108 = OpCompositeConstruct %102 %107 %107 + %210 = OpCompositeConstruct %93 %108 %73 + OpStore %104 %108 + %211 = OpCompositeConstruct %70 %108 %108 + OpBranch %24 + %24 = OpLabel + %109 = OpAccessChain %74 %104 %18 + %110 = OpLoad %21 %109 + %112 = OpLogicalOr %21 %110 %111 + %113 = OpAccessChain %74 %104 %14 + OpStore %113 %112 + %118 = OpAccessChain %57 %116 %14 + OpStore %118 %117 + %119 = OpAccessChain %57 %116 %14 + %120 = OpLoad %50 %119 + %121 = OpAccessChain %57 %53 %18 + OpStore %121 %120 + %125 = OpAccessChain %57 %116 %14 + %126 = OpLoad %50 %125 + %127 = OpAccessChain %57 %53 %18 + %203 = OpCompositeConstruct %51 %126 %120 + %128 = OpLoad %50 %127 + %129 = OpIAdd %50 %126 %128 + %130 = OpAccessChain %57 %124 %65 + OpStore %130 %129 + %134 = OpAccessChain %57 %116 %14 + %135 = OpLoad %50 %134 + %136 = OpBitcast %13 %135 + %208 = OpCompositeConstruct %131 %14 %18 %136 %136 + %137 = OpAccessChain %67 %133 %14 + OpStore %137 %136 + %207 = OpCompositeConstruct %59 %14 %18 %136 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp b/3rdparty/spirv-tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp index 704494b50..bfc7fa77c 100644 --- a/3rdparty/spirv-tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp +++ b/3rdparty/spirv-tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp @@ -16,6 +16,7 @@ #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/instruction_descriptor.h" #include "test/fuzz/fuzz_test_util.h" namespace spvtools { @@ -164,12 +165,14 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, FactManager fact_manager; std::vector uses_of_true = { - transformation::MakeIdUseDescriptor(41, SpvOpStore, 1, 44, 12), - transformation::MakeIdUseDescriptor(41, SpvOpLogicalOr, 0, 46, 0)}; + MakeIdUseDescriptor(41, MakeInstructionDescriptor(44, SpvOpStore, 12), 1), + MakeIdUseDescriptor(41, MakeInstructionDescriptor(46, SpvOpLogicalOr, 0), + 0)}; std::vector uses_of_false = { - transformation::MakeIdUseDescriptor(43, SpvOpStore, 1, 44, 13), - transformation::MakeIdUseDescriptor(43, SpvOpLogicalAnd, 1, 48, 0)}; + MakeIdUseDescriptor(43, MakeInstructionDescriptor(44, SpvOpStore, 13), 1), + MakeIdUseDescriptor(43, MakeInstructionDescriptor(48, SpvOpLogicalAnd, 0), + 1)}; const uint32_t fresh_id = 100; @@ -529,10 +532,10 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, FactManager fact_manager; - auto use_of_true_in_if = - transformation::MakeIdUseDescriptor(13, SpvOpBranchConditional, 0, 10, 0); - auto use_of_false_in_while = - transformation::MakeIdUseDescriptor(21, SpvOpBranchConditional, 0, 16, 0); + auto use_of_true_in_if = MakeIdUseDescriptor( + 13, MakeInstructionDescriptor(10, SpvOpBranchConditional, 0), 0); + auto use_of_false_in_while = MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(16, SpvOpBranchConditional, 0), 0); auto replacement_1 = TransformationReplaceBooleanConstantWithConstantBinary( use_of_true_in_if, 9, 11, SpvOpSLessThan, 100); @@ -641,8 +644,8 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, OpPhi) { FactManager fact_manager; auto replacement = TransformationReplaceBooleanConstantWithConstantBinary( - transformation::MakeIdUseDescriptor(9, SpvOpPhi, 0, 23, 0), 13, 15, - SpvOpSLessThan, 100); + MakeIdUseDescriptor(9, MakeInstructionDescriptor(23, SpvOpPhi, 0), 0), 13, + 15, SpvOpSLessThan, 100); ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager)); } diff --git a/3rdparty/spirv-tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp b/3rdparty/spirv-tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp index 06ee02585..ac2e3f9a6 100644 --- a/3rdparty/spirv-tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp +++ b/3rdparty/spirv-tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp @@ -13,6 +13,7 @@ // limitations under the License. #include "source/fuzz/transformation_replace_constant_with_uniform.h" +#include "source/fuzz/instruction_descriptor.h" #include "source/fuzz/uniform_buffer_element_descriptor.h" #include "test/fuzz/fuzz_test_util.h" @@ -116,11 +117,11 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) { // The constant ids are 9, 11 and 14, for 1, 2 and 3 respectively. protobufs::IdUseDescriptor use_of_9_in_store = - transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0); + MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1); protobufs::IdUseDescriptor use_of_11_in_add = - transformation::MakeIdUseDescriptor(11, SpvOpIAdd, 1, 12, 0); + MakeIdUseDescriptor(11, MakeInstructionDescriptor(12, SpvOpIAdd, 0), 1); protobufs::IdUseDescriptor use_of_14_in_add = - transformation::MakeIdUseDescriptor(14, SpvOpIAdd, 0, 15, 0); + MakeIdUseDescriptor(14, MakeInstructionDescriptor(15, SpvOpIAdd, 0), 0); // These transformations work: they match the facts. auto transformation_use_of_9_in_store = @@ -167,7 +168,7 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) { // The following transformation does not apply because the id descriptor is // not sensible. protobufs::IdUseDescriptor nonsense_id_use_descriptor = - transformation::MakeIdUseDescriptor(9, SpvOpIAdd, 0, 15, 0); + MakeIdUseDescriptor(9, MakeInstructionDescriptor(15, SpvOpIAdd, 0), 0); ASSERT_FALSE(TransformationReplaceConstantWithUniform( nonsense_id_use_descriptor, blockname_a, 101, 102) .IsApplicable(context.get(), fact_manager)); @@ -477,13 +478,13 @@ TEST(TransformationReplaceConstantWithUniformTest, NestedStruct) { // The constant ids are 13, 15, 17 and 20, for 1, 2, 3 and 4 respectively. protobufs::IdUseDescriptor use_of_13_in_store = - transformation::MakeIdUseDescriptor(13, SpvOpStore, 1, 21, 0); + MakeIdUseDescriptor(13, MakeInstructionDescriptor(21, SpvOpStore, 0), 1); protobufs::IdUseDescriptor use_of_15_in_add = - transformation::MakeIdUseDescriptor(15, SpvOpIAdd, 1, 16, 0); + MakeIdUseDescriptor(15, MakeInstructionDescriptor(16, SpvOpIAdd, 0), 1); protobufs::IdUseDescriptor use_of_17_in_add = - transformation::MakeIdUseDescriptor(17, SpvOpIAdd, 0, 19, 0); + MakeIdUseDescriptor(17, MakeInstructionDescriptor(19, SpvOpIAdd, 0), 0); protobufs::IdUseDescriptor use_of_20_in_store = - transformation::MakeIdUseDescriptor(20, SpvOpStore, 1, 19, 1); + MakeIdUseDescriptor(20, MakeInstructionDescriptor(19, SpvOpStore, 1), 1); // These transformations work: they match the facts. auto transformation_use_of_13_in_store = @@ -703,7 +704,7 @@ TEST(TransformationReplaceConstantWithUniformTest, NoUniformIntPointerPresent) { // The constant id is 9 for 0. protobufs::IdUseDescriptor use_of_9_in_store = - transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0); + MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1); // This transformation is not available because no uniform pointer to integer // type is present: @@ -778,7 +779,7 @@ TEST(TransformationReplaceConstantWithUniformTest, NoConstantPresentForIndex) { // The constant id is 9 for 9. protobufs::IdUseDescriptor use_of_9_in_store = - transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0); + MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1); // This transformation is not available because no constant is present for the // index 1 required to index into the uniform buffer: @@ -852,7 +853,7 @@ TEST(TransformationReplaceConstantWithUniformTest, // The constant id is 9 for 3.0. protobufs::IdUseDescriptor use_of_9_in_store = - transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0); + MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1); // This transformation is not available because no integer type is present to // allow a constant index to be expressed: @@ -937,9 +938,9 @@ TEST(TransformationReplaceConstantWithUniformTest, // The constant ids for 9 and 10 are 9 and 11 respectively protobufs::IdUseDescriptor use_of_9_in_store = - transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 10, 0); + MakeIdUseDescriptor(9, MakeInstructionDescriptor(10, SpvOpStore, 0), 1); protobufs::IdUseDescriptor use_of_11_in_store = - transformation::MakeIdUseDescriptor(11, SpvOpStore, 1, 10, 1); + MakeIdUseDescriptor(11, MakeInstructionDescriptor(10, SpvOpStore, 1), 1); // These are right: ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_9_in_store, @@ -1220,58 +1221,58 @@ TEST(TransformationReplaceConstantWithUniformTest, ComplexReplacements) { std::vector transformations; transformations.emplace_back(TransformationReplaceConstantWithUniform( - transformation::MakeIdUseDescriptor(18, SpvOpStore, 1, 20, 0), + MakeIdUseDescriptor(18, MakeInstructionDescriptor(20, SpvOpStore, 0), 1), uniform_f_a_4, 200, 201)); transformations.emplace_back(TransformationReplaceConstantWithUniform( - transformation::MakeIdUseDescriptor(22, SpvOpStore, 1, 23, 0), + MakeIdUseDescriptor(22, MakeInstructionDescriptor(23, SpvOpStore, 0), 1), uniform_f_a_3, 202, 203)); transformations.emplace_back(TransformationReplaceConstantWithUniform( - transformation::MakeIdUseDescriptor(25, SpvOpStore, 1, 26, 0), + MakeIdUseDescriptor(25, MakeInstructionDescriptor(26, SpvOpStore, 0), 1), uniform_f_a_2, 204, 205)); transformations.emplace_back(TransformationReplaceConstantWithUniform( - transformation::MakeIdUseDescriptor(28, SpvOpStore, 1, 29, 0), + MakeIdUseDescriptor(28, MakeInstructionDescriptor(29, SpvOpStore, 0), 1), uniform_f_a_1, 206, 207)); transformations.emplace_back(TransformationReplaceConstantWithUniform( - transformation::MakeIdUseDescriptor(31, SpvOpStore, 1, 32, 0), + MakeIdUseDescriptor(31, MakeInstructionDescriptor(32, SpvOpStore, 0), 1), uniform_f_a_0, 208, 209)); transformations.emplace_back(TransformationReplaceConstantWithUniform( - transformation::MakeIdUseDescriptor(30, SpvOpStore, 1, 35, 0), + MakeIdUseDescriptor(30, MakeInstructionDescriptor(35, SpvOpStore, 0), 1), uniform_f_b_w, 210, 211)); transformations.emplace_back(TransformationReplaceConstantWithUniform( - transformation::MakeIdUseDescriptor(27, SpvOpStore, 1, 37, 0), + MakeIdUseDescriptor(27, MakeInstructionDescriptor(37, SpvOpStore, 0), 1), uniform_f_b_z, 212, 213)); transformations.emplace_back(TransformationReplaceConstantWithUniform( - transformation::MakeIdUseDescriptor(24, SpvOpStore, 1, 39, 0), + MakeIdUseDescriptor(24, MakeInstructionDescriptor(39, SpvOpStore, 0), 1), uniform_f_b_y, 214, 215)); transformations.emplace_back(TransformationReplaceConstantWithUniform( - transformation::MakeIdUseDescriptor(21, SpvOpStore, 1, 41, 0), + MakeIdUseDescriptor(21, MakeInstructionDescriptor(41, SpvOpStore, 0), 1), uniform_f_b_x, 216, 217)); transformations.emplace_back(TransformationReplaceConstantWithUniform( - transformation::MakeIdUseDescriptor(44, SpvOpStore, 1, 45, 0), + MakeIdUseDescriptor(44, MakeInstructionDescriptor(45, SpvOpStore, 0), 1), uniform_f_c_z, 220, 221)); transformations.emplace_back(TransformationReplaceConstantWithUniform( - transformation::MakeIdUseDescriptor(46, SpvOpStore, 1, 47, 0), + MakeIdUseDescriptor(46, MakeInstructionDescriptor(47, SpvOpStore, 0), 1), uniform_f_c_y, 222, 223)); transformations.emplace_back(TransformationReplaceConstantWithUniform( - transformation::MakeIdUseDescriptor(48, SpvOpStore, 1, 49, 0), + MakeIdUseDescriptor(48, MakeInstructionDescriptor(49, SpvOpStore, 0), 1), uniform_f_c_x, 224, 225)); transformations.emplace_back(TransformationReplaceConstantWithUniform( - transformation::MakeIdUseDescriptor(50, SpvOpStore, 1, 52, 0), + MakeIdUseDescriptor(50, MakeInstructionDescriptor(52, SpvOpStore, 0), 1), uniform_f_d, 226, 227)); transformations.emplace_back(TransformationReplaceConstantWithUniform( - transformation::MakeIdUseDescriptor(53, SpvOpStore, 1, 54, 0), + MakeIdUseDescriptor(53, MakeInstructionDescriptor(54, SpvOpStore, 0), 1), uniform_h_x, 228, 229)); transformations.emplace_back(TransformationReplaceConstantWithUniform( - transformation::MakeIdUseDescriptor(55, SpvOpStore, 1, 56, 0), + MakeIdUseDescriptor(55, MakeInstructionDescriptor(56, SpvOpStore, 0), 1), uniform_h_y, 230, 231)); transformations.emplace_back(TransformationReplaceConstantWithUniform( - transformation::MakeIdUseDescriptor(42, SpvOpStore, 1, 43, 0), uniform_g, - 218, 219)); + MakeIdUseDescriptor(42, MakeInstructionDescriptor(43, SpvOpStore, 0), 1), + uniform_g, 218, 219)); for (auto& transformation : transformations) { ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager)); diff --git a/3rdparty/spirv-tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/3rdparty/spirv-tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp index 479eb160b..2e8a6140f 100644 --- a/3rdparty/spirv-tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp +++ b/3rdparty/spirv-tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp @@ -15,6 +15,7 @@ #include "source/fuzz/transformation_replace_id_with_synonym.h" #include "source/fuzz/data_descriptor.h" #include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/instruction_descriptor.h" #include "test/fuzz/fuzz_test_util.h" namespace spvtools { @@ -185,10 +186,14 @@ const std::string kComplexShader = R"( OpFunctionEnd )"; -protobufs::Fact MakeFact(uint32_t id, uint32_t copy_id) { +protobufs::Fact MakeSynonymFact(uint32_t id, uint32_t synonym_object, + std::vector indices = {}) { protobufs::FactIdSynonym id_synonym_fact; id_synonym_fact.set_id(id); - id_synonym_fact.mutable_data_descriptor()->set_object(copy_id); + id_synonym_fact.mutable_data_descriptor()->set_object(synonym_object); + for (auto index : indices) { + id_synonym_fact.mutable_data_descriptor()->add_index(index); + } protobufs::Fact result; *result.mutable_id_synonym_fact() = id_synonym_fact; return result; @@ -196,17 +201,17 @@ protobufs::Fact MakeFact(uint32_t id, uint32_t copy_id) { // Equips the fact manager with synonym facts for the above shader. void SetUpIdSynonyms(FactManager* fact_manager, opt::IRContext* context) { - fact_manager->AddFact(MakeFact(15, 200), context); - fact_manager->AddFact(MakeFact(15, 201), context); - fact_manager->AddFact(MakeFact(15, 202), context); - fact_manager->AddFact(MakeFact(55, 203), context); - fact_manager->AddFact(MakeFact(54, 204), context); - fact_manager->AddFact(MakeFact(74, 205), context); - fact_manager->AddFact(MakeFact(78, 206), context); - fact_manager->AddFact(MakeFact(84, 207), context); - fact_manager->AddFact(MakeFact(33, 208), context); - fact_manager->AddFact(MakeFact(12, 209), context); - fact_manager->AddFact(MakeFact(19, 210), context); + fact_manager->AddFact(MakeSynonymFact(15, 200), context); + fact_manager->AddFact(MakeSynonymFact(15, 201), context); + fact_manager->AddFact(MakeSynonymFact(15, 202), context); + fact_manager->AddFact(MakeSynonymFact(55, 203), context); + fact_manager->AddFact(MakeSynonymFact(54, 204), context); + fact_manager->AddFact(MakeSynonymFact(74, 205), context); + fact_manager->AddFact(MakeSynonymFact(78, 206), context); + fact_manager->AddFact(MakeSynonymFact(84, 207), context); + fact_manager->AddFact(MakeSynonymFact(33, 208), context); + fact_manager->AddFact(MakeSynonymFact(12, 209), context); + fact_manager->AddFact(MakeSynonymFact(19, 210), context); } TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) { @@ -222,7 +227,7 @@ TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) { // %202 cannot replace %15 as in-operand 0 of %300, since %202 does not // dominate %300. auto synonym_does_not_dominate_use = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(15, SpvOpIAdd, 0, 300, 0), + MakeIdUseDescriptor(15, MakeInstructionDescriptor(300, SpvOpIAdd, 0), 0), MakeDataDescriptor(202, {}), 0); ASSERT_FALSE( synonym_does_not_dominate_use.IsApplicable(context.get(), fact_manager)); @@ -231,28 +236,31 @@ TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) { // incoming value for block %72, and %202 does not dominate %72. auto synonym_does_not_dominate_use_op_phi = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(15, SpvOpPhi, 2, 301, 0), + MakeIdUseDescriptor(15, MakeInstructionDescriptor(301, SpvOpPhi, 0), + 2), MakeDataDescriptor(202, {}), 0); ASSERT_FALSE(synonym_does_not_dominate_use_op_phi.IsApplicable(context.get(), fact_manager)); // %200 is not a synonym for %84 auto id_in_use_is_not_synonymous = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(84, SpvOpSGreaterThan, 0, 67, 0), + MakeIdUseDescriptor( + 84, MakeInstructionDescriptor(67, SpvOpSGreaterThan, 0), 0), MakeDataDescriptor(200, {}), 0); ASSERT_FALSE( id_in_use_is_not_synonymous.IsApplicable(context.get(), fact_manager)); // %86 is not a synonym for anything (and in particular not for %74) auto id_has_no_synonyms = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(86, SpvOpPhi, 2, 84, 0), + MakeIdUseDescriptor(86, MakeInstructionDescriptor(84, SpvOpPhi, 0), 2), MakeDataDescriptor(74, {}), 0); ASSERT_FALSE(id_has_no_synonyms.IsApplicable(context.get(), fact_manager)); // This would lead to %207 = 'OpCopyObject %type %207' if it were allowed auto synonym_use_is_in_synonym_definition = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(84, SpvOpCopyObject, 0, 207, 0), + MakeIdUseDescriptor( + 84, MakeInstructionDescriptor(207, SpvOpCopyObject, 0), 0), MakeDataDescriptor(207, {}), 0); ASSERT_FALSE(synonym_use_is_in_synonym_definition.IsApplicable(context.get(), fact_manager)); @@ -260,14 +268,16 @@ TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) { // The id use descriptor does not lead to a use (%84 is not used in the // definition of %207) auto bad_id_use_descriptor = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(84, SpvOpCopyObject, 0, 200, 0), + MakeIdUseDescriptor( + 84, MakeInstructionDescriptor(200, SpvOpCopyObject, 0), 0), MakeDataDescriptor(207, {}), 0); ASSERT_FALSE(bad_id_use_descriptor.IsApplicable(context.get(), fact_manager)); // This replacement would lead to an access chain into a struct using a // non-constant index. auto bad_access_chain = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(12, SpvOpAccessChain, 1, 14, 0), + MakeIdUseDescriptor( + 12, MakeInstructionDescriptor(14, SpvOpAccessChain, 0), 1), MakeDataDescriptor(209, {}), 0); ASSERT_FALSE(bad_access_chain.IsApplicable(context.get(), fact_manager)); } @@ -283,7 +293,7 @@ TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) { SetUpIdSynonyms(&fact_manager, context.get()); auto global_constant_synonym = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(19, SpvOpStore, 1, 47, 0), + MakeIdUseDescriptor(19, MakeInstructionDescriptor(47, SpvOpStore, 0), 1), MakeDataDescriptor(210, {}), 0); ASSERT_TRUE( global_constant_synonym.IsApplicable(context.get(), fact_manager)); @@ -291,7 +301,8 @@ TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) { ASSERT_TRUE(IsValid(env, context.get())); auto replace_vector_access_chain_index = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(54, SpvOpAccessChain, 1, 55, 0), + MakeIdUseDescriptor( + 54, MakeInstructionDescriptor(55, SpvOpAccessChain, 0), 1), MakeDataDescriptor(204, {}), 0); ASSERT_TRUE(replace_vector_access_chain_index.IsApplicable(context.get(), fact_manager)); @@ -301,21 +312,22 @@ TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) { // This is an interesting case because it replaces something that is being // copied with something that is already a synonym. auto regular_replacement = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(15, SpvOpCopyObject, 0, 202, 0), + MakeIdUseDescriptor( + 15, MakeInstructionDescriptor(202, SpvOpCopyObject, 0), 0), MakeDataDescriptor(201, {}), 0); ASSERT_TRUE(regular_replacement.IsApplicable(context.get(), fact_manager)); regular_replacement.Apply(context.get(), &fact_manager); ASSERT_TRUE(IsValid(env, context.get())); auto regular_replacement2 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(55, SpvOpStore, 0, 203, 0), + MakeIdUseDescriptor(55, MakeInstructionDescriptor(203, SpvOpStore, 0), 0), MakeDataDescriptor(203, {}), 0); ASSERT_TRUE(regular_replacement2.IsApplicable(context.get(), fact_manager)); regular_replacement2.Apply(context.get(), &fact_manager); ASSERT_TRUE(IsValid(env, context.get())); auto good_op_phi = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(74, SpvOpPhi, 2, 86, 0), + MakeIdUseDescriptor(74, MakeInstructionDescriptor(86, SpvOpPhi, 0), 2), MakeDataDescriptor(205, {}), 0); ASSERT_TRUE(good_op_phi.IsApplicable(context.get(), fact_manager)); good_op_phi.Apply(context.get(), &fact_manager); @@ -493,13 +505,13 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) { FactManager fact_manager; - fact_manager.AddFact(MakeFact(10, 100), context.get()); - fact_manager.AddFact(MakeFact(8, 101), context.get()); + fact_manager.AddFact(MakeSynonymFact(10, 100), context.get()); + fact_manager.AddFact(MakeSynonymFact(8, 101), context.get()); // Replace %10 with %100 in: // %11 = OpLoad %6 %10 auto replacement1 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(10, SpvOpLoad, 0, 11, 0), + MakeIdUseDescriptor(10, MakeInstructionDescriptor(11, SpvOpLoad, 0), 0), MakeDataDescriptor(100, {}), 0); ASSERT_TRUE(replacement1.IsApplicable(context.get(), fact_manager)); replacement1.Apply(context.get(), &fact_manager); @@ -508,7 +520,7 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) { // Replace %8 with %101 in: // OpStore %8 %11 auto replacement2 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(8, SpvOpStore, 0, 11, 0), + MakeIdUseDescriptor(8, MakeInstructionDescriptor(11, SpvOpStore, 0), 0), MakeDataDescriptor(101, {}), 0); ASSERT_TRUE(replacement2.IsApplicable(context.get(), fact_manager)); replacement2.Apply(context.get(), &fact_manager); @@ -517,7 +529,7 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) { // Replace %8 with %101 in: // %12 = OpLoad %6 %8 auto replacement3 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(8, SpvOpLoad, 0, 12, 0), + MakeIdUseDescriptor(8, MakeInstructionDescriptor(12, SpvOpLoad, 0), 0), MakeDataDescriptor(101, {}), 0); ASSERT_TRUE(replacement3.IsApplicable(context.get(), fact_manager)); replacement3.Apply(context.get(), &fact_manager); @@ -526,7 +538,7 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) { // Replace %10 with %100 in: // OpStore %10 %12 auto replacement4 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(10, SpvOpStore, 0, 12, 0), + MakeIdUseDescriptor(10, MakeInstructionDescriptor(12, SpvOpStore, 0), 0), MakeDataDescriptor(100, {}), 0); ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager)); replacement4.Apply(context.get(), &fact_manager); @@ -622,12 +634,13 @@ TEST(TransformationReplaceIdWithSynonymTest, FactManager fact_manager; - fact_manager.AddFact(MakeFact(14, 100), context.get()); + fact_manager.AddFact(MakeSynonymFact(14, 100), context.get()); // Replace %14 with %100 in: // %16 = OpFunctionCall %2 %10 %14 auto replacement = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(14, SpvOpFunctionCall, 1, 16, 0), + MakeIdUseDescriptor( + 14, MakeInstructionDescriptor(16, SpvOpFunctionCall, 0), 1), MakeDataDescriptor(100, {}), 0); ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager)); } @@ -785,19 +798,19 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Add synonym facts corresponding to the OpCopyObject operations that have // been applied to all constants in the module. - fact_manager.AddFact(MakeFact(16, 100), context.get()); - fact_manager.AddFact(MakeFact(21, 101), context.get()); - fact_manager.AddFact(MakeFact(17, 102), context.get()); - fact_manager.AddFact(MakeFact(57, 103), context.get()); - fact_manager.AddFact(MakeFact(18, 104), context.get()); - fact_manager.AddFact(MakeFact(40, 105), context.get()); - fact_manager.AddFact(MakeFact(32, 106), context.get()); - fact_manager.AddFact(MakeFact(43, 107), context.get()); - fact_manager.AddFact(MakeFact(55, 108), context.get()); - fact_manager.AddFact(MakeFact(8, 109), context.get()); - fact_manager.AddFact(MakeFact(47, 110), context.get()); - fact_manager.AddFact(MakeFact(28, 111), context.get()); - fact_manager.AddFact(MakeFact(45, 112), context.get()); + fact_manager.AddFact(MakeSynonymFact(16, 100), context.get()); + fact_manager.AddFact(MakeSynonymFact(21, 101), context.get()); + fact_manager.AddFact(MakeSynonymFact(17, 102), context.get()); + fact_manager.AddFact(MakeSynonymFact(57, 103), context.get()); + fact_manager.AddFact(MakeSynonymFact(18, 104), context.get()); + fact_manager.AddFact(MakeSynonymFact(40, 105), context.get()); + fact_manager.AddFact(MakeSynonymFact(32, 106), context.get()); + fact_manager.AddFact(MakeSynonymFact(43, 107), context.get()); + fact_manager.AddFact(MakeSynonymFact(55, 108), context.get()); + fact_manager.AddFact(MakeSynonymFact(8, 109), context.get()); + fact_manager.AddFact(MakeSynonymFact(47, 110), context.get()); + fact_manager.AddFact(MakeSynonymFact(28, 111), context.get()); + fact_manager.AddFact(MakeSynonymFact(45, 112), context.get()); // Replacements of the form %16 -> %100 @@ -805,7 +818,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to d.*a*[2] // The index %16 used for a cannot be replaced auto replacement1 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 1, 20, 0), + MakeIdUseDescriptor( + 16, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 1), MakeDataDescriptor(100, {}), 0); ASSERT_FALSE(replacement1.IsApplicable(context.get(), fact_manager)); @@ -813,7 +827,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to h.*f* // The index %16 used for f cannot be replaced auto replacement2 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 1, 39, 0), + MakeIdUseDescriptor( + 16, MakeInstructionDescriptor(39, SpvOpAccessChain, 0), 1), MakeDataDescriptor(100, {}), 0); ASSERT_FALSE(replacement2.IsApplicable(context.get(), fact_manager)); @@ -821,7 +836,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to h.g.*a*[1] // The index %16 used for a cannot be replaced auto replacement3 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 2, 41, 0), + MakeIdUseDescriptor( + 16, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 2), MakeDataDescriptor(100, {}), 0); ASSERT_FALSE(replacement3.IsApplicable(context.get(), fact_manager)); @@ -829,7 +845,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to i[*0*].f // The index %16 used for 0 *can* be replaced auto replacement4 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 1, 52, 0), + MakeIdUseDescriptor( + 16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 1), MakeDataDescriptor(100, {}), 0); ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager)); replacement4.Apply(context.get(), &fact_manager); @@ -839,7 +856,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to i[0].*f* // The index %16 used for f cannot be replaced auto replacement5 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 2, 52, 0), + MakeIdUseDescriptor( + 16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 2), MakeDataDescriptor(100, {}), 0); ASSERT_FALSE(replacement5.IsApplicable(context.get(), fact_manager)); @@ -847,7 +865,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to i[1].g.*a*[0] // The index %16 used for a cannot be replaced auto replacement6 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 3, 53, 0), + MakeIdUseDescriptor( + 16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 3), MakeDataDescriptor(100, {}), 0); ASSERT_FALSE(replacement6.IsApplicable(context.get(), fact_manager)); @@ -855,7 +874,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to i[1].g.a[*0*] // The index %16 used for 0 *can* be replaced auto replacement7 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 4, 53, 0), + MakeIdUseDescriptor( + 16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 4), MakeDataDescriptor(100, {}), 0); ASSERT_TRUE(replacement7.IsApplicable(context.get(), fact_manager)); replacement7.Apply(context.get(), &fact_manager); @@ -867,7 +887,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to d.*b*[3] // The index %24 used for b cannot be replaced auto replacement8 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 24, 0), + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 1), MakeDataDescriptor(101, {}), 0); ASSERT_FALSE(replacement8.IsApplicable(context.get(), fact_manager)); @@ -875,7 +896,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to h.*g*.a[1] // The index %24 used for g cannot be replaced auto replacement9 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 41, 0), + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 1), MakeDataDescriptor(101, {}), 0); ASSERT_FALSE(replacement9.IsApplicable(context.get(), fact_manager)); @@ -883,7 +905,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to h.g.a[*1*] // The index %24 used for 1 *can* be replaced auto replacement10 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 3, 41, 0), + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 3), MakeDataDescriptor(101, {}), 0); ASSERT_TRUE(replacement10.IsApplicable(context.get(), fact_manager)); replacement10.Apply(context.get(), &fact_manager); @@ -893,7 +916,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to h.*g*.b[0] // The index %24 used for g cannot be replaced auto replacement11 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 44, 0), + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 1), MakeDataDescriptor(101, {}), 0); ASSERT_FALSE(replacement11.IsApplicable(context.get(), fact_manager)); @@ -901,7 +925,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to h.g.*b*[0] // The index %24 used for b cannot be replaced auto replacement12 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 44, 0), + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 2), MakeDataDescriptor(101, {}), 0); ASSERT_FALSE(replacement12.IsApplicable(context.get(), fact_manager)); @@ -909,7 +934,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to h.*g*.c // The index %24 used for g cannot be replaced auto replacement13 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 46, 0), + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 1), MakeDataDescriptor(101, {}), 0); ASSERT_FALSE(replacement13.IsApplicable(context.get(), fact_manager)); @@ -917,7 +943,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to i[*1*].g.a[0] // The index %24 used for 1 *can* be replaced auto replacement14 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 53, 0), + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 1), MakeDataDescriptor(101, {}), 0); ASSERT_TRUE(replacement14.IsApplicable(context.get(), fact_manager)); replacement14.Apply(context.get(), &fact_manager); @@ -927,7 +954,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to i[1].*g*.a[0] // The index %24 used for g cannot be replaced auto replacement15 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 53, 0), + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 2), MakeDataDescriptor(101, {}), 0); ASSERT_FALSE(replacement15.IsApplicable(context.get(), fact_manager)); @@ -935,7 +963,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to i[2].*g*.b[1] // The index %24 used for g cannot be replaced auto replacement16 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 56, 0), + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 2), MakeDataDescriptor(101, {}), 0); ASSERT_FALSE(replacement16.IsApplicable(context.get(), fact_manager)); @@ -943,7 +972,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to i[2].g.*b*[1] // The index %24 used for b cannot be replaced auto replacement17 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 3, 56, 0), + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 3), MakeDataDescriptor(101, {}), 0); ASSERT_FALSE(replacement17.IsApplicable(context.get(), fact_manager)); @@ -951,7 +981,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to i[3].*g*.c // The index %24 used for g cannot be replaced auto replacement18 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 58, 0), + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 2), MakeDataDescriptor(101, {}), 0); ASSERT_FALSE(replacement18.IsApplicable(context.get(), fact_manager)); @@ -961,7 +992,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to d.a[*2*] // The index %17 used for 2 *can* be replaced auto replacement19 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 2, 20, 0), + MakeIdUseDescriptor( + 17, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 2), MakeDataDescriptor(102, {}), 0); ASSERT_TRUE(replacement19.IsApplicable(context.get(), fact_manager)); replacement19.Apply(context.get(), &fact_manager); @@ -971,7 +1003,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to d.c // The index %17 used for c cannot be replaced auto replacement20 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 1, 27, 0), + MakeIdUseDescriptor( + 17, MakeInstructionDescriptor(27, SpvOpAccessChain, 0), 1), MakeDataDescriptor(102, {}), 0); ASSERT_FALSE(replacement20.IsApplicable(context.get(), fact_manager)); @@ -979,7 +1012,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to h.g.*c* // The index %17 used for c cannot be replaced auto replacement21 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 2, 46, 0), + MakeIdUseDescriptor( + 17, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 2), MakeDataDescriptor(102, {}), 0); ASSERT_FALSE(replacement21.IsApplicable(context.get(), fact_manager)); @@ -987,7 +1021,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to i[*2*].g.b[1] // The index %17 used for 2 *can* be replaced auto replacement22 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 1, 56, 0), + MakeIdUseDescriptor( + 17, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 1), MakeDataDescriptor(102, {}), 0); ASSERT_TRUE(replacement22.IsApplicable(context.get(), fact_manager)); replacement22.Apply(context.get(), &fact_manager); @@ -997,7 +1032,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to i[3].g.*c* // The index %17 used for c cannot be replaced auto replacement23 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 3, 58, 0), + MakeIdUseDescriptor( + 17, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 3), MakeDataDescriptor(102, {}), 0); ASSERT_FALSE(replacement23.IsApplicable(context.get(), fact_manager)); @@ -1007,7 +1043,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to i[*3*].g.c // The index %57 used for 3 *can* be replaced auto replacement24 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(57, SpvOpAccessChain, 1, 58, 0), + MakeIdUseDescriptor( + 57, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 1), MakeDataDescriptor(103, {}), 0); ASSERT_TRUE(replacement24.IsApplicable(context.get(), fact_manager)); replacement24.Apply(context.get(), &fact_manager); @@ -1019,7 +1056,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to e[*17*] // The index %32 used for 17 *can* be replaced auto replacement25 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(32, SpvOpAccessChain, 1, 34, 0), + MakeIdUseDescriptor( + 32, MakeInstructionDescriptor(34, SpvOpAccessChain, 0), 1), MakeDataDescriptor(106, {}), 0); ASSERT_TRUE(replacement25.IsApplicable(context.get(), fact_manager)); replacement25.Apply(context.get(), &fact_manager); @@ -1031,7 +1069,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to h.g.b[*0*] // The index %43 used for 0 *can* be replaced auto replacement26 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(43, SpvOpAccessChain, 3, 44, 0), + MakeIdUseDescriptor( + 43, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 3), MakeDataDescriptor(107, {}), 0); ASSERT_TRUE(replacement26.IsApplicable(context.get(), fact_manager)); replacement26.Apply(context.get(), &fact_manager); @@ -1043,7 +1082,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to i[2].g.b[*1*] // The index %55 used for 1 *can* be replaced auto replacement27 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(55, SpvOpAccessChain, 4, 56, 0), + MakeIdUseDescriptor( + 55, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 4), MakeDataDescriptor(108, {}), 0); ASSERT_TRUE(replacement27.IsApplicable(context.get(), fact_manager)); replacement27.Apply(context.get(), &fact_manager); @@ -1055,7 +1095,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { // Corresponds to d.b[*3*] // The index %8 used for 3 *can* be replaced auto replacement28 = TransformationReplaceIdWithSynonym( - transformation::MakeIdUseDescriptor(8, SpvOpAccessChain, 2, 24, 0), + MakeIdUseDescriptor(8, MakeInstructionDescriptor(24, SpvOpAccessChain, 0), + 2), MakeDataDescriptor(109, {}), 0); ASSERT_TRUE(replacement28.IsApplicable(context.get(), fact_manager)); replacement28.Apply(context.get(), &fact_manager); @@ -1171,6 +1212,983 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } +TEST(TransformationReplaceIdWithSynonymTest, ArrayCompositeSynonyms) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %11 "A" + OpName %20 "B" + OpName %31 "g" + OpName %35 "h" + OpDecorate %11 RelaxedPrecision + OpDecorate %22 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 3 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 0 + %13 = OpConstant %6 3 + %14 = OpTypePointer Function %6 + %16 = OpTypeFloat 32 + %17 = OpConstant %7 4 + %18 = OpTypeArray %16 %17 + %19 = OpTypePointer Function %18 + %24 = OpTypePointer Function %16 + %28 = OpConstant %16 42 + %30 = OpConstant %6 2 + %34 = OpConstant %6 1 + %38 = OpConstant %6 42 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %20 = OpVariable %19 Function + %31 = OpVariable %24 Function + %35 = OpVariable %14 Function + %15 = OpAccessChain %14 %11 %12 + %21 = OpAccessChain %14 %11 %12 + %22 = OpLoad %6 %21 + %100 = OpCompositeConstruct %9 %12 %13 %22 + OpStore %15 %13 + %23 = OpConvertSToF %16 %22 + %25 = OpAccessChain %24 %20 %12 + OpStore %25 %23 + %26 = OpAccessChain %14 %11 %12 + %27 = OpLoad %6 %26 + %29 = OpAccessChain %24 %20 %27 + OpStore %29 %28 + %32 = OpLoad %16 %31 + %101 = OpCompositeConstruct %18 %28 %23 %32 %23 + %50 = OpCopyObject %16 %23 + %51 = OpCopyObject %16 %23 + %33 = OpAccessChain %24 %20 %30 + OpStore %33 %28 + OpStore %33 %32 + %36 = OpLoad %6 %35 + %37 = OpAccessChain %14 %11 %34 + OpStore %37 %36 + %39 = OpAccessChain %14 %11 %12 + %40 = OpLoad %6 %39 + %41 = OpIAdd %6 %38 %40 + %42 = OpAccessChain %14 %11 %30 + OpStore %42 %41 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + fact_manager.AddFact(MakeSynonymFact(12, 100, {0}), context.get()); + fact_manager.AddFact(MakeSynonymFact(13, 100, {1}), context.get()); + fact_manager.AddFact(MakeSynonymFact(22, 100, {2}), context.get()); + fact_manager.AddFact(MakeSynonymFact(28, 101, {0}), context.get()); + fact_manager.AddFact(MakeSynonymFact(23, 101, {1}), context.get()); + fact_manager.AddFact(MakeSynonymFact(32, 101, {2}), context.get()); + fact_manager.AddFact(MakeSynonymFact(23, 101, {3}), context.get()); + + // Replace %12 with %100[0] in '%25 = OpAccessChain %24 %20 %12' + auto good_replacement_1 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 12, MakeInstructionDescriptor(25, SpvOpAccessChain, 0), 1), + MakeDataDescriptor(100, {0}), 102); + // Bad: id already in use + auto bad_replacement_1 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 12, MakeInstructionDescriptor(25, SpvOpAccessChain, 0), 1), + MakeDataDescriptor(100, {0}), 25); + ASSERT_TRUE(good_replacement_1.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(bad_replacement_1.IsApplicable(context.get(), fact_manager)); + good_replacement_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %13 with %100[1] in 'OpStore %15 %13' + auto good_replacement_2 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(13, MakeInstructionDescriptor(100, SpvOpStore, 0), 1), + MakeDataDescriptor(100, {1}), 103); + // Bad: too many indices + auto bad_replacement_2 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(13, MakeInstructionDescriptor(100, SpvOpStore, 0), 1), + MakeDataDescriptor(100, {1, 0}), 103); + ASSERT_TRUE(good_replacement_2.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(bad_replacement_2.IsApplicable(context.get(), fact_manager)); + good_replacement_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %22 with %100[2] in '%23 = OpConvertSToF %16 %22' + auto good_replacement_3 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 22, MakeInstructionDescriptor(23, SpvOpConvertSToF, 0), 0), + MakeDataDescriptor(100, {2}), 104); + // Bad: wrong input operand index + auto bad_replacement_3 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 22, MakeInstructionDescriptor(23, SpvOpConvertSToF, 0), 1), + MakeDataDescriptor(100, {2}), 104); + ASSERT_TRUE(good_replacement_3.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(bad_replacement_3.IsApplicable(context.get(), fact_manager)); + good_replacement_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %28 with %101[0] in 'OpStore %33 %28' + auto good_replacement_4 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(28, MakeInstructionDescriptor(33, SpvOpStore, 0), 1), + MakeDataDescriptor(101, {0}), 105); + // Bad: id use descriptor does not identify an appropriate instruction + auto bad_replacement_4 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(28, MakeInstructionDescriptor(33, SpvOpCopyObject, 0), + 1), + MakeDataDescriptor(101, {0}), 105); + ASSERT_TRUE(good_replacement_4.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(bad_replacement_4.IsApplicable(context.get(), fact_manager)); + good_replacement_4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %23 with %101[1] in '%50 = OpCopyObject %16 %23' + auto good_replacement_5 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(23, MakeInstructionDescriptor(50, SpvOpCopyObject, 0), + 0), + MakeDataDescriptor(101, {1}), 106); + // Bad: wrong synonym fact being used + auto bad_replacement_5 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(23, MakeInstructionDescriptor(50, SpvOpCopyObject, 0), + 0), + MakeDataDescriptor(101, {0}), 106); + ASSERT_TRUE(good_replacement_5.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(bad_replacement_5.IsApplicable(context.get(), fact_manager)); + good_replacement_5.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %32 with %101[2] in 'OpStore %33 %32' + auto good_replacement_6 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(32, MakeInstructionDescriptor(33, SpvOpStore, 1), 1), + MakeDataDescriptor(101, {2}), 107); + // Bad: id 1001 does not exist + auto bad_replacement_6 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(32, MakeInstructionDescriptor(33, SpvOpStore, 1), 1), + MakeDataDescriptor(1001, {2}), 107); + ASSERT_TRUE(good_replacement_6.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(bad_replacement_6.IsApplicable(context.get(), fact_manager)); + good_replacement_6.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %23 with %101[3] in '%51 = OpCopyObject %16 %23' + auto good_replacement_7 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(23, MakeInstructionDescriptor(51, SpvOpCopyObject, 0), + 0), + MakeDataDescriptor(101, {3}), 108); + // Bad: id 0 is invalid + auto bad_replacement_7 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(0, MakeInstructionDescriptor(51, SpvOpCopyObject, 0), + 0), + MakeDataDescriptor(101, {3}), 108); + ASSERT_TRUE(good_replacement_7.IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(bad_replacement_7.IsApplicable(context.get(), fact_manager)); + good_replacement_7.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %11 "A" + OpName %20 "B" + OpName %31 "g" + OpName %35 "h" + OpDecorate %11 RelaxedPrecision + OpDecorate %22 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 3 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 0 + %13 = OpConstant %6 3 + %14 = OpTypePointer Function %6 + %16 = OpTypeFloat 32 + %17 = OpConstant %7 4 + %18 = OpTypeArray %16 %17 + %19 = OpTypePointer Function %18 + %24 = OpTypePointer Function %16 + %28 = OpConstant %16 42 + %30 = OpConstant %6 2 + %34 = OpConstant %6 1 + %38 = OpConstant %6 42 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %20 = OpVariable %19 Function + %31 = OpVariable %24 Function + %35 = OpVariable %14 Function + %15 = OpAccessChain %14 %11 %12 + %21 = OpAccessChain %14 %11 %12 + %22 = OpLoad %6 %21 + %100 = OpCompositeConstruct %9 %12 %13 %22 + %103 = OpCompositeExtract %6 %100 1 + OpStore %15 %103 + %104 = OpCompositeExtract %6 %100 2 + %23 = OpConvertSToF %16 %104 + %102 = OpCompositeExtract %6 %100 0 + %25 = OpAccessChain %24 %20 %102 + OpStore %25 %23 + %26 = OpAccessChain %14 %11 %12 + %27 = OpLoad %6 %26 + %29 = OpAccessChain %24 %20 %27 + OpStore %29 %28 + %32 = OpLoad %16 %31 + %101 = OpCompositeConstruct %18 %28 %23 %32 %23 + %106 = OpCompositeExtract %16 %101 1 + %50 = OpCopyObject %16 %106 + %108 = OpCompositeExtract %16 %101 3 + %51 = OpCopyObject %16 %108 + %33 = OpAccessChain %24 %20 %30 + %105 = OpCompositeExtract %16 %101 0 + OpStore %33 %105 + %107 = OpCompositeExtract %16 %101 2 + OpStore %33 %107 + %36 = OpLoad %6 %35 + %37 = OpAccessChain %14 %11 %34 + OpStore %37 %36 + %39 = OpAccessChain %14 %11 %12 + %40 = OpLoad %6 %39 + %41 = OpIAdd %6 %38 %40 + %42 = OpAccessChain %14 %11 %30 + OpStore %42 %41 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceIdWithSynonymTest, MatrixCompositeSynonyms) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "m" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %50 = OpUndef %7 + %8 = OpTypeMatrix %7 3 + %9 = OpTypePointer Function %8 + %11 = OpTypeInt 32 1 + %12 = OpConstant %11 0 + %13 = OpConstant %6 1 + %14 = OpConstantComposite %7 %13 %13 %13 %13 + %15 = OpTypePointer Function %7 + %17 = OpConstant %11 1 + %18 = OpConstant %6 2 + %19 = OpConstantComposite %7 %18 %18 %18 %18 + %21 = OpConstant %11 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpVariable %9 Function + %16 = OpAccessChain %15 %10 %12 + OpStore %16 %14 + %20 = OpAccessChain %15 %10 %17 + OpStore %20 %19 + %22 = OpAccessChain %15 %10 %12 + %23 = OpLoad %7 %22 + %24 = OpAccessChain %15 %10 %17 + %25 = OpLoad %7 %24 + %100 = OpCompositeConstruct %8 %23 %25 %50 + %26 = OpFAdd %7 %23 %25 + %27 = OpAccessChain %15 %10 %21 + OpStore %27 %26 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + fact_manager.AddFact(MakeSynonymFact(23, 100, {0}), context.get()); + fact_manager.AddFact(MakeSynonymFact(25, 100, {1}), context.get()); + fact_manager.AddFact(MakeSynonymFact(50, 100, {2}), context.get()); + + // Replace %23 with %100[0] in '%26 = OpFAdd %7 %23 %25' + auto replacement_1 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(23, MakeInstructionDescriptor(26, SpvOpFAdd, 0), 0), + MakeDataDescriptor(100, {0}), 101); + ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager)); + replacement_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %25 with %100[1] in '%26 = OpFAdd %7 %23 %25' + auto replacement_2 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(25, MakeInstructionDescriptor(26, SpvOpFAdd, 0), 1), + MakeDataDescriptor(100, {1}), 102); + ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager)); + replacement_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "m" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %50 = OpUndef %7 + %8 = OpTypeMatrix %7 3 + %9 = OpTypePointer Function %8 + %11 = OpTypeInt 32 1 + %12 = OpConstant %11 0 + %13 = OpConstant %6 1 + %14 = OpConstantComposite %7 %13 %13 %13 %13 + %15 = OpTypePointer Function %7 + %17 = OpConstant %11 1 + %18 = OpConstant %6 2 + %19 = OpConstantComposite %7 %18 %18 %18 %18 + %21 = OpConstant %11 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpVariable %9 Function + %16 = OpAccessChain %15 %10 %12 + OpStore %16 %14 + %20 = OpAccessChain %15 %10 %17 + OpStore %20 %19 + %22 = OpAccessChain %15 %10 %12 + %23 = OpLoad %7 %22 + %24 = OpAccessChain %15 %10 %17 + %25 = OpLoad %7 %24 + %100 = OpCompositeConstruct %8 %23 %25 %50 + %101 = OpCompositeExtract %7 %100 0 + %102 = OpCompositeExtract %7 %100 1 + %26 = OpFAdd %7 %101 %102 + %27 = OpAccessChain %15 %10 %21 + OpStore %27 %26 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceIdWithSynonymTest, StructCompositeSynonyms) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "Inner" + OpMemberName %9 0 "a" + OpMemberName %9 1 "b" + OpName %11 "i1" + OpName %17 "i2" + OpName %31 "Point" + OpMemberName %31 0 "x" + OpMemberName %31 1 "y" + OpMemberName %31 2 "z" + OpName %32 "Outer" + OpMemberName %32 0 "c" + OpMemberName %32 1 "d" + OpName %34 "o1" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeVector %7 2 + %9 = OpTypeStruct %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 1 + %13 = OpConstant %7 2 + %14 = OpConstant %7 3 + %15 = OpConstantComposite %8 %13 %14 + %16 = OpConstantComposite %9 %12 %15 + %18 = OpConstant %6 0 + %19 = OpTypePointer Function %6 + %24 = OpTypePointer Function %8 + %27 = OpConstant %7 4 + %31 = OpTypeStruct %7 %7 %7 + %32 = OpTypeStruct %9 %31 + %33 = OpTypePointer Function %32 + %36 = OpConstant %7 10 + %37 = OpTypeInt 32 0 + %38 = OpConstant %37 0 + %39 = OpTypePointer Function %7 + %42 = OpConstant %37 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %17 = OpVariable %10 Function + %34 = OpVariable %33 Function + %101 = OpCompositeConstruct %31 %27 %36 %27 + OpStore %11 %16 + %20 = OpAccessChain %19 %11 %18 + %21 = OpLoad %6 %20 + %22 = OpIAdd %6 %21 %12 + %102 = OpCompositeConstruct %9 %22 %15 + %23 = OpAccessChain %19 %17 %18 + OpStore %23 %22 + %25 = OpAccessChain %24 %17 %12 + %26 = OpLoad %8 %25 + %28 = OpCompositeConstruct %8 %27 %27 + %29 = OpFAdd %8 %26 %28 + %30 = OpAccessChain %24 %17 %12 + OpStore %30 %29 + %35 = OpLoad %9 %11 + %40 = OpAccessChain %39 %11 %12 %38 + %41 = OpLoad %7 %40 + %43 = OpAccessChain %39 %11 %12 %42 + %44 = OpLoad %7 %43 + %45 = OpCompositeConstruct %31 %36 %41 %44 + %100 = OpCompositeConstruct %32 %16 %45 + %46 = OpCompositeConstruct %32 %35 %45 + OpStore %34 %46 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + fact_manager.AddFact(MakeSynonymFact(16, 100, {0}), context.get()); + fact_manager.AddFact(MakeSynonymFact(45, 100, {1}), context.get()); + fact_manager.AddFact(MakeSynonymFact(27, 101, {0}), context.get()); + fact_manager.AddFact(MakeSynonymFact(36, 101, {1}), context.get()); + fact_manager.AddFact(MakeSynonymFact(27, 101, {2}), context.get()); + fact_manager.AddFact(MakeSynonymFact(22, 102, {0}), context.get()); + fact_manager.AddFact(MakeSynonymFact(15, 102, {1}), context.get()); + + // Replace %45 with %100[1] in '%46 = OpCompositeConstruct %32 %35 %45' + auto replacement_1 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 45, MakeInstructionDescriptor(46, SpvOpCompositeConstruct, 0), 1), + MakeDataDescriptor(100, {1}), 201); + ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager)); + replacement_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace second occurrence of %27 with %101[0] in '%28 = + // OpCompositeConstruct %8 %27 %27' + auto replacement_2 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 27, MakeInstructionDescriptor(28, SpvOpCompositeConstruct, 0), 1), + MakeDataDescriptor(101, {0}), 202); + ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager)); + replacement_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %36 with %101[1] in '%45 = OpCompositeConstruct %31 %36 %41 %44' + auto replacement_3 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 36, MakeInstructionDescriptor(45, SpvOpCompositeConstruct, 0), 0), + MakeDataDescriptor(101, {1}), 203); + ASSERT_TRUE(replacement_3.IsApplicable(context.get(), fact_manager)); + replacement_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace first occurrence of %27 with %101[2] in '%28 = OpCompositeConstruct + // %8 %27 %27' + auto replacement_4 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 27, MakeInstructionDescriptor(28, SpvOpCompositeConstruct, 0), 0), + MakeDataDescriptor(101, {2}), 204); + ASSERT_TRUE(replacement_4.IsApplicable(context.get(), fact_manager)); + replacement_4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %22 with %102[0] in 'OpStore %23 %22' + auto replacement_5 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(22, MakeInstructionDescriptor(23, SpvOpStore, 0), 1), + MakeDataDescriptor(102, {0}), 205); + ASSERT_TRUE(replacement_5.IsApplicable(context.get(), fact_manager)); + replacement_5.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "Inner" + OpMemberName %9 0 "a" + OpMemberName %9 1 "b" + OpName %11 "i1" + OpName %17 "i2" + OpName %31 "Point" + OpMemberName %31 0 "x" + OpMemberName %31 1 "y" + OpMemberName %31 2 "z" + OpName %32 "Outer" + OpMemberName %32 0 "c" + OpMemberName %32 1 "d" + OpName %34 "o1" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeVector %7 2 + %9 = OpTypeStruct %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 1 + %13 = OpConstant %7 2 + %14 = OpConstant %7 3 + %15 = OpConstantComposite %8 %13 %14 + %16 = OpConstantComposite %9 %12 %15 + %18 = OpConstant %6 0 + %19 = OpTypePointer Function %6 + %24 = OpTypePointer Function %8 + %27 = OpConstant %7 4 + %31 = OpTypeStruct %7 %7 %7 + %32 = OpTypeStruct %9 %31 + %33 = OpTypePointer Function %32 + %36 = OpConstant %7 10 + %37 = OpTypeInt 32 0 + %38 = OpConstant %37 0 + %39 = OpTypePointer Function %7 + %42 = OpConstant %37 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %17 = OpVariable %10 Function + %34 = OpVariable %33 Function + %101 = OpCompositeConstruct %31 %27 %36 %27 + OpStore %11 %16 + %20 = OpAccessChain %19 %11 %18 + %21 = OpLoad %6 %20 + %22 = OpIAdd %6 %21 %12 + %102 = OpCompositeConstruct %9 %22 %15 + %23 = OpAccessChain %19 %17 %18 + %205 = OpCompositeExtract %6 %102 0 + OpStore %23 %205 + %25 = OpAccessChain %24 %17 %12 + %26 = OpLoad %8 %25 + %202 = OpCompositeExtract %7 %101 0 + %204 = OpCompositeExtract %7 %101 2 + %28 = OpCompositeConstruct %8 %204 %202 + %29 = OpFAdd %8 %26 %28 + %30 = OpAccessChain %24 %17 %12 + OpStore %30 %29 + %35 = OpLoad %9 %11 + %40 = OpAccessChain %39 %11 %12 %38 + %41 = OpLoad %7 %40 + %43 = OpAccessChain %39 %11 %12 %42 + %44 = OpLoad %7 %43 + %203 = OpCompositeExtract %7 %101 1 + %45 = OpCompositeConstruct %31 %203 %41 %44 + %100 = OpCompositeConstruct %32 %16 %45 + %201 = OpCompositeExtract %31 %100 1 + %46 = OpCompositeConstruct %32 %35 %201 + OpStore %34 %46 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceIdWithSynonymTest, VectorCompositeSynonyms) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "f" + OpName %12 "v2" + OpName %18 "v3" + OpName %23 "v4" + OpName %32 "b" + OpName %36 "bv2" + OpName %41 "bv3" + OpName %50 "bv4" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 42 + %10 = OpTypeVector %6 2 + %11 = OpTypePointer Function %10 + %16 = OpTypeVector %6 3 + %17 = OpTypePointer Function %16 + %21 = OpTypeVector %6 4 + %22 = OpTypePointer Function %21 + %30 = OpTypeBool + %31 = OpTypePointer Function %30 + %33 = OpConstantFalse %30 + %34 = OpTypeVector %30 2 + %35 = OpTypePointer Function %34 + %37 = OpConstantTrue %30 + %38 = OpConstantComposite %34 %37 %37 + %39 = OpTypeVector %30 3 + %40 = OpTypePointer Function %39 + %48 = OpTypeVector %30 4 + %49 = OpTypePointer Function %48 + %51 = OpTypeInt 32 0 + %52 = OpConstant %51 2 + %55 = OpConstant %6 0 + %57 = OpConstant %51 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %18 = OpVariable %17 Function + %23 = OpVariable %22 Function + %32 = OpVariable %31 Function + %36 = OpVariable %35 Function + %41 = OpVariable %40 Function + %50 = OpVariable %49 Function + OpStore %8 %9 + %13 = OpLoad %6 %8 + %14 = OpLoad %6 %8 + %15 = OpCompositeConstruct %10 %13 %14 + OpStore %12 %15 + %19 = OpLoad %10 %12 + %20 = OpVectorShuffle %16 %19 %19 0 0 1 + OpStore %18 %20 + %24 = OpLoad %16 %18 + %25 = OpLoad %6 %8 + %26 = OpCompositeExtract %6 %24 0 + %27 = OpCompositeExtract %6 %24 1 + %28 = OpCompositeExtract %6 %24 2 + %29 = OpCompositeConstruct %21 %26 %27 %28 %25 + OpStore %23 %29 + OpStore %32 %33 + OpStore %36 %38 + %42 = OpLoad %30 %32 + %43 = OpLoad %34 %36 + %44 = OpVectorShuffle %34 %43 %43 0 0 + %45 = OpCompositeExtract %30 %44 0 + %46 = OpCompositeExtract %30 %44 1 + %47 = OpCompositeConstruct %39 %42 %45 %46 + OpStore %41 %47 + %53 = OpAccessChain %7 %23 %52 + %54 = OpLoad %6 %53 + + %100 = OpCompositeConstruct %21 %20 %54 + %101 = OpCompositeConstruct %21 %15 %19 + %102 = OpCompositeConstruct %16 %27 %15 + %103 = OpCompositeConstruct %48 %33 %47 + %104 = OpCompositeConstruct %34 %42 %45 + %105 = OpCompositeConstruct %39 %38 %46 + + %86 = OpCopyObject %30 %33 + %56 = OpFOrdNotEqual %30 %54 %55 + %80 = OpCopyObject %16 %20 + %58 = OpAccessChain %7 %18 %57 + %59 = OpLoad %6 %58 + %60 = OpFOrdNotEqual %30 %59 %55 + %61 = OpLoad %34 %36 + %62 = OpLogicalAnd %30 %45 %46 + %63 = OpLogicalOr %30 %45 %46 + %64 = OpCompositeConstruct %48 %56 %60 %62 %63 + OpStore %12 %15 + %81 = OpVectorShuffle %16 %19 %19 0 0 1 + %82 = OpCompositeConstruct %21 %26 %27 %28 %25 + %83 = OpCopyObject %10 %15 + %84 = OpCopyObject %39 %47 + OpStore %50 %64 + %85 = OpCopyObject %30 %42 + OpStore %36 %38 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + fact_manager.AddFact(MakeSynonymFact(20, 100, {0}), context.get()); + fact_manager.AddFact(MakeSynonymFact(54, 100, {3}), context.get()); + fact_manager.AddFact(MakeSynonymFact(15, 101, {0}), context.get()); + fact_manager.AddFact(MakeSynonymFact(19, 101, {2}), context.get()); + fact_manager.AddFact(MakeSynonymFact(27, 102, {0}), context.get()); + fact_manager.AddFact(MakeSynonymFact(15, 102, {1}), context.get()); + fact_manager.AddFact(MakeSynonymFact(33, 103, {0}), context.get()); + fact_manager.AddFact(MakeSynonymFact(47, 103, {1}), context.get()); + fact_manager.AddFact(MakeSynonymFact(42, 104, {0}), context.get()); + fact_manager.AddFact(MakeSynonymFact(45, 104, {1}), context.get()); + fact_manager.AddFact(MakeSynonymFact(38, 105, {0}), context.get()); + fact_manager.AddFact(MakeSynonymFact(46, 105, {2}), context.get()); + + // Replace %20 with %100[0] in '%80 = OpCopyObject %16 %20' + auto replacement_1 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(20, MakeInstructionDescriptor(80, SpvOpCopyObject, 0), + 0), + MakeDataDescriptor(100, {0}), 200); + ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager)); + replacement_1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %54 with %100[3] in '%56 = OpFOrdNotEqual %30 %54 %55' + auto replacement_2 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 54, MakeInstructionDescriptor(56, SpvOpFOrdNotEqual, 0), 0), + MakeDataDescriptor(100, {3}), 201); + ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager)); + replacement_2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %15 with %101[0] in 'OpStore %12 %15' + auto replacement_3 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(15, MakeInstructionDescriptor(64, SpvOpStore, 0), 1), + MakeDataDescriptor(101, {0}), 202); + ASSERT_TRUE(replacement_3.IsApplicable(context.get(), fact_manager)); + replacement_3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %19 with %101[2] in '%81 = OpVectorShuffle %16 %19 %19 0 0 1' + auto replacement_4 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 19, MakeInstructionDescriptor(81, SpvOpVectorShuffle, 0), 0), + MakeDataDescriptor(101, {2}), 203); + ASSERT_TRUE(replacement_4.IsApplicable(context.get(), fact_manager)); + replacement_4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %27 with %102[0] in '%82 = OpCompositeConstruct %21 %26 %27 %28 + // %25' + auto replacement_5 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 27, MakeInstructionDescriptor(82, SpvOpCompositeConstruct, 0), 1), + MakeDataDescriptor(102, {0}), 204); + ASSERT_TRUE(replacement_5.IsApplicable(context.get(), fact_manager)); + replacement_5.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %15 with %102[1] in '%83 = OpCopyObject %10 %15' + auto replacement_6 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(15, MakeInstructionDescriptor(83, SpvOpCopyObject, 0), + 0), + MakeDataDescriptor(102, {1}), 205); + ASSERT_TRUE(replacement_6.IsApplicable(context.get(), fact_manager)); + replacement_6.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %33 with %103[0] in '%86 = OpCopyObject %30 %33' + auto replacement_7 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(33, MakeInstructionDescriptor(86, SpvOpCopyObject, 0), + 0), + MakeDataDescriptor(103, {0}), 206); + ASSERT_TRUE(replacement_7.IsApplicable(context.get(), fact_manager)); + replacement_7.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %47 with %103[1] in '%84 = OpCopyObject %39 %47' + auto replacement_8 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(47, MakeInstructionDescriptor(84, SpvOpCopyObject, 0), + 0), + MakeDataDescriptor(103, {1}), 207); + ASSERT_TRUE(replacement_8.IsApplicable(context.get(), fact_manager)); + replacement_8.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %42 with %104[0] in '%85 = OpCopyObject %30 %42' + auto replacement_9 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(42, MakeInstructionDescriptor(85, SpvOpCopyObject, 0), + 0), + MakeDataDescriptor(104, {0}), 208); + ASSERT_TRUE(replacement_9.IsApplicable(context.get(), fact_manager)); + replacement_9.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %45 with %104[1] in '%63 = OpLogicalOr %30 %45 %46' + auto replacement_10 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(45, MakeInstructionDescriptor(63, SpvOpLogicalOr, 0), + 0), + MakeDataDescriptor(104, {1}), 209); + ASSERT_TRUE(replacement_10.IsApplicable(context.get(), fact_manager)); + replacement_10.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %38 with %105[0] in 'OpStore %36 %38' + auto replacement_11 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(38, MakeInstructionDescriptor(85, SpvOpStore, 0), 1), + MakeDataDescriptor(105, {0}), 210); + ASSERT_TRUE(replacement_11.IsApplicable(context.get(), fact_manager)); + replacement_11.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %46 with %105[2] in '%62 = OpLogicalAnd %30 %45 %46' + auto replacement_12 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(46, MakeInstructionDescriptor(62, SpvOpLogicalAnd, 0), + 1), + MakeDataDescriptor(105, {2}), 211); + ASSERT_TRUE(replacement_12.IsApplicable(context.get(), fact_manager)); + replacement_12.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "f" + OpName %12 "v2" + OpName %18 "v3" + OpName %23 "v4" + OpName %32 "b" + OpName %36 "bv2" + OpName %41 "bv3" + OpName %50 "bv4" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 42 + %10 = OpTypeVector %6 2 + %11 = OpTypePointer Function %10 + %16 = OpTypeVector %6 3 + %17 = OpTypePointer Function %16 + %21 = OpTypeVector %6 4 + %22 = OpTypePointer Function %21 + %30 = OpTypeBool + %31 = OpTypePointer Function %30 + %33 = OpConstantFalse %30 + %34 = OpTypeVector %30 2 + %35 = OpTypePointer Function %34 + %37 = OpConstantTrue %30 + %38 = OpConstantComposite %34 %37 %37 + %39 = OpTypeVector %30 3 + %40 = OpTypePointer Function %39 + %48 = OpTypeVector %30 4 + %49 = OpTypePointer Function %48 + %51 = OpTypeInt 32 0 + %52 = OpConstant %51 2 + %55 = OpConstant %6 0 + %57 = OpConstant %51 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %18 = OpVariable %17 Function + %23 = OpVariable %22 Function + %32 = OpVariable %31 Function + %36 = OpVariable %35 Function + %41 = OpVariable %40 Function + %50 = OpVariable %49 Function + OpStore %8 %9 + %13 = OpLoad %6 %8 + %14 = OpLoad %6 %8 + %15 = OpCompositeConstruct %10 %13 %14 + OpStore %12 %15 + %19 = OpLoad %10 %12 + %20 = OpVectorShuffle %16 %19 %19 0 0 1 + OpStore %18 %20 + %24 = OpLoad %16 %18 + %25 = OpLoad %6 %8 + %26 = OpCompositeExtract %6 %24 0 + %27 = OpCompositeExtract %6 %24 1 + %28 = OpCompositeExtract %6 %24 2 + %29 = OpCompositeConstruct %21 %26 %27 %28 %25 + OpStore %23 %29 + OpStore %32 %33 + OpStore %36 %38 + %42 = OpLoad %30 %32 + %43 = OpLoad %34 %36 + %44 = OpVectorShuffle %34 %43 %43 0 0 + %45 = OpCompositeExtract %30 %44 0 + %46 = OpCompositeExtract %30 %44 1 + %47 = OpCompositeConstruct %39 %42 %45 %46 + OpStore %41 %47 + %53 = OpAccessChain %7 %23 %52 + %54 = OpLoad %6 %53 + + %100 = OpCompositeConstruct %21 %20 %54 + %101 = OpCompositeConstruct %21 %15 %19 + %102 = OpCompositeConstruct %16 %27 %15 + %103 = OpCompositeConstruct %48 %33 %47 + %104 = OpCompositeConstruct %34 %42 %45 + %105 = OpCompositeConstruct %39 %38 %46 + + %206 = OpCompositeExtract %30 %103 0 + %86 = OpCopyObject %30 %206 + %201 = OpCompositeExtract %6 %100 3 + %56 = OpFOrdNotEqual %30 %201 %55 + %200 = OpVectorShuffle %16 %100 %100 0 1 2 + %80 = OpCopyObject %16 %200 + %58 = OpAccessChain %7 %18 %57 + %59 = OpLoad %6 %58 + %60 = OpFOrdNotEqual %30 %59 %55 + %61 = OpLoad %34 %36 + %211 = OpCompositeExtract %30 %105 2 + %62 = OpLogicalAnd %30 %45 %211 + %209 = OpCompositeExtract %30 %104 1 + %63 = OpLogicalOr %30 %209 %46 + %64 = OpCompositeConstruct %48 %56 %60 %62 %63 + %202 = OpVectorShuffle %10 %101 %101 0 1 + OpStore %12 %202 + %203 = OpVectorShuffle %10 %101 %101 2 3 + %81 = OpVectorShuffle %16 %203 %19 0 0 1 + %204 = OpCompositeExtract %6 %102 0 + %82 = OpCompositeConstruct %21 %26 %204 %28 %25 + %205 = OpVectorShuffle %10 %102 %102 1 2 + %83 = OpCopyObject %10 %205 + %207 = OpVectorShuffle %39 %103 %103 1 2 3 + %84 = OpCopyObject %39 %207 + OpStore %50 %64 + %208 = OpCompositeExtract %30 %104 0 + %85 = OpCopyObject %30 %208 + %210 = OpVectorShuffle %34 %105 %105 0 1 + OpStore %36 %210 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/fuzz/transformation_set_function_control_test.cpp b/3rdparty/spirv-tools/test/fuzz/transformation_set_function_control_test.cpp new file mode 100644 index 000000000..536e965e3 --- /dev/null +++ b/3rdparty/spirv-tools/test/fuzz/transformation_set_function_control_test.cpp @@ -0,0 +1,251 @@ +// 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_set_function_control.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationSetFunctionControlTest, VariousScenarios) { + // This is a simple transformation; this test captures the important things + // to check for. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %54 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %11 "foo(i1;i1;" + OpName %9 "a" + OpName %10 "b" + OpName %13 "bar(" + OpName %17 "baz(i1;" + OpName %16 "x" + OpName %21 "boo(i1;i1;" + OpName %19 "a" + OpName %20 "b" + OpName %29 "g" + OpName %42 "param" + OpName %44 "param" + OpName %45 "param" + OpName %48 "param" + OpName %49 "param" + OpName %54 "color" + OpDecorate %54 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 %7 + %15 = OpTypeFunction %6 %7 + %28 = OpTypePointer Private %6 + %29 = OpVariable %28 Private + %30 = OpConstant %6 2 + %31 = OpConstant %6 5 + %51 = OpTypeFloat 32 + %52 = OpTypeVector %51 4 + %53 = OpTypePointer Output %52 + %54 = OpVariable %53 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %42 = OpVariable %7 Function + %44 = OpVariable %7 Function + %45 = OpVariable %7 Function + %48 = OpVariable %7 Function + %49 = OpVariable %7 Function + %41 = OpFunctionCall %2 %13 + OpStore %42 %30 + %43 = OpFunctionCall %6 %17 %42 + OpStore %44 %31 + %46 = OpLoad %6 %29 + OpStore %45 %46 + %47 = OpFunctionCall %6 %21 %44 %45 + OpStore %48 %43 + OpStore %49 %47 + %50 = OpFunctionCall %6 %11 %48 %49 + OpReturn + OpFunctionEnd + %11 = OpFunction %6 Const %8 + %9 = OpFunctionParameter %7 + %10 = OpFunctionParameter %7 + %12 = OpLabel + %23 = OpLoad %6 %9 + %24 = OpLoad %6 %10 + %25 = OpIAdd %6 %23 %24 + OpReturnValue %25 + OpFunctionEnd + %13 = OpFunction %2 Inline %3 + %14 = OpLabel + OpStore %29 %30 + OpReturn + OpFunctionEnd + %17 = OpFunction %6 Pure|DontInline %15 + %16 = OpFunctionParameter %7 + %18 = OpLabel + %32 = OpLoad %6 %16 + %33 = OpIAdd %6 %31 %32 + OpReturnValue %33 + OpFunctionEnd + %21 = OpFunction %6 DontInline %8 + %19 = OpFunctionParameter %7 + %20 = OpFunctionParameter %7 + %22 = OpLabel + %36 = OpLoad %6 %19 + %37 = OpLoad %6 %20 + %38 = OpIMul %6 %36 %37 + OpReturnValue %38 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + FactManager fact_manager; + + // %36 is not a function + ASSERT_FALSE(TransformationSetFunctionControl(36, SpvFunctionControlMaskNone) + .IsApplicable(context.get(), fact_manager)); + // Cannot add the Pure function control to %4 as it did not already have it + ASSERT_FALSE(TransformationSetFunctionControl(4, SpvFunctionControlPureMask) + .IsApplicable(context.get(), fact_manager)); + // Cannot add the Const function control to %21 as it did not already + // have it + ASSERT_FALSE(TransformationSetFunctionControl(21, SpvFunctionControlConstMask) + .IsApplicable(context.get(), fact_manager)); + + // Set to None, removing Const + TransformationSetFunctionControl transformation1(11, + SpvFunctionControlMaskNone); + ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); + transformation1.Apply(context.get(), &fact_manager); + + // Set to Inline; silly to do it on an entry point, but it is allowed + TransformationSetFunctionControl transformation2( + 4, SpvFunctionControlInlineMask); + ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); + transformation2.Apply(context.get(), &fact_manager); + + // Set to Pure, removing DontInline + TransformationSetFunctionControl transformation3(17, + SpvFunctionControlPureMask); + ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); + transformation3.Apply(context.get(), &fact_manager); + + // Change from Inline to DontInline + TransformationSetFunctionControl transformation4( + 13, SpvFunctionControlDontInlineMask); + ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); + transformation4.Apply(context.get(), &fact_manager); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %54 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %11 "foo(i1;i1;" + OpName %9 "a" + OpName %10 "b" + OpName %13 "bar(" + OpName %17 "baz(i1;" + OpName %16 "x" + OpName %21 "boo(i1;i1;" + OpName %19 "a" + OpName %20 "b" + OpName %29 "g" + OpName %42 "param" + OpName %44 "param" + OpName %45 "param" + OpName %48 "param" + OpName %49 "param" + OpName %54 "color" + OpDecorate %54 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 %7 + %15 = OpTypeFunction %6 %7 + %28 = OpTypePointer Private %6 + %29 = OpVariable %28 Private + %30 = OpConstant %6 2 + %31 = OpConstant %6 5 + %51 = OpTypeFloat 32 + %52 = OpTypeVector %51 4 + %53 = OpTypePointer Output %52 + %54 = OpVariable %53 Output + %4 = OpFunction %2 Inline %3 + %5 = OpLabel + %42 = OpVariable %7 Function + %44 = OpVariable %7 Function + %45 = OpVariable %7 Function + %48 = OpVariable %7 Function + %49 = OpVariable %7 Function + %41 = OpFunctionCall %2 %13 + OpStore %42 %30 + %43 = OpFunctionCall %6 %17 %42 + OpStore %44 %31 + %46 = OpLoad %6 %29 + OpStore %45 %46 + %47 = OpFunctionCall %6 %21 %44 %45 + OpStore %48 %43 + OpStore %49 %47 + %50 = OpFunctionCall %6 %11 %48 %49 + OpReturn + OpFunctionEnd + %11 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %10 = OpFunctionParameter %7 + %12 = OpLabel + %23 = OpLoad %6 %9 + %24 = OpLoad %6 %10 + %25 = OpIAdd %6 %23 %24 + OpReturnValue %25 + OpFunctionEnd + %13 = OpFunction %2 DontInline %3 + %14 = OpLabel + OpStore %29 %30 + OpReturn + OpFunctionEnd + %17 = OpFunction %6 Pure %15 + %16 = OpFunctionParameter %7 + %18 = OpLabel + %32 = OpLoad %6 %16 + %33 = OpIAdd %6 %31 %32 + OpReturnValue %33 + OpFunctionEnd + %21 = OpFunction %6 DontInline %8 + %19 = OpFunctionParameter %7 + %20 = OpFunctionParameter %7 + %22 = OpLabel + %36 = OpLoad %6 %19 + %37 = OpLoad %6 %20 + %38 = OpIMul %6 %36 %37 + OpReturnValue %38 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/fuzz/transformation_set_loop_control_test.cpp b/3rdparty/spirv-tools/test/fuzz/transformation_set_loop_control_test.cpp new file mode 100644 index 000000000..83953ec0e --- /dev/null +++ b/3rdparty/spirv-tools/test/fuzz/transformation_set_loop_control_test.cpp @@ -0,0 +1,968 @@ +// 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_set_loop_control.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationSetLoopControlTest, VariousScenarios) { + // This test features loops with various different controls, and goes through + // a number of acceptable and unacceptable transformations to those controls. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %22 = OpVariable %7 Function + %32 = OpVariable %7 Function + %42 = OpVariable %7 Function + %52 = OpVariable %7 Function + %62 = OpVariable %7 Function + %72 = OpVariable %7 Function + %82 = OpVariable %7 Function + %92 = OpVariable %7 Function + %102 = OpVariable %7 Function + %112 = OpVariable %7 Function + %122 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %132 = OpPhi %6 %9 %5 %21 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %132 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %21 = OpIAdd %6 %132 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpStore %22 %9 + OpBranch %23 + %23 = OpLabel + %133 = OpPhi %6 %9 %12 %31 %26 + OpLoopMerge %25 %26 Unroll + OpBranch %27 + %27 = OpLabel + %29 = OpSLessThan %17 %133 %16 + OpBranchConditional %29 %24 %25 + %24 = OpLabel + OpBranch %26 + %26 = OpLabel + %31 = OpIAdd %6 %133 %20 + OpStore %22 %31 + OpBranch %23 + %25 = OpLabel + OpStore %32 %9 + OpBranch %33 + %33 = OpLabel + %134 = OpPhi %6 %9 %25 %41 %36 + OpLoopMerge %35 %36 DontUnroll + OpBranch %37 + %37 = OpLabel + %39 = OpSLessThan %17 %134 %16 + OpBranchConditional %39 %34 %35 + %34 = OpLabel + OpBranch %36 + %36 = OpLabel + %41 = OpIAdd %6 %134 %20 + OpStore %32 %41 + OpBranch %33 + %35 = OpLabel + OpStore %42 %9 + OpBranch %43 + %43 = OpLabel + %135 = OpPhi %6 %9 %35 %51 %46 + OpLoopMerge %45 %46 DependencyInfinite + OpBranch %47 + %47 = OpLabel + %49 = OpSLessThan %17 %135 %16 + OpBranchConditional %49 %44 %45 + %44 = OpLabel + OpBranch %46 + %46 = OpLabel + %51 = OpIAdd %6 %135 %20 + OpStore %42 %51 + OpBranch %43 + %45 = OpLabel + OpStore %52 %9 + OpBranch %53 + %53 = OpLabel + %136 = OpPhi %6 %9 %45 %61 %56 + OpLoopMerge %55 %56 DependencyLength 3 + OpBranch %57 + %57 = OpLabel + %59 = OpSLessThan %17 %136 %16 + OpBranchConditional %59 %54 %55 + %54 = OpLabel + OpBranch %56 + %56 = OpLabel + %61 = OpIAdd %6 %136 %20 + OpStore %52 %61 + OpBranch %53 + %55 = OpLabel + OpStore %62 %9 + OpBranch %63 + %63 = OpLabel + %137 = OpPhi %6 %9 %55 %71 %66 + OpLoopMerge %65 %66 MinIterations 10 + OpBranch %67 + %67 = OpLabel + %69 = OpSLessThan %17 %137 %16 + OpBranchConditional %69 %64 %65 + %64 = OpLabel + OpBranch %66 + %66 = OpLabel + %71 = OpIAdd %6 %137 %20 + OpStore %62 %71 + OpBranch %63 + %65 = OpLabel + OpStore %72 %9 + OpBranch %73 + %73 = OpLabel + %138 = OpPhi %6 %9 %65 %81 %76 + OpLoopMerge %75 %76 MaxIterations 50 + OpBranch %77 + %77 = OpLabel + %79 = OpSLessThan %17 %138 %16 + OpBranchConditional %79 %74 %75 + %74 = OpLabel + OpBranch %76 + %76 = OpLabel + %81 = OpIAdd %6 %138 %20 + OpStore %72 %81 + OpBranch %73 + %75 = OpLabel + OpStore %82 %9 + OpBranch %83 + %83 = OpLabel + %139 = OpPhi %6 %9 %75 %91 %86 + OpLoopMerge %85 %86 IterationMultiple 4 + OpBranch %87 + %87 = OpLabel + %89 = OpSLessThan %17 %139 %16 + OpBranchConditional %89 %84 %85 + %84 = OpLabel + OpBranch %86 + %86 = OpLabel + %91 = OpIAdd %6 %139 %20 + OpStore %82 %91 + OpBranch %83 + %85 = OpLabel + OpStore %92 %9 + OpBranch %93 + %93 = OpLabel + %140 = OpPhi %6 %9 %85 %101 %96 + OpLoopMerge %95 %96 PeelCount 2 + OpBranch %97 + %97 = OpLabel + %99 = OpSLessThan %17 %140 %16 + OpBranchConditional %99 %94 %95 + %94 = OpLabel + OpBranch %96 + %96 = OpLabel + %101 = OpIAdd %6 %140 %20 + OpStore %92 %101 + OpBranch %93 + %95 = OpLabel + OpStore %102 %9 + OpBranch %103 + %103 = OpLabel + %141 = OpPhi %6 %9 %95 %111 %106 + OpLoopMerge %105 %106 PartialCount 3 + OpBranch %107 + %107 = OpLabel + %109 = OpSLessThan %17 %141 %16 + OpBranchConditional %109 %104 %105 + %104 = OpLabel + OpBranch %106 + %106 = OpLabel + %111 = OpIAdd %6 %141 %20 + OpStore %102 %111 + OpBranch %103 + %105 = OpLabel + OpStore %112 %9 + OpBranch %113 + %113 = OpLabel + %142 = OpPhi %6 %9 %105 %121 %116 + OpLoopMerge %115 %116 Unroll|PeelCount|PartialCount 3 4 + OpBranch %117 + %117 = OpLabel + %119 = OpSLessThan %17 %142 %16 + OpBranchConditional %119 %114 %115 + %114 = OpLabel + OpBranch %116 + %116 = OpLabel + %121 = OpIAdd %6 %142 %20 + OpStore %112 %121 + OpBranch %113 + %115 = OpLabel + OpStore %122 %9 + OpBranch %123 + %123 = OpLabel + %143 = OpPhi %6 %9 %115 %131 %126 + OpLoopMerge %125 %126 DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount 2 5 90 4 7 14 + OpBranch %127 + %127 = OpLabel + %129 = OpSLessThan %17 %143 %16 + OpBranchConditional %129 %124 %125 + %124 = OpLabel + OpBranch %126 + %126 = OpLabel + %131 = OpIAdd %6 %143 %20 + OpStore %122 %131 + OpBranch %123 + %125 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + // These are the loop headers together with the selection controls of their + // merge instructions: + // %10 None + // %23 Unroll + // %33 DontUnroll + // %43 DependencyInfinite + // %53 DependencyLength 3 + // %63 MinIterations 10 + // %73 MaxIterations 50 + // %83 IterationMultiple 4 + // %93 PeelCount 2 + // %103 PartialCount 3 + // %113 Unroll|PeelCount|PartialCount 3 4 + // %123 + // DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount + // 2 5 90 4 7 14 + + ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(10, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(TransformationSetLoopControl( + 10, SpvLoopControlDependencyInfiniteMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + TransformationSetLoopControl(10, SpvLoopControlDependencyLengthMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + TransformationSetLoopControl(10, SpvLoopControlMinIterationsMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + TransformationSetLoopControl(10, SpvLoopControlMaxIterationsMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(TransformationSetLoopControl( + 10, SpvLoopControlIterationMultipleMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 3) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 0, 3) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 3, 3) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl( + 10, + SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, + 3, 3) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(10, + SpvLoopControlUnrollMask | + SpvLoopControlPeelCountMask | + SpvLoopControlPartialCountMask, + 3, 3) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(TransformationSetLoopControl(10, + SpvLoopControlDontUnrollMask | + SpvLoopControlPeelCountMask | + SpvLoopControlPartialCountMask, + 3, 3) + .IsApplicable(context.get(), fact_manager)); + + ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl( + 23, + SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, + 3, 3) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + TransformationSetLoopControl(23, SpvLoopControlMaxIterationsMask, 2, 3) + .IsApplicable(context.get(), fact_manager)); + + ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(33, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + TransformationSetLoopControl(33, SpvLoopControlMinIterationsMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl( + 33, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(TransformationSetLoopControl(33, + SpvLoopControlDontUnrollMask | + SpvLoopControlPartialCountMask, + 0, 10) + .IsApplicable(context.get(), fact_manager)); + + ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(43, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl( + 43, + SpvLoopControlMaskNone | SpvLoopControlDependencyInfiniteMask, + 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl( + 43, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask, + 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl( + 43, + SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask, + 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + TransformationSetLoopControl(43, + SpvLoopControlDependencyInfiniteMask | + SpvLoopControlDependencyLengthMask, + 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl( + 43, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0) + .IsApplicable(context.get(), fact_manager)); + + ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(53, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + TransformationSetLoopControl(53, SpvLoopControlMaxIterationsMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl( + 53, SpvLoopControlMaskNone | SpvLoopControlDependencyLengthMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + TransformationSetLoopControl( + 53, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask, + 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl( + 53, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyLengthMask, + 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + TransformationSetLoopControl(53, + SpvLoopControlDependencyInfiniteMask | + SpvLoopControlDependencyLengthMask, + 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl( + 53, + SpvLoopControlUnrollMask | SpvLoopControlDependencyLengthMask | + SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, + 5, 3) + .IsApplicable(context.get(), fact_manager)); + + ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(63, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(63, + SpvLoopControlUnrollMask | + SpvLoopControlMinIterationsMask | + SpvLoopControlPeelCountMask | + SpvLoopControlPartialCountMask, + 5, 3) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(63, + SpvLoopControlUnrollMask | + SpvLoopControlMinIterationsMask | + SpvLoopControlPeelCountMask, + 23, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(TransformationSetLoopControl( + 63, + SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | + SpvLoopControlPeelCountMask, + 2, 23) + .IsApplicable(context.get(), fact_manager)); + + ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(73, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(TransformationSetLoopControl( + 73, + SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | + SpvLoopControlPeelCountMask | + SpvLoopControlPartialCountMask, + 5, 3) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(73, + SpvLoopControlUnrollMask | + SpvLoopControlMaxIterationsMask | + SpvLoopControlPeelCountMask, + 23, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(TransformationSetLoopControl( + 73, + SpvLoopControlUnrollMask | SpvLoopControlMaxIterationsMask | + SpvLoopControlPeelCountMask, + 2, 23) + .IsApplicable(context.get(), fact_manager)); + + ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(TransformationSetLoopControl( + 83, + SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | + SpvLoopControlPeelCountMask | + SpvLoopControlPartialCountMask, + 5, 3) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(83, + SpvLoopControlUnrollMask | + SpvLoopControlIterationMultipleMask | + SpvLoopControlPeelCountMask, + 23, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + TransformationSetLoopControl(83, + SpvLoopControlUnrollMask | + SpvLoopControlIterationMultipleMask | + SpvLoopControlPeelCountMask, + 2, 23) + .IsApplicable(context.get(), fact_manager)); + + ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(93, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 8) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(93, SpvLoopControlPartialCountMask, 0, 8) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl( + 93, + SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, + 16, 8) + .IsApplicable(context.get(), fact_manager)); + + ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(103, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(TransformationSetLoopControl(103, + SpvLoopControlDontUnrollMask | + SpvLoopControlPartialCountMask, + 0, 60) + .IsApplicable(context.get(), fact_manager)); + + ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(113, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + TransformationSetLoopControl( + 113, + SpvLoopControlIterationMultipleMask | SpvLoopControlPeelCountMask, 12, + 0) + .IsApplicable(context.get(), fact_manager)); + + ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl(123, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE( + TransformationSetLoopControl( + 123, + SpvLoopControlMinIterationsMask | SpvLoopControlMaxIterationsMask | + SpvLoopControlIterationMultipleMask | + SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, + 7, 8) + .IsApplicable(context.get(), fact_manager)); + ASSERT_TRUE(TransformationSetLoopControl(123, + SpvLoopControlUnrollMask | + SpvLoopControlMinIterationsMask | + SpvLoopControlMaxIterationsMask | + SpvLoopControlPartialCountMask, + 0, 9) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE(TransformationSetLoopControl( + 123, + SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | + SpvLoopControlMaxIterationsMask | + SpvLoopControlPartialCountMask, + 7, 9) + .IsApplicable(context.get(), fact_manager)); + ASSERT_FALSE( + TransformationSetLoopControl( + 123, + SpvLoopControlDontUnrollMask | SpvLoopControlMinIterationsMask | + SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask, + 7, 9) + .IsApplicable(context.get(), fact_manager)); + + TransformationSetLoopControl(10, + SpvLoopControlUnrollMask | + SpvLoopControlPeelCountMask | + SpvLoopControlPartialCountMask, + 3, 3) + .Apply(context.get(), &fact_manager); + TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0) + .Apply(context.get(), &fact_manager); + TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0) + .Apply(context.get(), &fact_manager); + TransformationSetLoopControl( + 43, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask, + 0, 0) + .Apply(context.get(), &fact_manager); + TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0) + .Apply(context.get(), &fact_manager); + TransformationSetLoopControl(63, + SpvLoopControlUnrollMask | + SpvLoopControlMinIterationsMask | + SpvLoopControlPeelCountMask, + 23, 0) + .Apply(context.get(), &fact_manager); + TransformationSetLoopControl(73, + SpvLoopControlUnrollMask | + SpvLoopControlMaxIterationsMask | + SpvLoopControlPeelCountMask, + 23, 0) + .Apply(context.get(), &fact_manager); + TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0) + .Apply(context.get(), &fact_manager); + TransformationSetLoopControl( + 93, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 16, 8) + .Apply(context.get(), &fact_manager); + TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60) + .Apply(context.get(), &fact_manager); + TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0) + .Apply(context.get(), &fact_manager); + TransformationSetLoopControl( + 123, + SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | + SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask, + 0, 9) + .Apply(context.get(), &fact_manager); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %22 = OpVariable %7 Function + %32 = OpVariable %7 Function + %42 = OpVariable %7 Function + %52 = OpVariable %7 Function + %62 = OpVariable %7 Function + %72 = OpVariable %7 Function + %82 = OpVariable %7 Function + %92 = OpVariable %7 Function + %102 = OpVariable %7 Function + %112 = OpVariable %7 Function + %122 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %132 = OpPhi %6 %9 %5 %21 %13 + OpLoopMerge %12 %13 Unroll|PeelCount|PartialCount 3 3 + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %132 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %21 = OpIAdd %6 %132 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpStore %22 %9 + OpBranch %23 + %23 = OpLabel + %133 = OpPhi %6 %9 %12 %31 %26 + OpLoopMerge %25 %26 DontUnroll + OpBranch %27 + %27 = OpLabel + %29 = OpSLessThan %17 %133 %16 + OpBranchConditional %29 %24 %25 + %24 = OpLabel + OpBranch %26 + %26 = OpLabel + %31 = OpIAdd %6 %133 %20 + OpStore %22 %31 + OpBranch %23 + %25 = OpLabel + OpStore %32 %9 + OpBranch %33 + %33 = OpLabel + %134 = OpPhi %6 %9 %25 %41 %36 + OpLoopMerge %35 %36 Unroll + OpBranch %37 + %37 = OpLabel + %39 = OpSLessThan %17 %134 %16 + OpBranchConditional %39 %34 %35 + %34 = OpLabel + OpBranch %36 + %36 = OpLabel + %41 = OpIAdd %6 %134 %20 + OpStore %32 %41 + OpBranch %33 + %35 = OpLabel + OpStore %42 %9 + OpBranch %43 + %43 = OpLabel + %135 = OpPhi %6 %9 %35 %51 %46 + OpLoopMerge %45 %46 DontUnroll|DependencyInfinite + OpBranch %47 + %47 = OpLabel + %49 = OpSLessThan %17 %135 %16 + OpBranchConditional %49 %44 %45 + %44 = OpLabel + OpBranch %46 + %46 = OpLabel + %51 = OpIAdd %6 %135 %20 + OpStore %42 %51 + OpBranch %43 + %45 = OpLabel + OpStore %52 %9 + OpBranch %53 + %53 = OpLabel + %136 = OpPhi %6 %9 %45 %61 %56 + OpLoopMerge %55 %56 None + OpBranch %57 + %57 = OpLabel + %59 = OpSLessThan %17 %136 %16 + OpBranchConditional %59 %54 %55 + %54 = OpLabel + OpBranch %56 + %56 = OpLabel + %61 = OpIAdd %6 %136 %20 + OpStore %52 %61 + OpBranch %53 + %55 = OpLabel + OpStore %62 %9 + OpBranch %63 + %63 = OpLabel + %137 = OpPhi %6 %9 %55 %71 %66 + OpLoopMerge %65 %66 Unroll|MinIterations|PeelCount 10 23 + OpBranch %67 + %67 = OpLabel + %69 = OpSLessThan %17 %137 %16 + OpBranchConditional %69 %64 %65 + %64 = OpLabel + OpBranch %66 + %66 = OpLabel + %71 = OpIAdd %6 %137 %20 + OpStore %62 %71 + OpBranch %63 + %65 = OpLabel + OpStore %72 %9 + OpBranch %73 + %73 = OpLabel + %138 = OpPhi %6 %9 %65 %81 %76 + OpLoopMerge %75 %76 Unroll|MaxIterations|PeelCount 50 23 + OpBranch %77 + %77 = OpLabel + %79 = OpSLessThan %17 %138 %16 + OpBranchConditional %79 %74 %75 + %74 = OpLabel + OpBranch %76 + %76 = OpLabel + %81 = OpIAdd %6 %138 %20 + OpStore %72 %81 + OpBranch %73 + %75 = OpLabel + OpStore %82 %9 + OpBranch %83 + %83 = OpLabel + %139 = OpPhi %6 %9 %75 %91 %86 + OpLoopMerge %85 %86 DontUnroll + OpBranch %87 + %87 = OpLabel + %89 = OpSLessThan %17 %139 %16 + OpBranchConditional %89 %84 %85 + %84 = OpLabel + OpBranch %86 + %86 = OpLabel + %91 = OpIAdd %6 %139 %20 + OpStore %82 %91 + OpBranch %83 + %85 = OpLabel + OpStore %92 %9 + OpBranch %93 + %93 = OpLabel + %140 = OpPhi %6 %9 %85 %101 %96 + OpLoopMerge %95 %96 PeelCount|PartialCount 16 8 + OpBranch %97 + %97 = OpLabel + %99 = OpSLessThan %17 %140 %16 + OpBranchConditional %99 %94 %95 + %94 = OpLabel + OpBranch %96 + %96 = OpLabel + %101 = OpIAdd %6 %140 %20 + OpStore %92 %101 + OpBranch %93 + %95 = OpLabel + OpStore %102 %9 + OpBranch %103 + %103 = OpLabel + %141 = OpPhi %6 %9 %95 %111 %106 + OpLoopMerge %105 %106 PartialCount 60 + OpBranch %107 + %107 = OpLabel + %109 = OpSLessThan %17 %141 %16 + OpBranchConditional %109 %104 %105 + %104 = OpLabel + OpBranch %106 + %106 = OpLabel + %111 = OpIAdd %6 %141 %20 + OpStore %102 %111 + OpBranch %103 + %105 = OpLabel + OpStore %112 %9 + OpBranch %113 + %113 = OpLabel + %142 = OpPhi %6 %9 %105 %121 %116 + OpLoopMerge %115 %116 PeelCount 12 + OpBranch %117 + %117 = OpLabel + %119 = OpSLessThan %17 %142 %16 + OpBranchConditional %119 %114 %115 + %114 = OpLabel + OpBranch %116 + %116 = OpLabel + %121 = OpIAdd %6 %142 %20 + OpStore %112 %121 + OpBranch %113 + %115 = OpLabel + OpStore %122 %9 + OpBranch %123 + %123 = OpLabel + %143 = OpPhi %6 %9 %115 %131 %126 + OpLoopMerge %125 %126 Unroll|MinIterations|MaxIterations|PartialCount 5 90 9 + OpBranch %127 + %127 = OpLabel + %129 = OpSLessThan %17 %143 %16 + OpBranchConditional %129 %124 %125 + %124 = OpLabel + OpBranch %126 + %126 = OpLabel + %131 = OpIAdd %6 %143 %20 + OpStore %122 %131 + OpBranch %123 + %125 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationSetLoopControlTest, CheckSPIRVVersionsRespected) { + // This test checks that we do not allow introducing PeelCount and + // PartialCount loop controls if the SPIR-V version being used does not + // support them. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %19 = OpLoad %6 %8 + %21 = OpIAdd %6 %19 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto consumer = nullptr; + const auto context_1_0 = + BuildModule(SPV_ENV_UNIVERSAL_1_0, consumer, shader, kFuzzAssembleOption); + const auto context_1_1 = + BuildModule(SPV_ENV_UNIVERSAL_1_1, consumer, shader, kFuzzAssembleOption); + const auto context_1_2 = + BuildModule(SPV_ENV_UNIVERSAL_1_2, consumer, shader, kFuzzAssembleOption); + const auto context_1_3 = + BuildModule(SPV_ENV_UNIVERSAL_1_3, consumer, shader, kFuzzAssembleOption); + const auto context_1_4 = + BuildModule(SPV_ENV_UNIVERSAL_1_4, consumer, shader, kFuzzAssembleOption); + const auto context_1_5 = + BuildModule(SPV_ENV_UNIVERSAL_1_5, consumer, shader, kFuzzAssembleOption); + + FactManager fact_manager; + + TransformationSetLoopControl set_peel_and_partial( + 10, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 4, 4); + + // PeelCount and PartialCount were introduced in SPIRV 1.4, so are not valid + // in the context of older versions. + ASSERT_FALSE( + set_peel_and_partial.IsApplicable(context_1_0.get(), fact_manager)); + ASSERT_FALSE( + set_peel_and_partial.IsApplicable(context_1_1.get(), fact_manager)); + ASSERT_FALSE( + set_peel_and_partial.IsApplicable(context_1_2.get(), fact_manager)); + ASSERT_FALSE( + set_peel_and_partial.IsApplicable(context_1_3.get(), fact_manager)); + + ASSERT_TRUE( + set_peel_and_partial.IsApplicable(context_1_4.get(), fact_manager)); + ASSERT_TRUE( + set_peel_and_partial.IsApplicable(context_1_5.get(), fact_manager)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/fuzz/transformation_set_selection_control_test.cpp b/3rdparty/spirv-tools/test/fuzz/transformation_set_selection_control_test.cpp new file mode 100644 index 000000000..9696417b0 --- /dev/null +++ b/3rdparty/spirv-tools/test/fuzz/transformation_set_selection_control_test.cpp @@ -0,0 +1,219 @@ +// 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_set_selection_control.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationSetSelectionControlTest, VariousScenarios) { + // This is a simple transformation; this test captures the important things + // to check for. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 3 + %25 = OpConstant %6 1 + %28 = OpConstant %6 2 + %38 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %19 = OpLoad %6 %8 + %21 = OpSGreaterThan %17 %19 %20 + OpSelectionMerge %23 Flatten + OpBranchConditional %21 %22 %23 + %22 = OpLabel + %24 = OpLoad %6 %8 + %26 = OpIAdd %6 %24 %25 + OpStore %8 %26 + OpBranch %23 + %23 = OpLabel + %27 = OpLoad %6 %8 + %29 = OpSLessThan %17 %27 %28 + OpSelectionMerge %31 DontFlatten + OpBranchConditional %29 %30 %31 + %30 = OpLabel + %32 = OpLoad %6 %8 + %33 = OpISub %6 %32 %25 + OpStore %8 %33 + OpBranch %31 + %31 = OpLabel + %34 = OpLoad %6 %8 + OpSelectionMerge %37 None + OpSwitch %34 %36 0 %35 + %36 = OpLabel + OpBranch %37 + %35 = OpLabel + %39 = OpLoad %6 %8 + %40 = OpIAdd %6 %39 %38 + OpStore %8 %40 + OpBranch %36 + %37 = OpLabel + OpBranch %13 + %13 = OpLabel + %43 = OpLoad %6 %8 + %44 = OpIAdd %6 %43 %25 + OpStore %8 %44 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + FactManager fact_manager; + + // %44 is not a block + ASSERT_FALSE( + TransformationSetSelectionControl(44, SpvSelectionControlFlattenMask) + .IsApplicable(context.get(), fact_manager)); + // %13 does not end with OpSelectionMerge + ASSERT_FALSE( + TransformationSetSelectionControl(13, SpvSelectionControlMaskNone) + .IsApplicable(context.get(), fact_manager)); + // %10 ends in OpLoopMerge, not OpSelectionMerge + ASSERT_FALSE( + TransformationSetSelectionControl(10, SpvSelectionControlMaskNone) + .IsApplicable(context.get(), fact_manager)); + + TransformationSetSelectionControl transformation1( + 11, SpvSelectionControlDontFlattenMask); + ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); + transformation1.Apply(context.get(), &fact_manager); + + TransformationSetSelectionControl transformation2( + 23, SpvSelectionControlFlattenMask); + ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); + transformation2.Apply(context.get(), &fact_manager); + + TransformationSetSelectionControl transformation3( + 31, SpvSelectionControlMaskNone); + ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager)); + transformation3.Apply(context.get(), &fact_manager); + + TransformationSetSelectionControl transformation4( + 31, SpvSelectionControlFlattenMask); + ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager)); + transformation4.Apply(context.get(), &fact_manager); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 3 + %25 = OpConstant %6 1 + %28 = OpConstant %6 2 + %38 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %19 = OpLoad %6 %8 + %21 = OpSGreaterThan %17 %19 %20 + OpSelectionMerge %23 DontFlatten + OpBranchConditional %21 %22 %23 + %22 = OpLabel + %24 = OpLoad %6 %8 + %26 = OpIAdd %6 %24 %25 + OpStore %8 %26 + OpBranch %23 + %23 = OpLabel + %27 = OpLoad %6 %8 + %29 = OpSLessThan %17 %27 %28 + OpSelectionMerge %31 Flatten + OpBranchConditional %29 %30 %31 + %30 = OpLabel + %32 = OpLoad %6 %8 + %33 = OpISub %6 %32 %25 + OpStore %8 %33 + OpBranch %31 + %31 = OpLabel + %34 = OpLoad %6 %8 + OpSelectionMerge %37 Flatten + OpSwitch %34 %36 0 %35 + %36 = OpLabel + OpBranch %37 + %35 = OpLabel + %39 = OpLoad %6 %8 + %40 = OpIAdd %6 %39 %38 + OpStore %8 %40 + OpBranch %36 + %37 = OpLabel + OpBranch %13 + %13 = OpLabel + %43 = OpLoad %6 %8 + %44 = OpIAdd %6 %43 %25 + OpStore %8 %44 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/opt/if_conversion_test.cpp b/3rdparty/spirv-tools/test/opt/if_conversion_test.cpp index 03932a95a..aa5adea6e 100644 --- a/3rdparty/spirv-tools/test/opt/if_conversion_test.cpp +++ b/3rdparty/spirv-tools/test/opt/if_conversion_test.cpp @@ -375,14 +375,12 @@ OpEntryPoint Vertex %1 "func" %2 OpSelectionMerge %12 None OpBranchConditional %true %13 %12 %13 = OpLabel -OpBranchConditional %true %14 %15 +OpBranchConditional %true %14 %12 %14 = OpLabel OpBranch %12 -%15 = OpLabel -OpBranch %12 %12 = OpLabel -%16 = OpPhi %uint %uint_0 %11 %uint_0 %14 %uint_1 %15 -OpStore %2 %16 +%15 = OpPhi %uint %uint_0 %11 %uint_0 %13 %uint_1 %14 +OpStore %2 %15 OpReturn OpFunctionEnd )"; diff --git a/3rdparty/spirv-tools/test/opt/ir_context_test.cpp b/3rdparty/spirv-tools/test/opt/ir_context_test.cpp index 4e2f5b2c4..d5710fc46 100644 --- a/3rdparty/spirv-tools/test/opt/ir_context_test.cpp +++ b/3rdparty/spirv-tools/test/opt/ir_context_test.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "source/opt/ir_context.h" + #include #include #include @@ -19,7 +21,6 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "source/opt/ir_context.h" #include "source/opt/pass.h" #include "test/opt/pass_fixture.h" #include "test/opt/pass_utils.h" @@ -664,6 +665,90 @@ OpFunctionEnd)"; EXPECT_EQ(next_id_bound, 0); EXPECT_EQ(current_bound, context->module()->id_bound()); } + +TEST_F(IRContextTest, CfgAndDomAnalysis) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd)"; + + std::unique_ptr ctx = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + // Building the dominator analysis should build the CFG. + ASSERT_TRUE(ctx->module()->begin() != ctx->module()->end()); + ctx->GetDominatorAnalysis(&*ctx->module()->begin()); + + EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisCFG)); + EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDominatorAnalysis)); + + // Invalidating the CFG analysis should invalidate the dominator analysis. + ctx->InvalidateAnalyses(IRContext::kAnalysisCFG); + EXPECT_FALSE(ctx->AreAnalysesValid(IRContext::kAnalysisCFG)); + EXPECT_FALSE(ctx->AreAnalysesValid(IRContext::kAnalysisDominatorAnalysis)); +} + +TEST_F(IRContextTest, AsanErrorTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %11 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + %11 = OpLoad %6 %8 + OpBranch %20 + %20 = OpLabel + %21 = OpPhi %6 %11 %5 + OpStore %10 %21 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule( + env, consumer, shader, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + opt::Function* fun = + context->cfg()->block(5)->GetParent(); // Computes the CFG analysis + opt::DominatorAnalysis* dom = nullptr; + dom = context->GetDominatorAnalysis(fun); // Computes the dominator analysis, + // which depends on the CFG + // analysis + context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisDominatorAnalysis); // Invalidates the + // CFG analysis + dom = context->GetDominatorAnalysis( + fun); // Recompute the CFG analysis because the Dominator tree uses it. + auto bb = dom->ImmediateDominator(5); + std::cout + << bb->id(); // Make sure asan does not complain about use after free. +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/opt/type_manager_test.cpp b/3rdparty/spirv-tools/test/opt/type_manager_test.cpp index 267d98c3f..743d0b616 100644 --- a/3rdparty/spirv-tools/test/opt/type_manager_test.cpp +++ b/3rdparty/spirv-tools/test/opt/type_manager_test.cpp @@ -156,7 +156,8 @@ std::vector> GenerateAllTypes() { types.emplace_back(new ReserveId()); types.emplace_back(new Queue()); - // Pipe, Forward Pointer, PipeStorage, NamedBarrier, AccelerationStructureNV + // Pipe, Forward Pointer, PipeStorage, NamedBarrier, AccelerationStructureNV, + // CooperativeMatrixNV types.emplace_back(new Pipe(SpvAccessQualifierReadWrite)); types.emplace_back(new Pipe(SpvAccessQualifierReadOnly)); types.emplace_back(new ForwardPointer(1, SpvStorageClassInput)); @@ -165,6 +166,7 @@ std::vector> GenerateAllTypes() { types.emplace_back(new PipeStorage()); types.emplace_back(new NamedBarrier()); types.emplace_back(new AccelerationStructureNV()); + types.emplace_back(new CooperativeMatrixNV(f32, 24, 24, 24)); return types; } @@ -214,6 +216,7 @@ TEST(TypeManager, TypeStrings) { %arr_spec_const_with_id = OpTypeArray %s32 %spec_const_with_id %arr_long_constant = OpTypeArray %s32 %long_constant %arr_spec_const_op = OpTypeArray %s32 %spec_const_op + %cm = OpTypeCooperativeMatrixNV %f64 %id4 %id4 %id4 )"; std::vector> type_id_strs = { @@ -251,6 +254,7 @@ TEST(TypeManager, TypeStrings) { {36, "[sint32, id(1), words(1,99,42)]"}, {37, "[sint32, id(33), words(0,705032704,1)]"}, {38, "[sint32, id(34), words(2,34)]"}, + {39, ""}, }; std::unique_ptr context = @@ -1060,6 +1064,7 @@ TEST(TypeManager, GetTypeInstructionAllTypes) { ; CHECK: OpTypePipeStorage ; CHECK: OpTypeNamedBarrier ; CHECK: OpTypeAccelerationStructureNV +; CHECK: OpTypeCooperativeMatrixNV [[f32]] [[uint24]] [[uint24]] [[uint24]] OpCapability Shader OpCapability Int64 OpCapability Linkage diff --git a/3rdparty/spirv-tools/test/reduce/CMakeLists.txt b/3rdparty/spirv-tools/test/reduce/CMakeLists.txt index 2d3b37834..b19bba453 100644 --- a/3rdparty/spirv-tools/test/reduce/CMakeLists.txt +++ b/3rdparty/spirv-tools/test/reduce/CMakeLists.txt @@ -23,8 +23,6 @@ add_spvtools_unittest(TARGET reduce reducer_test.cpp remove_block_test.cpp remove_function_test.cpp - remove_opname_instruction_test.cpp - remove_relaxed_precision_decoration_test.cpp remove_selection_test.cpp remove_unreferenced_instruction_test.cpp structured_loop_to_selection_test.cpp diff --git a/3rdparty/spirv-tools/test/reduce/reducer_test.cpp b/3rdparty/spirv-tools/test/reduce/reducer_test.cpp index 8787733c9..a650d3bec 100644 --- a/3rdparty/spirv-tools/test/reduce/reducer_test.cpp +++ b/3rdparty/spirv-tools/test/reduce/reducer_test.cpp @@ -14,8 +14,8 @@ #include "source/reduce/reducer.h" +#include "source/opt/build_module.h" #include "source/reduce/operand_to_const_reduction_opportunity_finder.h" -#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h" #include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h" #include "test/reduce/reduce_test_util.h" @@ -23,6 +23,12 @@ namespace spvtools { namespace reduce { namespace { +using opt::BasicBlock; +using opt::IRContext; + +const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3; +const MessageConsumer kMessageConsumer = CLIMessageConsumer; + // This changes its mind each time IsInteresting is invoked as to whether the // binary is interesting, until some limit is reached after which the binary is // always deemed interesting. This is useful to test that reduction passes @@ -55,6 +61,8 @@ class PingPongInteresting { }; TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) { + // Check that ExprToConstant and RemoveUnreferenced work together; once some + // ID uses have been changed to constants, those IDs can be removed. std::string original = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -149,15 +157,6 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) { OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %60 OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - OpName %4 "main" - OpName %16 "buf2" - OpMemberName %16 0 "i" - OpName %18 "" - OpName %25 "buf1" - OpMemberName %25 0 "f" - OpName %27 "" - OpName %60 "_GLF_color" OpMemberDecorate %16 0 Offset 0 OpDecorate %16 Block OpDecorate %18 DescriptorSet 0 @@ -174,14 +173,12 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) { %16 = OpTypeStruct %6 %17 = OpTypePointer Uniform %16 %18 = OpVariable %17 Uniform - %19 = OpTypePointer Uniform %6 %22 = OpTypeBool %100 = OpConstantTrue %22 %24 = OpTypeFloat 32 %25 = OpTypeStruct %24 %26 = OpTypePointer Uniform %25 %27 = OpVariable %26 Uniform - %28 = OpTypePointer Uniform %24 %31 = OpConstant %24 2 %56 = OpConstant %6 1 %58 = OpTypeVector %24 4 @@ -209,8 +206,7 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) { OpFunctionEnd )"; - spv_target_env env = SPV_ENV_UNIVERSAL_1_3; - Reducer reducer(env); + Reducer reducer(kEnv); PingPongInteresting ping_pong_interesting(10); reducer.SetMessageConsumer(NopDiagnostic); reducer.SetInterestingnessFunction( @@ -218,12 +214,13 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) { return ping_pong_interesting.IsInteresting(binary); }); reducer.AddReductionPass( - MakeUnique()); + MakeUnique( + false)); reducer.AddReductionPass( - MakeUnique()); + MakeUnique()); std::vector binary_in; - SpirvTools t(env); + SpirvTools t(kEnv); ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption)); std::vector binary_out; @@ -237,71 +234,169 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) { ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete); - CheckEqual(env, expected, binary_out); + CheckEqual(kEnv, expected, binary_out); } -TEST(ReducerTest, RemoveOpnameAndRemoveUnreferenced) { - const std::string original = R"( +bool InterestingWhileOpcodeExists(const std::vector& binary, + uint32_t opcode, uint32_t count, bool dump) { + if (dump) { + std::stringstream ss; + ss << "temp_" << count << ".spv"; + DumpShader(binary, ss.str().c_str()); + } + + std::unique_ptr context = + BuildModule(kEnv, kMessageConsumer, binary.data(), binary.size()); + assert(context); + bool interesting = false; + for (auto& function : *context->module()) { + context->cfg()->ForEachBlockInPostOrder( + &*function.begin(), [opcode, &interesting](BasicBlock* block) -> void { + for (auto& inst : *block) { + if (inst.opcode() == opcode) { + interesting = true; + break; + } + } + }); + if (interesting) { + break; + } + } + return interesting; +} + +bool InterestingWhileIMulReachable(const std::vector& binary, + uint32_t count) { + return InterestingWhileOpcodeExists(binary, SpvOpIMul, count, false); +} + +bool InterestingWhileSDivReachable(const std::vector& binary, + uint32_t count) { + return InterestingWhileOpcodeExists(binary, SpvOpSDiv, count, false); +} + +// The shader below was derived from the following GLSL, and optimized. +// #version 310 es +// precision highp float; +// layout(location = 0) out vec4 _GLF_color; +// int foo() { +// int x = 1; +// int y; +// x = y / x; // SDiv +// return x; +// } +// void main() { +// int c; +// while (bool(c)) { +// do { +// if (bool(c)) { +// if (bool(c)) { +// ++c; +// } else { +// _GLF_color.x = float(c*c); // IMul +// } +// return; +// } +// } while(bool(foo())); +// return; +// } +// } +const std::string kShaderWithLoopsDivAndMul = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %2 "main" - OpExecutionMode %2 OriginUpperLeft + OpEntryPoint Fragment %4 "main" %49 + OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 - OpName %2 "main" - OpName %3 "a" - OpName %4 "this-name-counts-as-usage-for-load-instruction" - %5 = OpTypeVoid - %6 = OpTypeFunction %5 - %7 = OpTypeFloat 32 - %8 = OpTypePointer Function %7 - %9 = OpConstant %7 1 - %2 = OpFunction %5 None %6 - %10 = OpLabel - %3 = OpVariable %8 Function - %4 = OpLoad %7 %3 - OpStore %3 %9 + OpName %4 "main" + OpName %49 "_GLF_color" + OpDecorate %49 Location 0 + OpDecorate %52 RelaxedPrecision + OpDecorate %77 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %12 = OpConstant %6 1 + %27 = OpTypeBool + %28 = OpTypeInt 32 0 + %29 = OpConstant %28 0 + %46 = OpTypeFloat 32 + %47 = OpTypeVector %46 4 + %48 = OpTypePointer Output %47 + %49 = OpVariable %48 Output + %54 = OpTypePointer Output %46 + %64 = OpConstantFalse %27 + %67 = OpConstantTrue %27 + %81 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %61 + %61 = OpLabel + OpLoopMerge %60 %63 None + OpBranch %20 + %20 = OpLabel + %30 = OpINotEqual %27 %81 %29 + OpLoopMerge %22 %23 None + OpBranchConditional %30 %21 %22 + %21 = OpLabel + OpBranch %31 + %31 = OpLabel + OpLoopMerge %33 %38 None + OpBranch %32 + %32 = OpLabel + OpSelectionMerge %38 None + OpBranchConditional %30 %37 %38 + %37 = OpLabel + OpSelectionMerge %42 None + OpBranchConditional %30 %41 %45 + %41 = OpLabel + OpBranch %42 + %45 = OpLabel + %52 = OpIMul %6 %81 %81 + %53 = OpConvertSToF %46 %52 + %55 = OpAccessChain %54 %49 %29 + OpStore %55 %53 + OpBranch %42 + %42 = OpLabel + OpBranch %33 + %38 = OpLabel + %77 = OpSDiv %6 %81 %12 + %58 = OpINotEqual %27 %77 %29 + OpBranchConditional %58 %31 %33 + %33 = OpLabel + %86 = OpPhi %27 %67 %42 %64 %38 + OpSelectionMerge %68 None + OpBranchConditional %86 %22 %68 + %68 = OpLabel + OpBranch %22 + %23 = OpLabel + OpBranch %20 + %22 = OpLabel + %90 = OpPhi %27 %64 %20 %86 %33 %67 %68 + OpSelectionMerge %70 None + OpBranchConditional %90 %60 %70 + %70 = OpLabel + OpBranch %60 + %63 = OpLabel + OpBranch %61 + %60 = OpLabel OpReturn OpFunctionEnd )"; - const std::string expected = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %2 "main" - OpExecutionMode %2 OriginUpperLeft - OpSource ESSL 310 - %5 = OpTypeVoid - %6 = OpTypeFunction %5 - %7 = OpTypeFloat 32 - %8 = OpTypePointer Function %7 - %9 = OpConstant %7 1 - %2 = OpFunction %5 None %6 - %10 = OpLabel - OpReturn - OpFunctionEnd - )"; +TEST(ReducerTest, ShaderReduceWhileMulReachable) { + Reducer reducer(kEnv); - spv_target_env env = SPV_ENV_UNIVERSAL_1_3; - Reducer reducer(env); - // Make ping-pong interesting very quickly, as there are not many - // opportunities. - PingPongInteresting ping_pong_interesting(1); - reducer.SetMessageConsumer(NopDiagnostic); - reducer.SetInterestingnessFunction( - [&](const std::vector& binary, uint32_t) -> bool { - return ping_pong_interesting.IsInteresting(binary); - }); - reducer.AddReductionPass( - MakeUnique()); - reducer.AddReductionPass( - MakeUnique()); + reducer.SetInterestingnessFunction(InterestingWhileIMulReachable); + reducer.AddDefaultReductionPasses(); + reducer.SetMessageConsumer(kMessageConsumer); std::vector binary_in; - SpirvTools t(env); + SpirvTools t(kEnv); - ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption)); + ASSERT_TRUE( + t.Assemble(kShaderWithLoopsDivAndMul, &binary_in, kReduceAssembleOption)); std::vector binary_out; spvtools::ReducerOptions reducer_options; reducer_options.set_step_limit(500); @@ -312,8 +407,30 @@ TEST(ReducerTest, RemoveOpnameAndRemoveUnreferenced) { std::move(binary_in), &binary_out, reducer_options, validator_options); ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete); +} - CheckEqual(env, expected, binary_out); +TEST(ReducerTest, ShaderReduceWhileDivReachable) { + Reducer reducer(kEnv); + + reducer.SetInterestingnessFunction(InterestingWhileSDivReachable); + reducer.AddDefaultReductionPasses(); + reducer.SetMessageConsumer(kMessageConsumer); + + std::vector binary_in; + SpirvTools t(kEnv); + + ASSERT_TRUE( + t.Assemble(kShaderWithLoopsDivAndMul, &binary_in, kReduceAssembleOption)); + std::vector binary_out; + spvtools::ReducerOptions reducer_options; + reducer_options.set_step_limit(500); + reducer_options.set_fail_on_validation_error(true); + spvtools::ValidatorOptions validator_options; + + Reducer::ReductionResultStatus status = reducer.Run( + std::move(binary_in), &binary_out, reducer_options, validator_options); + + ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete); } } // namespace diff --git a/3rdparty/spirv-tools/test/reduce/remove_opname_instruction_test.cpp b/3rdparty/spirv-tools/test/reduce/remove_opname_instruction_test.cpp deleted file mode 100644 index 9d40cfcfb..000000000 --- a/3rdparty/spirv-tools/test/reduce/remove_opname_instruction_test.cpp +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (c) 2018 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/reduce/remove_opname_instruction_reduction_opportunity_finder.h" - -#include "source/opt/build_module.h" -#include "source/reduce/reduction_opportunity.h" -#include "source/reduce/reduction_pass.h" -#include "test/reduce/reduce_test_util.h" - -namespace spvtools { -namespace reduce { -namespace { - -TEST(RemoveOpnameInstructionReductionPassTest, NothingToRemove) { - const std::string source = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - %2 = OpTypeVoid - %3 = OpTypeFunction %2 - %4 = OpFunction %2 None %3 - %5 = OpLabel - OpReturn - OpFunctionEnd - )"; - - const auto env = SPV_ENV_UNIVERSAL_1_3; - const auto consumer = nullptr; - const auto context = - BuildModule(env, consumer, source, kReduceAssembleOption); - const auto ops = RemoveOpNameInstructionReductionOpportunityFinder() - .GetAvailableOpportunities(context.get()); - ASSERT_EQ(0, ops.size()); -} - -TEST(RemoveOpnameInstructionReductionPassTest, RemoveSingleOpName) { - const std::string prologue = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - )"; - - const std::string epilogue = R"( - %2 = OpTypeVoid - %3 = OpTypeFunction %2 - %4 = OpFunction %2 None %3 - %5 = OpLabel - OpReturn - OpFunctionEnd - )"; - - const std::string original = prologue + R"( - OpName %4 "main" - )" + epilogue; - - const std::string expected = prologue + epilogue; - - const auto env = SPV_ENV_UNIVERSAL_1_3; - const auto consumer = nullptr; - const auto context = - BuildModule(env, consumer, original, kReduceAssembleOption); - const auto ops = RemoveOpNameInstructionReductionOpportunityFinder() - .GetAvailableOpportunities(context.get()); - ASSERT_EQ(1, ops.size()); - ASSERT_TRUE(ops[0]->PreconditionHolds()); - ops[0]->TryToApply(); - - CheckEqual(env, expected, context.get()); -} - -TEST(RemoveOpnameInstructionReductionPassTest, TryApplyRemovesAllOpName) { - const std::string prologue = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - )"; - - const std::string epilogue = R"( - %2 = OpTypeVoid - %3 = OpTypeFunction %2 - %6 = OpTypeFloat 32 - %7 = OpTypePointer Function %6 - %9 = OpConstant %6 1 - %4 = OpFunction %2 None %3 - %5 = OpLabel - %8 = OpVariable %7 Function - %10 = OpVariable %7 Function - %11 = OpVariable %7 Function - %12 = OpVariable %7 Function - OpStore %8 %9 - OpStore %10 %9 - OpStore %11 %9 - OpStore %12 %9 - OpReturn - OpFunctionEnd - )"; - - const std::string original = prologue + R"( - OpName %4 "main" - OpName %8 "a" - OpName %10 "b" - OpName %11 "c" - OpName %12 "d" - )" + epilogue; - - const std::string expected = prologue + epilogue; - - const auto env = SPV_ENV_UNIVERSAL_1_3; - - { - // Check the right number of opportunities is detected - const auto consumer = nullptr; - const auto context = - BuildModule(env, consumer, original, kReduceAssembleOption); - const auto ops = RemoveOpNameInstructionReductionOpportunityFinder() - .GetAvailableOpportunities(context.get()); - ASSERT_EQ(5, ops.size()); - } - - { - // The reduction should remove all OpName - std::vector binary; - SpirvTools t(env); - ASSERT_TRUE(t.Assemble(original, &binary, kReduceAssembleOption)); - auto reduced_binary = - ReductionPass(env, - spvtools::MakeUnique< - RemoveOpNameInstructionReductionOpportunityFinder>()) - .TryApplyReduction(binary); - CheckEqual(env, expected, reduced_binary); - } -} - -TEST(RemoveOpnameInstructionReductionPassTest, - TryApplyRemovesAllOpNameAndOpMemberName) { - const std::string prologue = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - )"; - - const std::string epilogue = R"( - %2 = OpTypeVoid - %3 = OpTypeFunction %2 - %6 = OpTypeFloat 32 - %7 = OpTypeInt 32 1 - %8 = OpTypeVector %6 3 - %9 = OpTypeStruct %6 %7 %8 - %10 = OpTypePointer Function %9 - %12 = OpConstant %7 0 - %13 = OpConstant %6 1 - %14 = OpTypePointer Function %6 - %4 = OpFunction %2 None %3 - %5 = OpLabel - %11 = OpVariable %10 Function - %15 = OpAccessChain %14 %11 %12 - OpStore %15 %13 - OpReturn - OpFunctionEnd - )"; - - const std::string original = prologue + R"( - OpName %4 "main" - OpName %9 "S" - OpMemberName %9 0 "f" - OpMemberName %9 1 "i" - OpMemberName %9 2 "v" - OpName %11 "s" - )" + epilogue; - - const std::string expected = prologue + epilogue; - - const auto env = SPV_ENV_UNIVERSAL_1_3; - - { - // Check the right number of opportunities is detected - const auto consumer = nullptr; - const auto context = - BuildModule(env, consumer, original, kReduceAssembleOption); - const auto ops = RemoveOpNameInstructionReductionOpportunityFinder() - .GetAvailableOpportunities(context.get()); - ASSERT_EQ(6, ops.size()); - } - - { - // The reduction should remove all OpName - std::vector binary; - SpirvTools t(env); - ASSERT_TRUE(t.Assemble(original, &binary, kReduceAssembleOption)); - auto reduced_binary = - ReductionPass(env, - spvtools::MakeUnique< - RemoveOpNameInstructionReductionOpportunityFinder>()) - .TryApplyReduction(binary); - CheckEqual(env, expected, reduced_binary); - } -} - -} // namespace -} // namespace reduce -} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/reduce/remove_relaxed_precision_decoration_test.cpp b/3rdparty/spirv-tools/test/reduce/remove_relaxed_precision_decoration_test.cpp deleted file mode 100644 index f9ff081cc..000000000 --- a/3rdparty/spirv-tools/test/reduce/remove_relaxed_precision_decoration_test.cpp +++ /dev/null @@ -1,177 +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/reduce/remove_relaxed_precision_decoration_opportunity_finder.h" - -#include "source/opt/build_module.h" -#include "source/reduce/reduction_opportunity.h" -#include "source/reduce/reduction_pass.h" -#include "test/reduce/reduce_test_util.h" - -namespace spvtools { -namespace reduce { -namespace { - -TEST(RemoveRelaxedPrecisionDecorationTest, NothingToRemove) { - const std::string source = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - %2 = OpTypeVoid - %3 = OpTypeFunction %2 - %4 = OpFunction %2 None %3 - %5 = OpLabel - OpReturn - OpFunctionEnd - )"; - - const auto env = SPV_ENV_UNIVERSAL_1_3; - const auto consumer = nullptr; - const auto context = - BuildModule(env, consumer, source, kReduceAssembleOption); - const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder() - .GetAvailableOpportunities(context.get()); - ASSERT_EQ(0, ops.size()); -} - -TEST(RemoveRelaxedPrecisionDecorationTest, RemoveDecorations) { - const std::string source = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - OpName %4 "main" - OpName %8 "f" - OpName %12 "i" - OpName %16 "v" - OpName %19 "S" - OpMemberName %19 0 "a" - OpMemberName %19 1 "b" - OpMemberName %19 2 "c" - OpName %21 "s" - OpDecorate %8 RelaxedPrecision - OpDecorate %12 RelaxedPrecision - OpDecorate %16 RelaxedPrecision - OpDecorate %17 RelaxedPrecision - OpDecorate %18 RelaxedPrecision - OpMemberDecorate %19 0 RelaxedPrecision - OpMemberDecorate %19 1 RelaxedPrecision - OpMemberDecorate %19 2 RelaxedPrecision - OpDecorate %22 RelaxedPrecision - OpDecorate %23 RelaxedPrecision - OpDecorate %24 RelaxedPrecision - %2 = OpTypeVoid - %3 = OpTypeFunction %2 - %6 = OpTypeFloat 32 - %7 = OpTypePointer Function %6 - %9 = OpConstant %6 2 - %10 = OpTypeInt 32 1 - %11 = OpTypePointer Function %10 - %13 = OpConstant %10 22 - %14 = OpTypeVector %6 2 - %15 = OpTypePointer Function %14 - %19 = OpTypeStruct %10 %6 %14 - %20 = OpTypePointer Function %19 - %4 = OpFunction %2 None %3 - %5 = OpLabel - %8 = OpVariable %7 Function - %12 = OpVariable %11 Function - %16 = OpVariable %15 Function - %21 = OpVariable %20 Function - OpStore %8 %9 - OpStore %12 %13 - %17 = OpLoad %6 %8 - %18 = OpCompositeConstruct %14 %17 %17 - OpStore %16 %18 - %22 = OpLoad %10 %12 - %23 = OpLoad %6 %8 - %24 = OpLoad %14 %16 - %25 = OpCompositeConstruct %19 %22 %23 %24 - OpStore %21 %25 - OpReturn - OpFunctionEnd - )"; - - const auto env = SPV_ENV_UNIVERSAL_1_3; - const auto consumer = nullptr; - const auto context = - BuildModule(env, consumer, source, kReduceAssembleOption); - const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder() - .GetAvailableOpportunities(context.get()); - ASSERT_EQ(11, ops.size()); - - for (auto& op : ops) { - ASSERT_TRUE(op->PreconditionHolds()); - op->TryToApply(); - } - - const std::string expected = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - OpName %4 "main" - OpName %8 "f" - OpName %12 "i" - OpName %16 "v" - OpName %19 "S" - OpMemberName %19 0 "a" - OpMemberName %19 1 "b" - OpMemberName %19 2 "c" - OpName %21 "s" - %2 = OpTypeVoid - %3 = OpTypeFunction %2 - %6 = OpTypeFloat 32 - %7 = OpTypePointer Function %6 - %9 = OpConstant %6 2 - %10 = OpTypeInt 32 1 - %11 = OpTypePointer Function %10 - %13 = OpConstant %10 22 - %14 = OpTypeVector %6 2 - %15 = OpTypePointer Function %14 - %19 = OpTypeStruct %10 %6 %14 - %20 = OpTypePointer Function %19 - %4 = OpFunction %2 None %3 - %5 = OpLabel - %8 = OpVariable %7 Function - %12 = OpVariable %11 Function - %16 = OpVariable %15 Function - %21 = OpVariable %20 Function - OpStore %8 %9 - OpStore %12 %13 - %17 = OpLoad %6 %8 - %18 = OpCompositeConstruct %14 %17 %17 - OpStore %16 %18 - %22 = OpLoad %10 %12 - %23 = OpLoad %6 %8 - %24 = OpLoad %14 %16 - %25 = OpCompositeConstruct %19 %22 %23 %24 - OpStore %21 %25 - OpReturn - OpFunctionEnd - )"; - - CheckEqual(env, expected, context.get()); -} - -} // namespace -} // namespace reduce -} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/reduce/remove_unreferenced_instruction_test.cpp b/3rdparty/spirv-tools/test/reduce/remove_unreferenced_instruction_test.cpp index 0babf781a..3caf88cfe 100644 --- a/3rdparty/spirv-tools/test/reduce/remove_unreferenced_instruction_test.cpp +++ b/3rdparty/spirv-tools/test/reduce/remove_unreferenced_instruction_test.cpp @@ -23,19 +23,26 @@ namespace spvtools { namespace reduce { namespace { +const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3; + TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) { - const std::string prologue = R"( + // A module with some unused instructions, including some unused OpStore + // instructions. + + RemoveUnreferencedInstructionReductionOpportunityFinder finder(true); + + const std::string original = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - OpName %4 "main" - OpName %8 "a" - OpName %10 "b" - OpName %12 "c" - OpName %14 "d" + OpSource ESSL 310 ; 0 + OpName %4 "main" ; 1 + OpName %8 "a" ; 2 + OpName %10 "b" ; 3 + OpName %12 "c" ; 4 + OpName %14 "d" ; 5 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 @@ -49,51 +56,323 @@ TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) { %10 = OpVariable %7 Function %12 = OpVariable %7 Function %14 = OpVariable %7 Function + OpStore %8 %9 ; 6 + OpStore %10 %11 ; 7 + OpStore %12 %13 ; 8 + %15 = OpLoad %6 %8 + OpStore %14 %15 ; 9 + OpReturn + OpFunctionEnd + )"; - const std::string epilogue = R"( + const MessageConsumer consumer = nullptr; + const auto context = + BuildModule(kEnv, consumer, original, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = finder.GetAvailableOpportunities(context.get()); + + ASSERT_EQ(10, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + const std::string step_2 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 ; 0 + %11 = OpConstant %6 20 ; 1 + %13 = OpConstant %6 30 ; 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function ; 3 + %12 = OpVariable %7 Function ; 4 + %14 = OpVariable %7 Function ; 5 + %15 = OpLoad %6 %8 ; 6 OpReturn OpFunctionEnd )"; - const std::string original = prologue + R"( - OpStore %8 %9 - OpStore %10 %11 - OpStore %12 %13 - %15 = OpLoad %6 %8 - OpStore %14 %15 - )" + epilogue; + CheckEqual(kEnv, step_2, context.get()); - const std::string expected_after_2 = prologue + R"( - OpStore %12 %13 - %15 = OpLoad %6 %8 - OpStore %14 %15 - )" + epilogue; + ops = finder.GetAvailableOpportunities(context.get()); - const std::string expected_after_4 = prologue + R"( - %15 = OpLoad %6 %8 - )" + epilogue; + ASSERT_EQ(7, ops.size()); - const auto env = SPV_ENV_UNIVERSAL_1_3; - const auto consumer = nullptr; - const auto context = - BuildModule(env, consumer, original, kReduceAssembleOption); - const auto ops = RemoveUnreferencedInstructionReductionOpportunityFinder() - .GetAvailableOpportunities(context.get()); - ASSERT_EQ(4, ops.size()); - ASSERT_TRUE(ops[0]->PreconditionHolds()); - ops[0]->TryToApply(); - ASSERT_TRUE(ops[1]->PreconditionHolds()); - ops[1]->TryToApply(); + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } - CheckEqual(env, expected_after_2, context.get()); + const std::string step_3 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function ; 0 + OpReturn + OpFunctionEnd + )"; - ASSERT_TRUE(ops[2]->PreconditionHolds()); - ops[2]->TryToApply(); - ASSERT_TRUE(ops[3]->PreconditionHolds()); - ops[3]->TryToApply(); + CheckEqual(kEnv, step_3, context.get()); - CheckEqual(env, expected_after_4, context.get()); + ops = finder.GetAvailableOpportunities(context.get()); + + ASSERT_EQ(1, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + const std::string step_4 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 ; 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, step_4, context.get()); + + ops = finder.GetAvailableOpportunities(context.get()); + + ASSERT_EQ(1, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + const std::string step_5 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 ; 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, step_5, context.get()); + + ops = finder.GetAvailableOpportunities(context.get()); + + ASSERT_EQ(1, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + const std::string step_6 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, step_6, context.get()); + + ops = finder.GetAvailableOpportunities(context.get()); + + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveUnreferencedInstructionReductionPassTest, Referenced) { + // A module with some unused global variables, constants, and types. Some will + // not be removed initially because of the OpDecorate instructions. + + RemoveUnreferencedInstructionReductionOpportunityFinder finder(true); + + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 ; 1 + OpName %4 "main" ; 2 + OpName %12 "a" ; 3 + OpDecorate %12 RelaxedPrecision ; 4 + OpDecorate %13 RelaxedPrecision ; 5 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 ; 6 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Private %10 + %12 = OpVariable %11 Private + %13 = OpConstant %10 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = finder.GetAvailableOpportunities(context.get()); + + ASSERT_EQ(6, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool ; 1 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Private %10 + %12 = OpVariable %11 Private ; 2 + %13 = OpConstant %10 1 ; 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, after, context.get()); + + ops = finder.GetAvailableOpportunities(context.get()); + + ASSERT_EQ(3, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + std::string after_2 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Private %10 ; 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, after_2, context.get()); + + ops = finder.GetAvailableOpportunities(context.get()); + + ASSERT_EQ(1, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + std::string after_3 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 1 ; 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, after_3, context.get()); + + ops = finder.GetAvailableOpportunities(context.get()); + + ASSERT_EQ(1, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + std::string after_4 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, after_4, context.get()); + + ops = finder.GetAvailableOpportunities(context.get()); + + ASSERT_EQ(0, ops.size()); } } // namespace diff --git a/3rdparty/spirv-tools/test/val/val_cfg_test.cpp b/3rdparty/spirv-tools/test/val/val_cfg_test.cpp index 547eb578f..f06f36c14 100644 --- a/3rdparty/spirv-tools/test/val/val_cfg_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_cfg_test.cpp @@ -3026,6 +3026,7 @@ OpMemoryModel Logical GLSL450 %undef = OpUndef %bool %func = OpFunction %void None %void_fn %entry = OpLabel +OpSelectionMerge %block None OpBranchConditional %undef %block %unreachable %block = OpLabel OpReturn @@ -3049,6 +3050,7 @@ OpMemoryModel Logical GLSL450 %undef = OpUndef %int %func = OpFunction %void None %void_fn %entry = OpLabel +OpSelectionMerge %block1 None OpSwitch %undef %block1 0 %unreachable 1 %block2 %block1 = OpLabel OpReturn @@ -3748,7 +3750,355 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -/// TODO(umar): Nested CFG constructs +TEST_F(ValidateCFG, MissingMergeConditionalBranchBad) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranchConditional %undef %then %else +%then = OpLabel +OpReturn +%else = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured")); +} + +TEST_F(ValidateCFG, MissingMergeSwitchBad) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%undef = OpUndef %int +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSwitch %undef %then 0 %else +%then = OpLabel +OpReturn +%else = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured")); +} + +TEST_F(ValidateCFG, MissingMergeSwitchBad2) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%undef = OpUndef %int +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSwitch %undef %then 0 %then 1 %then 2 %else +%then = OpLabel +OpReturn +%else = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured")); +} + +TEST_F(ValidateCFG, MissingMergeOneBranchToMergeGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %b3 None +OpBranchConditional %undef %b1 %b2 +%b1 = OpLabel +OpBranchConditional %undef %b2 %b3 +%b2 = OpLabel +OpBranch %b3 +%b3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeSameTargetConditionalBranchGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranchConditional %undef %then %then +%then = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeOneTargetSwitchGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%undef = OpUndef %int +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSwitch %undef %then 0 %then 1 %then +%then = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeOneUnseenTargetSwitchGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%undef_int = OpUndef %int +%bool = OpTypeBool +%undef_bool = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %merge None +OpBranchConditional %undef_bool %merge %b1 +%b1 = OpLabel +OpSwitch %undef_int %b2 0 %b2 1 %merge 2 %b2 +%b2 = OpLabel +OpBranch %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeLoopBreakGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %exit %continue None +OpBranch %body +%body = OpLabel +OpBranchConditional %undef %body2 %exit +%body2 = OpLabel +OpBranch %continue +%continue = OpLabel +OpBranch %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeLoopContinueGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %exit %continue None +OpBranch %body +%body = OpLabel +OpBranchConditional %undef %body2 %continue +%body2 = OpLabel +OpBranch %continue +%continue = OpLabel +OpBranch %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeSwitchBreakGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %merge None +OpSwitch %int_0 %merge 1 %b1 +%b1 = OpLabel +OpBranchConditional %undef %merge %b2 +%b2 = OpLabel +OpBranch %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeSwitchFallThroughGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %merge None +OpSwitch %int_0 %b1 1 %b2 +%b1 = OpLabel +OpBranchConditional %undef %b3 %b2 +%b2 = OpLabel +OpBranch %merge +%b3 = OpLabel +OpBranch %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeInALoopBad) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %exit %continue None +OpBranch %body +%body = OpLabel +OpBranchConditional %undef %b1 %b2 +%b1 = OpLabel +OpBranch %exit +%b2 = OpLabel +OpBranch %continue +%continue = OpLabel +OpBranch %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured")); +} + +TEST_F(ValidateCFG, MissingMergeCrissCrossBad) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %merge None +OpBranchConditional %undef %b1 %b2 +%b1 = OpLabel +OpBranchConditional %undef %b3 %b4 +%b2 = OpLabel +OpBranchConditional %undef %b3 %b4 +%b3 = OpLabel +OpBranch %merge +%b4 = OpLabel +OpBranch %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured")); +} } // namespace } // namespace val diff --git a/3rdparty/spirv-tools/test/val/val_misc_test.cpp b/3rdparty/spirv-tools/test/val/val_misc_test.cpp index d266653e3..dd9fff96e 100644 --- a/3rdparty/spirv-tools/test/val/val_misc_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_misc_test.cpp @@ -144,7 +144,8 @@ OpFunctionEnd)"; EXPECT_THAT(getDiagnosticString(), HasSubstr("vector of two components")); } -TEST_F(ValidateMisc, ShaderClockExecutionScope) { +// #2952: disabled until scope discussion is resolved. +TEST_F(ValidateMisc, DISABLED_ShaderClockExecutionScope) { const std::string spirv = ShaderClockSpriv + R"( %3 = OpTypeFunction %void %ulong = OpTypeInt 64 0