diff --git a/3rdparty/spirv-tools/include/generated/build-version.inc b/3rdparty/spirv-tools/include/generated/build-version.inc index 14a992f21..d930a7090 100644 --- a/3rdparty/spirv-tools/include/generated/build-version.inc +++ b/3rdparty/spirv-tools/include/generated/build-version.inc @@ -1 +1 @@ -"v2020.4-dev", "SPIRV-Tools v2020.4-dev 7efb218fd1272e44be3bc1e891ac153da59e9871" +"v2020.4-dev", "SPIRV-Tools v2020.4-dev 38bba44706260cfb807097c58ca926c64c3a13d2" diff --git a/3rdparty/spirv-tools/include/generated/generators.inc b/3rdparty/spirv-tools/include/generated/generators.inc index be262f96c..0c62854b5 100644 --- a/3rdparty/spirv-tools/include/generated/generators.inc +++ b/3rdparty/spirv-tools/include/generated/generators.inc @@ -20,4 +20,5 @@ {19, "Clay", "Clay Shader Compiler", "Clay Clay Shader Compiler"}, {20, "W3C WebGPU Group", "WHLSL Shader Translator", "W3C WebGPU Group WHLSL Shader Translator"}, {21, "Google", "Clspv", "Google Clspv"}, -{22, "Google", "MLIR SPIR-V Serializer", "Google MLIR SPIR-V Serializer"}, \ No newline at end of file +{22, "Google", "MLIR SPIR-V Serializer", "Google MLIR SPIR-V Serializer"}, +{23, "Google", "Tint Compiler", "Google Tint Compiler"}, \ No newline at end of file diff --git a/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt b/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt index 7bd9ff2ef..9adb40091 100644 --- a/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt @@ -46,6 +46,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_add_equation_instructions.h fuzzer_pass_add_function_calls.h fuzzer_pass_add_global_variables.h + fuzzer_pass_add_image_sample_unused_components.h fuzzer_pass_add_loads.h fuzzer_pass_add_local_variables.h fuzzer_pass_add_no_contraction_decorations.h @@ -70,6 +71,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_permute_phi_operands.h fuzzer_pass_push_ids_through_variables.h fuzzer_pass_replace_linear_algebra_instructions.h + fuzzer_pass_replace_parameter_with_global.h fuzzer_pass_split_blocks.h fuzzer_pass_swap_commutable_operands.h fuzzer_pass_swap_conditional_branch_operands.h @@ -96,6 +98,7 @@ if(SPIRV_BUILD_FUZZER) transformation_add_function.h transformation_add_global_undef.h transformation_add_global_variable.h + transformation_add_image_sample_unused_components.h transformation_add_local_variable.h transformation_add_no_contraction_decoration.h transformation_add_parameter.h @@ -125,10 +128,12 @@ if(SPIRV_BUILD_FUZZER) transformation_permute_function_parameters.h transformation_permute_phi_operands.h transformation_push_id_through_variable.h + transformation_record_synonymous_constants.h transformation_replace_boolean_constant_with_constant_binary.h transformation_replace_constant_with_uniform.h transformation_replace_id_with_synonym.h transformation_replace_linear_algebra_instruction.h + transformation_replace_parameter_with_global.h transformation_set_function_control.h transformation_set_loop_control.h transformation_set_memory_operands_mask.h @@ -158,6 +163,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_add_equation_instructions.cpp fuzzer_pass_add_function_calls.cpp fuzzer_pass_add_global_variables.cpp + fuzzer_pass_add_image_sample_unused_components.cpp fuzzer_pass_add_loads.cpp fuzzer_pass_add_local_variables.cpp fuzzer_pass_add_no_contraction_decorations.cpp @@ -182,6 +188,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_permute_phi_operands.cpp fuzzer_pass_push_ids_through_variables.cpp fuzzer_pass_replace_linear_algebra_instructions.cpp + fuzzer_pass_replace_parameter_with_global.cpp fuzzer_pass_split_blocks.cpp fuzzer_pass_swap_commutable_operands.cpp fuzzer_pass_swap_conditional_branch_operands.cpp @@ -207,6 +214,7 @@ if(SPIRV_BUILD_FUZZER) transformation_add_function.cpp transformation_add_global_undef.cpp transformation_add_global_variable.cpp + transformation_add_image_sample_unused_components.cpp transformation_add_local_variable.cpp transformation_add_no_contraction_decoration.cpp transformation_add_parameter.cpp @@ -236,10 +244,12 @@ if(SPIRV_BUILD_FUZZER) transformation_permute_function_parameters.cpp transformation_permute_phi_operands.cpp transformation_push_id_through_variable.cpp + transformation_record_synonymous_constants.cpp transformation_replace_boolean_constant_with_constant_binary.cpp transformation_replace_constant_with_uniform.cpp transformation_replace_id_with_synonym.cpp transformation_replace_linear_algebra_instruction.cpp + transformation_replace_parameter_with_global.cpp transformation_set_function_control.cpp transformation_set_loop_control.cpp transformation_set_memory_operands_mask.cpp diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp index 6cb620228..663a0c7d7 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp @@ -30,6 +30,7 @@ #include "source/fuzz/fuzzer_pass_add_equation_instructions.h" #include "source/fuzz/fuzzer_pass_add_function_calls.h" #include "source/fuzz/fuzzer_pass_add_global_variables.h" +#include "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h" #include "source/fuzz/fuzzer_pass_add_loads.h" #include "source/fuzz/fuzzer_pass_add_local_variables.h" #include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h" @@ -53,6 +54,7 @@ #include "source/fuzz/fuzzer_pass_permute_phi_operands.h" #include "source/fuzz/fuzzer_pass_push_ids_through_variables.h" #include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h" +#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h" #include "source/fuzz/fuzzer_pass_split_blocks.h" #include "source/fuzz/fuzzer_pass_swap_commutable_operands.h" #include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h" @@ -224,6 +226,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( MaybeAddPass( &passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); MaybeAddPass(&passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); @@ -272,6 +277,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( MaybeAddPass( &passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); MaybeAddPass( &passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp index 9f119f6e4..e0c2c4243 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp @@ -34,6 +34,8 @@ const std::pair kChanceOfAddingDeadContinue = {5, 80}; const std::pair kChanceOfAddingEquationInstruction = {5, 90}; const std::pair kChanceOfAddingGlobalVariable = {20, 90}; +const std::pair kChanceOfAddingImageSampleUnusedComponents = + {20, 90}; const std::pair kChanceOfAddingLoad = {5, 50}; const std::pair kChanceOfAddingLocalVariable = {20, 90}; const std::pair kChanceOfAddingMatrixType = {20, 70}; @@ -54,6 +56,8 @@ const std::pair kChanceOfAdjustingSelectionControl = {20, const std::pair kChanceOfCallingFunction = {1, 10}; const std::pair kChanceOfChoosingStructTypeVsArrayType = { 20, 80}; +const std::pair kChanceOfChoosingWorkgroupStorageClass = { + 50, 50}; const std::pair kChanceOfConstructingComposite = {20, 50}; const std::pair kChanceOfCopyingObject = {20, 50}; const std::pair kChanceOfDonatingAdditionalModule = {5, 50}; @@ -72,6 +76,8 @@ const std::pair kChanceOfPushingIdThroughVariable = {5, 50}; const std::pair kChanceOfReplacingIdWithSynonym = {10, 90}; const std::pair kChanceOfReplacingLinearAlgebraInstructions = {10, 90}; +const std::pair kChanceOfReplacingParametersWithGlobals = { + 30, 70}; const std::pair kChanceOfSplittingBlock = {40, 95}; const std::pair kChanceOfSwappingConditionalBranchOperands = {10, 70}; @@ -135,6 +141,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, chance_of_adding_global_variable_ = ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable); chance_of_adding_load_ = ChooseBetweenMinAndMax(kChanceOfAddingLoad); + chance_of_adding_image_sample_unused_components_ = + ChooseBetweenMinAndMax(kChanceOfAddingImageSampleUnusedComponents); chance_of_adding_local_variable_ = ChooseBetweenMinAndMax(kChanceOfAddingLocalVariable); chance_of_adding_matrix_type_ = @@ -162,6 +170,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfCallingFunction); chance_of_choosing_struct_type_vs_array_type_ = ChooseBetweenMinAndMax(kChanceOfChoosingStructTypeVsArrayType); + chance_of_choosing_workgroup_storage_class_ = + ChooseBetweenMinAndMax(kChanceOfChoosingWorkgroupStorageClass); chance_of_constructing_composite_ = ChooseBetweenMinAndMax(kChanceOfConstructingComposite); chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject); @@ -190,6 +200,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym); chance_of_replacing_linear_algebra_instructions_ = ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions); + chance_of_replacing_parameters_with_globals_ = + ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals); chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock); chance_of_swapping_conditional_branch_operands_ = ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands); diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h index 682ba9a19..86dbc141e 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h @@ -128,6 +128,9 @@ class FuzzerContext { uint32_t GetChanceOfAddingGlobalVariable() { return chance_of_adding_global_variable_; } + uint32_t GetChanceOfAddingImageSampleUnusedComponents() { + return chance_of_adding_image_sample_unused_components_; + } uint32_t GetChanceOfAddingLoad() { return chance_of_adding_load_; } uint32_t GetChanceOfAddingLocalVariable() { return chance_of_adding_local_variable_; @@ -165,6 +168,9 @@ class FuzzerContext { uint32_t GetChanceOfChoosingStructTypeVsArrayType() { return chance_of_choosing_struct_type_vs_array_type_; } + uint32_t GetChanceOfChoosingWorkgroupStorageClass() { + return chance_of_choosing_workgroup_storage_class_; + } uint32_t GetChanceOfConstructingComposite() { return chance_of_constructing_composite_; } @@ -204,6 +210,9 @@ class FuzzerContext { uint32_t GetChanceOfReplacingLinearAlgebraInstructions() { return chance_of_replacing_linear_algebra_instructions_; } + uint32_t GetChanceOfReplacingParametersWithGlobals() { + return chance_of_replacing_parameters_with_globals_; + } uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; } uint32_t GetChanceOfSwappingConditionalBranchOperands() { return chance_of_swapping_conditional_branch_operands_; @@ -265,6 +274,11 @@ class FuzzerContext { // Ensure that the array size is non-zero. return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1; } + uint32_t GetRandomUnusedComponentCountForImageSample( + uint32_t max_unused_component_count) { + // Ensure that the number of unused components is non-zero. + return random_generator_->RandomUint32(max_unused_component_count) + 1; + } bool GoDeeperInConstantObfuscation(uint32_t depth) { return go_deeper_in_constant_obfuscation_(depth, random_generator_); } @@ -286,6 +300,7 @@ class FuzzerContext { uint32_t chance_of_adding_dead_continue_; uint32_t chance_of_adding_equation_instruction_; uint32_t chance_of_adding_global_variable_; + uint32_t chance_of_adding_image_sample_unused_components_; uint32_t chance_of_adding_load_; uint32_t chance_of_adding_local_variable_; uint32_t chance_of_adding_matrix_type_; @@ -301,6 +316,7 @@ class FuzzerContext { uint32_t chance_of_adjusting_selection_control_; uint32_t chance_of_calling_function_; uint32_t chance_of_choosing_struct_type_vs_array_type_; + uint32_t chance_of_choosing_workgroup_storage_class_; uint32_t chance_of_constructing_composite_; uint32_t chance_of_copying_object_; uint32_t chance_of_donating_additional_module_; @@ -316,6 +332,7 @@ class FuzzerContext { uint32_t chance_of_pushing_id_through_variable_; uint32_t chance_of_replacing_id_with_synonym_; uint32_t chance_of_replacing_linear_algebra_instructions_; + uint32_t chance_of_replacing_parameters_with_globals_; uint32_t chance_of_splitting_block_; uint32_t chance_of_swapping_conditional_branch_operands_; uint32_t chance_of_toggling_access_chain_instruction_; diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp index 4023b2207..2a6fdc28f 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp @@ -30,8 +30,22 @@ FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables( FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default; void FuzzerPassAddGlobalVariables::Apply() { + SpvStorageClass variable_storage_class = SpvStorageClassPrivate; + for (auto& entry_point : GetIRContext()->module()->entry_points()) { + // If the execution model of some entry point is GLCompute, + // then the variable storage class may be Workgroup. + if (entry_point.GetSingleWordInOperand(0) == SpvExecutionModelGLCompute) { + variable_storage_class = + GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfChoosingWorkgroupStorageClass()) + ? SpvStorageClassWorkgroup + : SpvStorageClassPrivate; + break; + } + } + auto basic_type_ids_and_pointers = - GetAvailableBasicTypesAndPointers(SpvStorageClassPrivate); + GetAvailableBasicTypesAndPointers(variable_storage_class); // These are the basic types that are available to this fuzzer pass. auto& basic_types = basic_type_ids_and_pointers.first; @@ -59,18 +73,21 @@ void FuzzerPassAddGlobalVariables::Apply() { pointer_type_id = GetFuzzerContext()->GetFreshId(); available_pointers_to_basic_type.push_back(pointer_type_id); ApplyTransformation(TransformationAddTypePointer( - pointer_type_id, SpvStorageClassPrivate, basic_type)); + pointer_type_id, variable_storage_class, basic_type)); } else { // There is - grab one. pointer_type_id = available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex( available_pointers_to_basic_type)]; } - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3274): We could - // add new variables with Workgroup storage class in compute shaders. + ApplyTransformation(TransformationAddGlobalVariable( GetFuzzerContext()->GetFreshId(), pointer_type_id, - SpvStorageClassPrivate, FindOrCreateZeroConstant(basic_type), true)); + variable_storage_class, + variable_storage_class == SpvStorageClassPrivate + ? FindOrCreateZeroConstant(basic_type) + : 0, + true)); } } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp new file mode 100644 index 000000000..01fd282dd --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2020 André Perez Maselco +// +// 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_image_sample_unused_components.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_image_sample_unused_components.h" +#include "source/fuzz/transformation_composite_construct.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddImageSampleUnusedComponents:: + FuzzerPassAddImageSampleUnusedComponents( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddImageSampleUnusedComponents:: + ~FuzzerPassAddImageSampleUnusedComponents() = default; + +void FuzzerPassAddImageSampleUnusedComponents::Apply() { + // SPIR-V module to help understand the transformation. + // + // OpCapability Shader + // %1 = OpExtInstImport "GLSL.std.450" + // OpMemoryModel Logical GLSL450 + // OpEntryPoint Fragment %15 "main" %12 %14 + // OpExecutionMode %15 OriginUpperLeft + // + // ; Decorations + // OpDecorate %12 Location 0 ; Input color variable location + // OpDecorate %13 DescriptorSet 0 ; Image coordinate variable + // descriptor set OpDecorate %13 Binding 0 ; Image coordinate + // variable binding OpDecorate %14 Location 0 ; Fragment color + // variable location + // + // ; Types + // %2 = OpTypeVoid + // %3 = OpTypeFunction %2 + // %4 = OpTypeFloat 32 + // %5 = OpTypeVector %4 2 + // %6 = OpTypeVector %4 4 + // %7 = OpTypeImage %4 2D 0 0 0 1 Rgba32f + // %8 = OpTypeSampledImage %7 + // %9 = OpTypePointer Input %5 + // %10 = OpTypePointer UniformConstant %8 + // %11 = OpTypePointer Output %6 + // + // ; Variables + // %12 = OpVariable %9 Input ; Input image coordinate variable + // %13 = OpVariable %10 UniformConstant ; Image variable + // %14 = OpVariable %11 Output ; Fragment color variable + // + // ; main function + // %15 = OpFunction %2 None %3 + // %16 = OpLabel + // %17 = OpLoad %5 %12 + // %18 = OpLoad %8 %13 + // %19 = OpImageSampleImplicitLod %6 %18 %17 + // OpStore %14 %19 + // OpReturn + // OpFunctionEnd + + GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { + // |instruction| %19 = OpImageSampleImplicitLod %6 %18 %17 + if (!spvOpcodeIsImageSample(instruction->opcode())) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfAddingImageSampleUnusedComponents())) { + return; + } + + // Gets image sample coordinate information. + // |coordinate_instruction| %17 = OpLoad %5 %12 + uint32_t coordinate_id = instruction->GetSingleWordInOperand(1); + auto coordinate_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(coordinate_id); + auto coordinate_type = GetIRContext()->get_type_mgr()->GetType( + coordinate_instruction->type_id()); + + // If the coordinate is a 4-dimensional vector, then no unused components + // may be added. + if (coordinate_type->AsVector() && + coordinate_type->AsVector()->element_count() == 4) { + return; + } + + // If the coordinate is a scalar, then at most 3 unused components may be + // added. If the coordinate is a vector, then the maximum number of unused + // components depends on the vector size. + // For the sample module, the coordinate type instruction is %5 = + // OpTypeVector %4 2, thus |max_unused_component_count| = 4 - 2 = 2. + uint32_t max_unused_component_count = + coordinate_type->AsInteger() || coordinate_type->AsFloat() + ? 3 + : 4 - coordinate_type->AsVector()->element_count(); + + // |unused_component_count| may be 1 or 2. + uint32_t unused_component_count = + GetFuzzerContext()->GetRandomUnusedComponentCountForImageSample( + max_unused_component_count); + + // Gets a type for the zero-unused components. + uint32_t zero_constant_type_id; + switch (unused_component_count) { + case 1: + // If the coordinate is an integer or float, then the unused components + // type is the same as the coordinate. If the coordinate is a vector, + // then the unused components type is the same as the vector components + // type. + zero_constant_type_id = + coordinate_type->AsInteger() || coordinate_type->AsFloat() + ? coordinate_instruction->type_id() + : GetIRContext()->get_type_mgr()->GetId( + coordinate_type->AsVector()->element_type()); + break; + case 2: + case 3: + // If the coordinate is an integer or float, then the unused components + // type is the same as the coordinate. If the coordinate is a vector, + // then the unused components type is the same as the coordinate + // components type. + // |zero_constant_type_id| %5 = OpTypeVector %4 2 + zero_constant_type_id = + coordinate_type->AsInteger() || coordinate_type->AsFloat() + ? FindOrCreateVectorType(coordinate_instruction->type_id(), + unused_component_count) + : FindOrCreateVectorType( + GetIRContext()->get_type_mgr()->GetId( + coordinate_type->AsVector()->element_type()), + unused_component_count); + break; + default: + assert(false && "Should be unreachable."); + zero_constant_type_id = 0; + break; + } + + // Gets |coordinate_type| again because the module may have changed due to + // the use of FindOrCreateVectorType above. + coordinate_type = GetIRContext()->get_type_mgr()->GetType( + coordinate_instruction->type_id()); + + // If the new vector type with unused components does not exist, then create + // it. |coordinate_with_unused_components_type_id| %6 = OpTypeVector %4 4 + uint32_t coordinate_with_unused_components_type_id = + coordinate_type->AsInteger() || coordinate_type->AsFloat() + ? FindOrCreateVectorType(coordinate_instruction->type_id(), + 1 + unused_component_count) + : FindOrCreateVectorType( + GetIRContext()->get_type_mgr()->GetId( + coordinate_type->AsVector()->element_type()), + coordinate_type->AsVector()->element_count() + + unused_component_count); + + // Inserts an OpCompositeConstruct instruction which + // represents the coordinate with unused components. + // |coordinate_with_unused_components_id| + // %22 = OpCompositeConstruct %6 %17 %21 + uint32_t coordinate_with_unused_components_id = + GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationCompositeConstruct( + coordinate_with_unused_components_type_id, + {coordinate_instruction->result_id(), + // FindOrCreateZeroConstant + // %20 = OpConstant %4 0 + // %21 = OpConstantComposite %5 %20 %20 + FindOrCreateZeroConstant(zero_constant_type_id)}, + MakeInstructionDescriptor(GetIRContext(), instruction), + coordinate_with_unused_components_id)); + + // Tries to add unused components to the image sample coordinate. + // %19 = OpImageSampleImplicitLod %6 %18 %22 + ApplyTransformation(TransformationAddImageSampleUnusedComponents( + coordinate_with_unused_components_id, + MakeInstructionDescriptor(GetIRContext(), instruction))); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h new file mode 100644 index 000000000..26374c326 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 André Perez Maselco +// +// 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_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass searches for image sample instructions in the module and +// randomly applies the transformation to add unused components to the image +// sample coordinate. +class FuzzerPassAddImageSampleUnusedComponents : public FuzzerPass { + public: + FuzzerPassAddImageSampleUnusedComponents( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddImageSampleUnusedComponents(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp index 7f9b84887..43fba5260 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp @@ -43,6 +43,8 @@ void FuzzerPassReplaceLinearAlgebraInstructions::Apply() { // |spvOpcodeIsLinearAlgebra|. if (instruction->opcode() != SpvOpVectorTimesScalar && instruction->opcode() != SpvOpMatrixTimesScalar && + instruction->opcode() != SpvOpVectorTimesMatrix && + instruction->opcode() != SpvOpMatrixTimesVector && instruction->opcode() != SpvOpDot) { return; } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp new file mode 100644 index 000000000..87b2b5d72 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp @@ -0,0 +1,92 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h" + +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_replace_parameter_with_global.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceParameterWithGlobal::FuzzerPassReplaceParameterWithGlobal( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassReplaceParameterWithGlobal::~FuzzerPassReplaceParameterWithGlobal() = + default; + +void FuzzerPassReplaceParameterWithGlobal::Apply() { + for (const auto& function : *GetIRContext()->module()) { + if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(), + function.result_id())) { + continue; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfReplacingParametersWithGlobals())) { + continue; + } + + auto params = + fuzzerutil::GetParameters(GetIRContext(), function.result_id()); + + // Make sure at least one parameter can be replaced. Also checks that the + // function has at least one parameter. + if (std::none_of(params.begin(), params.end(), + [this](const opt::Instruction* param) { + const auto* param_type = + GetIRContext()->get_type_mgr()->GetType( + param->type_id()); + assert(param_type && "Parameter has invalid type"); + return TransformationReplaceParameterWithGlobal:: + CanReplaceFunctionParameterType(*param_type); + })) { + continue; + } + + // Select id of a parameter to replace. + const opt::Instruction* replaced_param = nullptr; + const opt::analysis::Type* param_type = nullptr; + do { + replaced_param = GetFuzzerContext()->RemoveAtRandomIndex(¶ms); + param_type = + GetIRContext()->get_type_mgr()->GetType(replaced_param->type_id()); + assert(param_type && "Parameter has invalid type"); + } while (!TransformationReplaceParameterWithGlobal:: + CanReplaceFunctionParameterType(*param_type)); + + assert(replaced_param && "Unable to find a parameter to replace"); + + // Make sure type id for the global variable exists in the module. + FindOrCreatePointerType(replaced_param->type_id(), SpvStorageClassPrivate); + + // Make sure initializer for the global variable exists in the module. + FindOrCreateZeroConstant(replaced_param->type_id()); + + ApplyTransformation(TransformationReplaceParameterWithGlobal( + GetFuzzerContext()->GetFreshId(), replaced_param->result_id(), + GetFuzzerContext()->GetFreshId())); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h new file mode 100644 index 000000000..25011bdca --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Iterates over all non-entry-point functions in the module and randomly +// replaces a parameter with a global variable. +class FuzzerPassReplaceParameterWithGlobal : public FuzzerPass { + public: + FuzzerPassReplaceParameterWithGlobal( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassReplaceParameterWithGlobal() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp index 80dff2d10..359de9a63 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp @@ -112,19 +112,6 @@ bool PhiIdsOkForNewEdge( return true; } -uint32_t MaybeGetBoolConstantId(opt::IRContext* context, bool value) { - opt::analysis::Bool bool_type; - auto registered_bool_type = - context->get_type_mgr()->GetRegisteredType(&bool_type); - if (!registered_bool_type) { - return 0; - } - opt::analysis::BoolConstant bool_constant(registered_bool_type->AsBool(), - value); - return context->get_constant_mgr()->FindDeclaredConstant( - &bool_constant, context->get_type_mgr()->GetId(&bool_type)); -} - void AddUnreachableEdgeAndUpdateOpPhis( opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, bool condition_value, @@ -135,7 +122,7 @@ void AddUnreachableEdgeAndUpdateOpPhis( "Precondition on terminator of bb_from is not satisfied"); // Get the id of the boolean constant to be used as the condition. - uint32_t bool_id = MaybeGetBoolConstantId(context, condition_value); + uint32_t bool_id = MaybeGetBoolConstant(context, condition_value); assert( bool_id && "Precondition that condition value must be available is not satisfied"); @@ -627,6 +614,7 @@ void AddGlobalVariable(opt::IRContext* context, uint32_t result_id, context, SpvOpVariable, type_id, result_id, std::move(operands))); AddVariableIdToEntryPointInterfaces(context, result_id); + UpdateModuleIdBound(context, result_id); } void AddLocalVariable(opt::IRContext* context, uint32_t result_id, @@ -657,6 +645,8 @@ void AddLocalVariable(opt::IRContext* context, uint32_t result_id, opt::Instruction::OperandList{ {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}, {SPV_OPERAND_TYPE_ID, {initializer_id}}})); + + UpdateModuleIdBound(context, result_id); } bool HasDuplicates(const std::vector& arr) { @@ -771,6 +761,199 @@ uint32_t MaybeGetStructType(opt::IRContext* ir_context, return ir_context->get_type_mgr()->GetId(&type); } +uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context, + uint32_t scalar_or_composite_type_id) { + const auto* type = + ir_context->get_type_mgr()->GetType(scalar_or_composite_type_id); + assert(type && "|scalar_or_composite_type_id| is invalid"); + + switch (type->kind()) { + case opt::analysis::Type::kBool: + return MaybeGetBoolConstant(ir_context, false); + case opt::analysis::Type::kFloat: + case opt::analysis::Type::kInteger: { + std::vector words = {0}; + if ((type->AsInteger() && type->AsInteger()->width() > 32) || + (type->AsFloat() && type->AsFloat()->width() > 32)) { + words.push_back(0); + } + + return MaybeGetScalarConstant(ir_context, words, + scalar_or_composite_type_id); + } + case opt::analysis::Type::kStruct: { + std::vector component_ids; + for (const auto* component_type : type->AsStruct()->element_types()) { + auto component_type_id = + ir_context->get_type_mgr()->GetId(component_type); + assert(component_type_id && "Component type is invalid"); + + auto component_id = MaybeGetZeroConstant(ir_context, component_type_id); + if (component_id == 0) { + return 0; + } + + component_ids.push_back(component_id); + } + + return MaybeGetCompositeConstant(ir_context, component_ids, + scalar_or_composite_type_id); + } + case opt::analysis::Type::kMatrix: + case opt::analysis::Type::kVector: { + const auto* component_type = type->AsVector() + ? type->AsVector()->element_type() + : type->AsMatrix()->element_type(); + auto component_type_id = + ir_context->get_type_mgr()->GetId(component_type); + assert(component_type_id && "Component type is invalid"); + + if (auto component_id = + MaybeGetZeroConstant(ir_context, component_type_id)) { + auto component_count = type->AsVector() + ? type->AsVector()->element_count() + : type->AsMatrix()->element_count(); + return MaybeGetCompositeConstant( + ir_context, std::vector(component_count, component_id), + scalar_or_composite_type_id); + } + + return 0; + } + case opt::analysis::Type::kArray: { + auto component_type_id = + ir_context->get_type_mgr()->GetId(type->AsArray()->element_type()); + assert(component_type_id && "Component type is invalid"); + + if (auto component_id = + MaybeGetZeroConstant(ir_context, component_type_id)) { + auto type_id = ir_context->get_type_mgr()->GetId(type); + assert(type_id && "|type| is invalid"); + + const auto* type_inst = ir_context->get_def_use_mgr()->GetDef(type_id); + assert(type_inst && "Array's type id is invalid"); + + return MaybeGetCompositeConstant( + ir_context, + std::vector(GetArraySize(*type_inst, ir_context), + component_id), + scalar_or_composite_type_id); + } + + return 0; + } + default: + assert(false && "Type is not supported"); + return 0; + } +} + +uint32_t MaybeGetScalarConstant(opt::IRContext* ir_context, + const std::vector& words, + uint32_t scalar_type_id) { + const auto* type = ir_context->get_type_mgr()->GetType(scalar_type_id); + assert(type && "|scalar_type_id| is invalid"); + + if (const auto* int_type = type->AsInteger()) { + return MaybeGetIntegerConstant(ir_context, words, int_type->width(), + int_type->IsSigned()); + } else if (const auto* float_type = type->AsFloat()) { + return MaybeGetFloatConstant(ir_context, words, float_type->width()); + } else { + assert(type->AsBool() && words.size() == 1 && + "|scalar_type_id| doesn't represent a scalar type"); + return MaybeGetBoolConstant(ir_context, words[0]); + } +} + +uint32_t MaybeGetCompositeConstant(opt::IRContext* ir_context, + const std::vector& component_ids, + uint32_t composite_type_id) { + std::vector constants; + for (auto id : component_ids) { + const auto* component_constant = + ir_context->get_constant_mgr()->FindDeclaredConstant(id); + assert(component_constant && "|id| is invalid"); + + constants.push_back(component_constant); + } + + const auto* type = ir_context->get_type_mgr()->GetType(composite_type_id); + assert(type && "|composite_type_id| is invalid"); + + std::unique_ptr composite_constant; + switch (type->kind()) { + case opt::analysis::Type::kStruct: + composite_constant = MakeUnique( + type->AsStruct(), std::move(constants)); + break; + case opt::analysis::Type::kVector: + composite_constant = MakeUnique( + type->AsVector(), std::move(constants)); + break; + case opt::analysis::Type::kMatrix: + composite_constant = MakeUnique( + type->AsMatrix(), std::move(constants)); + break; + case opt::analysis::Type::kArray: + composite_constant = MakeUnique( + type->AsArray(), std::move(constants)); + break; + default: + assert(false && + "|composite_type_id| is not a result id of a composite type"); + return 0; + } + + return ir_context->get_constant_mgr()->FindDeclaredConstant( + composite_constant.get(), composite_type_id); +} + +uint32_t MaybeGetIntegerConstant(opt::IRContext* ir_context, + const std::vector& words, + uint32_t width, bool is_signed) { + auto type_id = MaybeGetIntegerType(ir_context, width, is_signed); + if (!type_id) { + return 0; + } + + const auto* type = ir_context->get_type_mgr()->GetType(type_id); + assert(type && "|type_id| is invalid"); + + opt::analysis::IntConstant constant(type->AsInteger(), words); + return ir_context->get_constant_mgr()->FindDeclaredConstant(&constant, + type_id); +} + +uint32_t MaybeGetFloatConstant(opt::IRContext* ir_context, + const std::vector& words, + uint32_t width) { + auto type_id = MaybeGetFloatType(ir_context, width); + if (!type_id) { + return 0; + } + + const auto* type = ir_context->get_type_mgr()->GetType(type_id); + assert(type && "|type_id| is invalid"); + + opt::analysis::FloatConstant constant(type->AsFloat(), words); + return ir_context->get_constant_mgr()->FindDeclaredConstant(&constant, + type_id); +} + +uint32_t MaybeGetBoolConstant(opt::IRContext* context, bool value) { + opt::analysis::Bool bool_type; + auto registered_bool_type = + context->get_type_mgr()->GetRegisteredType(&bool_type); + if (!registered_bool_type) { + return 0; + } + opt::analysis::BoolConstant bool_constant(registered_bool_type->AsBool(), + value); + return context->get_constant_mgr()->FindDeclaredConstant( + &bool_constant, context->get_type_mgr()->GetId(&bool_type)); +} + void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id, uint32_t width, bool is_signed) { ir_context->module()->AddType(MakeUnique( diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h index 5b471bd34..36e860f45 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h @@ -53,10 +53,6 @@ bool PhiIdsOkForNewEdge( opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, const google::protobuf::RepeatedField& phi_ids); -// Returns the id of a boolean constant with value |value| if it exists in the -// module, or 0 otherwise. -uint32_t MaybeGetBoolConstantId(opt::IRContext* context, bool value); - // Requires that a boolean constant with value |condition_value| is available, // that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) holds, and that // bb_from ends with "OpBranch %some_block". Turns OpBranch into @@ -229,7 +225,8 @@ void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id); // Adds a global variable with storage class |storage_class| to the module, with // type |type_id| and either no initializer or |initializer_id| as an // initializer, depending on whether |initializer_id| is 0. The global variable -// has result id |result_id|. +// has result id |result_id|. Updates module's id bound to accommodate for +// |result_id|. // // - |type_id| must be the id of a pointer type with the same storage class as // |storage_class|. @@ -243,6 +240,7 @@ void AddGlobalVariable(opt::IRContext* context, uint32_t result_id, // Adds an instruction to the start of |function_id|, of the form: // |result_id| = OpVariable |type_id| Function |initializer_id|. +// Updates module's id bound to accommodate for |result_id|. // // - |type_id| must be the id of a pointer type with Function storage class. // - |initializer_id| must be the id of a constant with the same type as the @@ -306,6 +304,48 @@ uint32_t MaybeGetVectorType(opt::IRContext* ir_context, uint32_t MaybeGetStructType(opt::IRContext* ir_context, const std::vector& component_type_ids); +// Recursive definition is the following: +// - if |scalar_or_composite_type_id| is a result id of a scalar type - returns +// a result id of the following constants (depending on the type): int -> 0, +// float -> 0.0, bool -> false. +// - otherwise, returns a result id of an OpConstantComposite instruction. +// Every component of the composite constant is looked up by calling this +// function with the type id of that component. +// Returns 0 if no such instruction is present in the module. +uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context, + uint32_t scalar_or_composite_type_id); + +// Returns the result id of an OpConstant instruction. |scalar_type_id| must be +// a result id of a scalar type (i.e. int, float or bool). Returns 0 if no such +// instruction is present in the module. +uint32_t MaybeGetScalarConstant(opt::IRContext* ir_context, + const std::vector& words, + uint32_t scalar_type_id); + +// Returns the result id of an OpConstantComposite instruction. +// |composite_type_id| must be a result id of a composite type (i.e. vector, +// matrix, struct or array). Returns 0 if no such instruction is present in the +// module. +uint32_t MaybeGetCompositeConstant(opt::IRContext* ir_context, + const std::vector& component_ids, + uint32_t composite_type_id); + +// Returns the result id of an OpConstant instruction of integral type. +// Returns 0 if no such instruction or type is present in the module. +uint32_t MaybeGetIntegerConstant(opt::IRContext* ir_context, + const std::vector& words, + uint32_t width, bool is_signed); + +// Returns the result id of an OpConstant instruction of floating-point type. +// Returns 0 if no such instruction or type is present in the module. +uint32_t MaybeGetFloatConstant(opt::IRContext* ir_context, + const std::vector& words, + uint32_t width); + +// Returns the id of a boolean constant with value |value| if it exists in the +// module, or 0 otherwise. +uint32_t MaybeGetBoolConstant(opt::IRContext* context, bool value); + // Creates a new OpTypeInt instruction in the module. Updates module's id bound // to accommodate for |result_id|. void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id, diff --git a/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto index a7cc7c992..eebfccf23 100644 --- a/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -383,6 +383,9 @@ message Transformation { TransformationAddParameter add_parameter = 52; TransformationAddCopyMemory add_copy_memory = 53; TransformationInvertComparisonOperator invert_comparison_operator = 54; + TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 55; + TransformationReplaceParameterWithGlobal replace_parameter_with_global = 56; + TransformationRecordSynonymousConstants record_synonymous_constants = 57; // Add additional option using the next available number. } } @@ -616,6 +619,18 @@ message TransformationAddGlobalVariable { } +message TransformationAddImageSampleUnusedComponents { + + // A transformation that adds unused components to an image sample coordinate. + + // An vector id with the original coordinate and the unused components. + uint32 coordinate_with_unused_components_id = 1; + + // A descriptor for an image sample instruction. + InstructionDescriptor instruction_descriptor = 2; + +} + message TransformationAddLocalVariable { // Adds a local variable of the given type (which must be a pointer with @@ -1109,6 +1124,43 @@ message TransformationPushIdThroughVariable { } +message TransformationRecordSynonymousConstants { + + // A transformation that, given the IDs to two synonymous constants, + // records the fact that they are synonymous. The module is not changed. + // Two constants are synonymous if: + // - they have the same type (ignoring the presence of integer sign) + // - they have the same opcode (one of OpConstant, OpConstantTrue, + // OpConstantFalse, OpConstantNull) + // - they have the same value + // If the types are the same, OpConstantNull is equivalent to + // OpConstantFalse or OpConstant with value zero. + + // The id of a constant + uint32 constant1_id = 1; + + // The id of the synonym + uint32 constant2_id = 2; + +} + +message TransformationReplaceParameterWithGlobal { + + // Removes parameter with result id |parameter_id| from its function + // and creates a global variable to pass its value to the function instead. + + // Fresh id for a new function type. This might not be used if a required + // function type already exists or if we can change the old function type. + uint32 function_type_fresh_id = 2; + + // Result id of the OpFunctionParameter instruction to remove. + uint32 parameter_id = 3; + + // Fresh id of a global variable used to pass parameter's value to the function. + uint32 global_variable_fresh_id = 4; + +} + message TransformationReplaceBooleanConstantWithConstantBinary { // A transformation to capture replacing a use of a boolean constant with @@ -1176,14 +1228,14 @@ message TransformationReplaceLinearAlgebraInstruction { // Supported: // OpVectorTimesScalar // OpMatrixTimesScalar + // OpVectorTimesMatrix + // OpMatrixTimesVector // OpDot // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354): // Right now we only support certain operations. When this issue is addressed // the supporting comments can be removed. // To be supported in the future: // OpTranspose - // OpVectorTimesMatrix - // OpMatrixTimesVector // OpMatrixTimesMatrix // OpOuterProduct InstructionDescriptor instruction_descriptor = 2; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation.cpp b/3rdparty/spirv-tools/source/fuzz/transformation.cpp index 26c93efb7..4adb39c34 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation.cpp @@ -29,6 +29,7 @@ #include "source/fuzz/transformation_add_function.h" #include "source/fuzz/transformation_add_global_undef.h" #include "source/fuzz/transformation_add_global_variable.h" +#include "source/fuzz/transformation_add_image_sample_unused_components.h" #include "source/fuzz/transformation_add_local_variable.h" #include "source/fuzz/transformation_add_no_contraction_decoration.h" #include "source/fuzz/transformation_add_parameter.h" @@ -57,10 +58,12 @@ #include "source/fuzz/transformation_permute_function_parameters.h" #include "source/fuzz/transformation_permute_phi_operands.h" #include "source/fuzz/transformation_push_id_through_variable.h" +#include "source/fuzz/transformation_record_synonymous_constants.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_replace_linear_algebra_instruction.h" +#include "source/fuzz/transformation_replace_parameter_with_global.h" #include "source/fuzz/transformation_set_function_control.h" #include "source/fuzz/transformation_set_loop_control.h" #include "source/fuzz/transformation_set_memory_operands_mask.h" @@ -112,6 +115,10 @@ std::unique_ptr Transformation::FromMessage( case protobufs::Transformation::TransformationCase::kAddGlobalVariable: return MakeUnique( message.add_global_variable()); + case protobufs::Transformation::TransformationCase:: + kAddImageSampleUnusedComponents: + return MakeUnique( + message.add_image_sample_unused_components()); case protobufs::Transformation::TransformationCase::kAddLocalVariable: return MakeUnique( message.add_local_variable()); @@ -188,6 +195,14 @@ std::unique_ptr Transformation::FromMessage( case protobufs::Transformation::TransformationCase::kPushIdThroughVariable: return MakeUnique( message.push_id_through_variable()); + case protobufs::Transformation::TransformationCase:: + kRecordSynonymousConstants: + return MakeUnique( + message.record_synonymous_constants()); + case protobufs::Transformation::TransformationCase:: + kReplaceParameterWithGlobal: + return MakeUnique( + message.replace_parameter_with_global()); case protobufs::Transformation::TransformationCase:: kReplaceBooleanConstantWithConstantBinary: return MakeUnique( diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_block.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_block.cpp index b246c3fc8..0bbda5a2b 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_block.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_block.cpp @@ -40,8 +40,8 @@ bool TransformationAddDeadBlock::IsApplicable( // First, we check that a constant with the same value as // |message_.condition_value| is present. - if (!fuzzerutil::MaybeGetBoolConstantId(ir_context, - message_.condition_value())) { + if (!fuzzerutil::MaybeGetBoolConstant(ir_context, + message_.condition_value())) { // The required constant is not present, so the transformation cannot be // applied. return false; @@ -92,8 +92,8 @@ void TransformationAddDeadBlock::Apply( existing_block->terminator()->GetSingleWordInOperand(0); // Get the id of the boolean value that will be used as the branch condition. - auto bool_id = fuzzerutil::MaybeGetBoolConstantId(ir_context, - message_.condition_value()); + auto bool_id = + fuzzerutil::MaybeGetBoolConstant(ir_context, message_.condition_value()); // Make a new block that unconditionally branches to the original successor // block. diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.cpp index db9de7d23..44c9abada 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.cpp @@ -112,8 +112,8 @@ bool TransformationAddDeadBreak::IsApplicable( const TransformationContext& transformation_context) const { // First, we check that a constant with the same value as // |message_.break_condition_value| is present. - if (!fuzzerutil::MaybeGetBoolConstantId(ir_context, - message_.break_condition_value())) { + if (!fuzzerutil::MaybeGetBoolConstant(ir_context, + message_.break_condition_value())) { // The required constant is not present, so the transformation cannot be // applied. return false; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_continue.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_continue.cpp index 1fc6d6715..1328b1eaf 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_continue.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_continue.cpp @@ -38,8 +38,8 @@ bool TransformationAddDeadContinue::IsApplicable( const TransformationContext& transformation_context) const { // First, we check that a constant with the same value as // |message_.continue_condition_value| is present. - if (!fuzzerutil::MaybeGetBoolConstantId( - ir_context, message_.continue_condition_value())) { + if (!fuzzerutil::MaybeGetBoolConstant(ir_context, + message_.continue_condition_value())) { // The required constant is not present, so the transformation cannot be // applied. return false; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_global_variable.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_global_variable.cpp index b69f208cc..303c4d977 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_global_variable.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_global_variable.cpp @@ -98,8 +98,6 @@ void TransformationAddGlobalVariable::Apply( static_cast(message_.storage_class()), message_.initializer_id()); - fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - if (message_.value_is_irrelevant()) { transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( message_.fresh_id()); diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.cpp new file mode 100644 index 000000000..1be1d4378 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.cpp @@ -0,0 +1,117 @@ +// Copyright (c) 2020 André Perez Maselco +// +// 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_image_sample_unused_components.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddImageSampleUnusedComponents:: + TransformationAddImageSampleUnusedComponents( + const spvtools::fuzz::protobufs:: + TransformationAddImageSampleUnusedComponents& message) + : message_(message) {} + +TransformationAddImageSampleUnusedComponents:: + TransformationAddImageSampleUnusedComponents( + uint32_t coordinate_with_unused_components_id, + const protobufs::InstructionDescriptor& instruction_descriptor) { + message_.set_coordinate_with_unused_components_id( + coordinate_with_unused_components_id); + *message_.mutable_instruction_descriptor() = instruction_descriptor; +} + +bool TransformationAddImageSampleUnusedComponents::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto image_sample_instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + + // The image sample instruction must be defined. + if (image_sample_instruction == nullptr) { + return false; + } + + // The instruction must be an image sample instruction. + if (!spvOpcodeIsImageSample(image_sample_instruction->opcode())) { + return false; + } + + uint32_t coordinate_id = image_sample_instruction->GetSingleWordInOperand(1); + auto coordinate_instruction = + ir_context->get_def_use_mgr()->GetDef(coordinate_id); + auto coordinate_type = + ir_context->get_type_mgr()->GetType(coordinate_instruction->type_id()); + + // It must be possible to add unused components. + if (coordinate_type->AsVector() && + coordinate_type->AsVector()->element_count() == 4) { + return false; + } + + auto coordinate_with_unused_components_instruction = + ir_context->get_def_use_mgr()->GetDef( + message_.coordinate_with_unused_components_id()); + + // The coordinate with unused components instruction must be defined. + if (coordinate_with_unused_components_instruction == nullptr) { + return false; + } + + // It must be an OpCompositeConstruct instruction such that it can be checked + // that the original components are present. + if (coordinate_with_unused_components_instruction->opcode() != + SpvOpCompositeConstruct) { + return false; + } + + // The first constituent must be the original coordinate. + if (coordinate_with_unused_components_instruction->GetSingleWordInOperand( + 0) != coordinate_id) { + return false; + } + + auto coordinate_with_unused_components_type = + ir_context->get_type_mgr()->GetType( + coordinate_with_unused_components_instruction->type_id()); + + // |coordinate_with_unused_components_type| must be a vector. + if (!coordinate_with_unused_components_type->AsVector()) { + return false; + } + + return true; +} + +void TransformationAddImageSampleUnusedComponents::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Sets the coordinate operand. + auto image_sample_instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + image_sample_instruction->SetInOperand( + 1, {message_.coordinate_with_unused_components_id()}); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation +TransformationAddImageSampleUnusedComponents::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_image_sample_unused_components() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.h b/3rdparty/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.h new file mode 100644 index 000000000..749348121 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.h @@ -0,0 +1,57 @@ +// Copyright (c) 2020 André Perez Maselco +// +// 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_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddImageSampleUnusedComponents : public Transformation { + public: + explicit TransformationAddImageSampleUnusedComponents( + const protobufs::TransformationAddImageSampleUnusedComponents& message); + + TransformationAddImageSampleUnusedComponents( + uint32_t coordinate_with_unused_components_id, + const protobufs::InstructionDescriptor& instruction_descriptor); + + // - |coordinate_with_unused_components_id| must identify a vector such that + // the first components match the components of the image sample coordinate. + // - |message_.instruction_descriptor| must identify an image sample + // instruction + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Add unused components to an image sample coordinate by replacing the + // coordinate with |coordinate_with_unused_components_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddImageSampleUnusedComponents message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_local_variable.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_local_variable.cpp index a93f10469..a6b31b491 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_local_variable.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_local_variable.cpp @@ -74,8 +74,6 @@ void TransformationAddLocalVariable::Apply( message_.type_id(), message_.function_id(), message_.initializer_id()); - fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - if (message_.value_is_irrelevant()) { transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( message_.fresh_id()); diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp index 54d76fab0..0206c77bc 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp @@ -127,8 +127,6 @@ void TransformationPushIdThroughVariable::Apply( message_.initializer_id()); } - fuzzerutil::UpdateModuleIdBound(ir_context, message_.variable_id()); - // Stores value id to variable id. FindInstruction(message_.instruction_descriptor(), ir_context) ->InsertBefore(MakeUnique( diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp new file mode 100644 index 000000000..422e57e89 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp @@ -0,0 +1,107 @@ +// Copyright (c) 2020 Stefano Milizia +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "transformation_record_synonymous_constants.h" + +namespace spvtools { +namespace fuzz { + +namespace { +bool IsScalarZeroConstant(const opt::analysis::Constant* constant) { + return constant->AsScalarConstant() && constant->IsZero(); +} +} // namespace + +TransformationRecordSynonymousConstants:: + TransformationRecordSynonymousConstants( + const protobufs::TransformationRecordSynonymousConstants& message) + : message_(message) {} + +TransformationRecordSynonymousConstants:: + TransformationRecordSynonymousConstants(uint32_t constant1_id, + uint32_t constant2_id) { + message_.set_constant1_id(constant1_id); + message_.set_constant2_id(constant2_id); +} + +bool TransformationRecordSynonymousConstants::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& /* unused */) const { + // The ids must be different + if (message_.constant1_id() == message_.constant2_id()) { + return false; + } + + auto constant1 = ir_context->get_constant_mgr()->FindDeclaredConstant( + message_.constant1_id()); + auto constant2 = ir_context->get_constant_mgr()->FindDeclaredConstant( + message_.constant2_id()); + + // The constants must exist + if (constant1 == nullptr || constant2 == nullptr) { + return false; + } + + // If the constants are equal, then they are equivalent + if (constant1 == constant2) { + return true; + } + + // If the constants are two integers (signed or unsigned), they are equal + // if they have the same width and the same data words. + if (constant1->AsIntConstant() && constant2->AsIntConstant() && + constant1->type()->AsInteger()->width() == + constant2->type()->AsInteger()->width() && + constant1->AsIntConstant()->words() == + constant2->AsIntConstant()->words()) { + return true; + } + + // The types must be the same + if (!constant1->type()->IsSame(constant2->type())) { + return false; + } + + // The constants are equivalent if one is null and the other is a static + // constant with value 0. + return (constant1->AsNullConstant() && IsScalarZeroConstant(constant2)) || + (IsScalarZeroConstant(constant1) && constant2->AsNullConstant()); +} + +void TransformationRecordSynonymousConstants::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + protobufs::FactDataSynonym fact_data_synonym; + // Define the two equivalent data descriptors (just containing the ids) + *fact_data_synonym.mutable_data1() = + MakeDataDescriptor(message_.constant1_id(), {}); + *fact_data_synonym.mutable_data2() = + MakeDataDescriptor(message_.constant2_id(), {}); + protobufs::Fact fact; + *fact.mutable_data_synonym_fact() = fact_data_synonym; + + // Add the fact to the fact manager + transformation_context->GetFactManager()->AddFact(fact, ir_context); +} + +protobufs::Transformation TransformationRecordSynonymousConstants::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_record_synonymous_constants() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h b/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h new file mode 100644 index 000000000..6a1a607dd --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h @@ -0,0 +1,59 @@ +// Copyright (c) 2020 Stefano Milizia +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS_H +#define SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS_H + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { + +class TransformationRecordSynonymousConstants : public Transformation { + public: + explicit TransformationRecordSynonymousConstants( + const protobufs::TransformationRecordSynonymousConstants& message); + + TransformationRecordSynonymousConstants(uint32_t constant1_id, + uint32_t constant2_id); + + // - |message_.constant_id| and |message_.synonym_id| are distinct ids + // of constants + // - |message_.constant_id| and |message_.synonym_id| refer to constants + // that are equal or equivalent. + // Two integers with the same width and value are equal, even if one is + // signed and the other is not. + // Constants are equivalent if both of them represent zero-like scalar + // values of the same type (for example OpConstant of type int and value + // 0 and OpConstantNull of type int). + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds the fact that |message_.constant_id| and |message_.synonym_id| + // are synonyms to the fact manager. The module is not changed. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationRecordSynonymousConstants message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp index 1c7d0c991..cebb6efec 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp @@ -47,6 +47,8 @@ bool TransformationReplaceLinearAlgebraInstruction::IsApplicable( // It must be a supported linear algebra instruction. if (instruction->opcode() != SpvOpVectorTimesScalar && instruction->opcode() != SpvOpMatrixTimesScalar && + instruction->opcode() != SpvOpVectorTimesMatrix && + instruction->opcode() != SpvOpMatrixTimesVector && instruction->opcode() != SpvOpDot) { return false; } @@ -59,9 +61,8 @@ bool TransformationReplaceLinearAlgebraInstruction::IsApplicable( } // All ids in |message_.fresh_ids| must be fresh. - for (uint32_t i = 0; i < static_cast(message_.fresh_ids().size()); - i++) { - if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_ids(i))) { + for (uint32_t fresh_id : message_.fresh_ids()) { + if (!fuzzerutil::IsFreshId(ir_context, fresh_id)) { return false; } } @@ -81,6 +82,12 @@ void TransformationReplaceLinearAlgebraInstruction::Apply( case SpvOpMatrixTimesScalar: ReplaceOpMatrixTimesScalar(ir_context, linear_algebra_instruction); break; + case SpvOpVectorTimesMatrix: + ReplaceOpVectorTimesMatrix(ir_context, linear_algebra_instruction); + break; + case SpvOpMatrixTimesVector: + ReplaceOpMatrixTimesVector(ir_context, linear_algebra_instruction); + break; case SpvOpDot: ReplaceOpDot(ir_context, linear_algebra_instruction); break; @@ -128,6 +135,49 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount( ->AsVector() ->element_count()); } + case SpvOpVectorTimesMatrix: { + // For each vector component, 1 OpCompositeExtract instruction will be + // inserted. For each matrix column, |1 + vector_component_count| + // OpCompositeExtract, |vector_component_count| OpFMul and + // |vector_component_count - 1| OpFAdd instructions will be inserted. + auto vector_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(1)); + uint32_t vector_component_count = + ir_context->get_type_mgr() + ->GetType(vector_instruction->type_id()) + ->AsVector() + ->element_count(); + uint32_t matrix_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + return vector_component_count * (3 * matrix_column_count + 1); + } + case SpvOpMatrixTimesVector: { + // For each matrix column, |1 + matrix_row_count| OpCompositeExtract + // will be inserted. For each matrix row, |matrix_column_count| OpFMul and + // |matrix_column_count - 1| OpFAdd instructions will be inserted. For + // each vector component, 1 OpCompositeExtract instruction will be + // inserted. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + uint32_t matrix_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + uint32_t matrix_row_count = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_type() + ->AsVector() + ->element_count(); + return 3 * matrix_column_count * matrix_row_count + + 2 * matrix_column_count - matrix_row_count; + } case SpvOpDot: // For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul // will be inserted. The first two OpFMul instructions will result the @@ -280,6 +330,236 @@ void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesScalar( } } +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesMatrix( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets vector information. + auto vector_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + uint32_t vector_component_count = ir_context->get_type_mgr() + ->GetType(vector_instruction->type_id()) + ->AsVector() + ->element_count(); + auto vector_component_type = ir_context->get_type_mgr() + ->GetType(vector_instruction->type_id()) + ->AsVector() + ->element_type(); + + // Extracts vector components. + uint32_t fresh_id_index = 0; + std::vector vector_component_ids(vector_component_count); + for (uint32_t i = 0; i < vector_component_count; i++) { + vector_component_ids[i] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(vector_component_type), + vector_component_ids[i], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + } + + // Gets matrix information. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + uint32_t matrix_column_count = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + auto matrix_column_type = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_type(); + + std::vector result_component_ids(matrix_column_count); + for (uint32_t i = 0; i < matrix_column_count; i++) { + // Extracts matrix column. + uint32_t matrix_extract_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_column_type), + matrix_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + std::vector float_multiplication_ids(vector_component_count); + for (uint32_t j = 0; j < vector_component_count; j++) { + // Extracts column component. + uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(vector_component_type), + column_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_extract_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}}))); + + // Multiplies corresponding vector and column components. + float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFMul, + ir_context->get_type_mgr()->GetId(vector_component_type), + float_multiplication_ids[j], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_component_ids[j]}}, + {SPV_OPERAND_TYPE_ID, {column_extract_id}}}))); + } + + // Adds the multiplication results. + std::vector float_add_ids; + uint32_t float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFAdd, + ir_context->get_type_mgr()->GetId(vector_component_type), float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}}, + {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}}))); + for (uint32_t j = 2; j < float_multiplication_ids.size(); j++) { + float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFAdd, + ir_context->get_type_mgr()->GetId(vector_component_type), + float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[j]}}, + {SPV_OPERAND_TYPE_ID, {float_add_ids[j - 2]}}}))); + } + + result_component_ids[i] = float_add_ids.back(); + } + + // The OpVectorTimesMatrix instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {result_component_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {result_component_ids[1]}); + for (uint32_t i = 2; i < result_component_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {result_component_ids[i]}}); + } + + fuzzerutil::UpdateModuleIdBound( + ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1)); +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesVector( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets matrix information. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + uint32_t matrix_column_count = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + auto matrix_column_type = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_type(); + uint32_t matrix_row_count = matrix_column_type->AsVector()->element_count(); + + // Extracts matrix columns. + uint32_t fresh_id_index = 0; + std::vector matrix_column_ids(matrix_column_count); + for (uint32_t i = 0; i < matrix_column_count; i++) { + matrix_column_ids[i] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_column_type), + matrix_column_ids[i], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + } + + // Gets vector information. + auto vector_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + auto vector_component_type = ir_context->get_type_mgr() + ->GetType(vector_instruction->type_id()) + ->AsVector() + ->element_type(); + + // Extracts vector components. + std::vector vector_component_ids(matrix_column_count); + for (uint32_t i = 0; i < matrix_column_count; i++) { + vector_component_ids[i] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(vector_component_type), + vector_component_ids[i], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + } + + std::vector result_component_ids(matrix_row_count); + for (uint32_t i = 0; i < matrix_row_count; i++) { + std::vector float_multiplication_ids(matrix_column_count); + for (uint32_t j = 0; j < matrix_column_count; j++) { + // Extracts column component. + uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(vector_component_type), + column_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_column_ids[j]}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + // Multiplies corresponding vector and column components. + float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFMul, + ir_context->get_type_mgr()->GetId(vector_component_type), + float_multiplication_ids[j], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {column_extract_id}}, + {SPV_OPERAND_TYPE_ID, {vector_component_ids[j]}}}))); + } + + // Adds the multiplication results. + std::vector float_add_ids; + uint32_t float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFAdd, + ir_context->get_type_mgr()->GetId(vector_component_type), float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}}, + {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}}))); + for (uint32_t j = 2; j < float_multiplication_ids.size(); j++) { + float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFAdd, + ir_context->get_type_mgr()->GetId(vector_component_type), + float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[j]}}, + {SPV_OPERAND_TYPE_ID, {float_add_ids[j - 2]}}}))); + } + + result_component_ids[i] = float_add_ids.back(); + } + + // The OpMatrixTimesVector instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {result_component_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {result_component_ids[1]}); + for (uint32_t i = 2; i < result_component_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {result_component_ids[i]}}); + } + + fuzzerutil::UpdateModuleIdBound( + ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1)); +} + void TransformationReplaceLinearAlgebraInstruction::ReplaceOpDot( opt::IRContext* ir_context, opt::Instruction* linear_algebra_instruction) const { diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h b/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h index 45b12626d..39dc58912 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h @@ -60,6 +60,14 @@ class TransformationReplaceLinearAlgebraInstruction : public Transformation { void ReplaceOpMatrixTimesScalar(opt::IRContext* ir_context, opt::Instruction* instruction) const; + // Replaces an OpVectorTimesMatrix instruction. + void ReplaceOpVectorTimesMatrix(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + + // Replaces an OpMatrixTimesVector instruction. + void ReplaceOpMatrixTimesVector(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + // Replaces an OpDot instruction. void ReplaceOpDot(opt::IRContext* ir_context, opt::Instruction* instruction) const; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp new file mode 100644 index 000000000..2ce7cfeeb --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp @@ -0,0 +1,245 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_parameter_with_global.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context, + uint32_t param_id) { + auto* param_inst = ir_context->get_def_use_mgr()->GetDef(param_id); + assert(param_inst && "Parameter id is invalid"); + + for (auto& function : *ir_context->module()) { + if (fuzzerutil::InstructionIsFunctionParameter(param_inst, &function)) { + return &function; + } + } + + return nullptr; +} + +} // namespace + +TransformationReplaceParameterWithGlobal:: + TransformationReplaceParameterWithGlobal( + const protobufs::TransformationReplaceParameterWithGlobal& message) + : message_(message) {} + +TransformationReplaceParameterWithGlobal:: + TransformationReplaceParameterWithGlobal( + uint32_t function_type_fresh_id, uint32_t parameter_id, + uint32_t global_variable_fresh_id) { + message_.set_function_type_fresh_id(function_type_fresh_id); + message_.set_parameter_id(parameter_id); + message_.set_global_variable_fresh_id(global_variable_fresh_id); +} + +bool TransformationReplaceParameterWithGlobal::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Check that |parameter_id| is valid. + const auto* param_inst = + ir_context->get_def_use_mgr()->GetDef(message_.parameter_id()); + if (!param_inst || param_inst->opcode() != SpvOpFunctionParameter) { + return false; + } + + // Check that function exists and is not an entry point. + const auto* function = + GetFunctionFromParameterId(ir_context, message_.parameter_id()); + if (!function || + fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) { + return false; + } + + // We already know that the function has at least one parameter - + // |parameter_id|. + + // Check that replaced parameter has valid type. + const auto* param_type = + ir_context->get_type_mgr()->GetType(param_inst->type_id()); + assert(param_type && "Parameter has invalid type"); + if (!CanReplaceFunctionParameterType(*param_type)) { + return false; + } + + // Check that initializer for the global variable exists in the module. + if (fuzzerutil::MaybeGetZeroConstant(ir_context, param_inst->type_id()) == + 0) { + return false; + } + + // Check that pointer type for the global variable exists in the module. + if (!fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(), + SpvStorageClassPrivate)) { + return false; + } + + return fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()) && + fuzzerutil::IsFreshId(ir_context, + message_.global_variable_fresh_id()) && + message_.function_type_fresh_id() != + message_.global_variable_fresh_id(); +} + +void TransformationReplaceParameterWithGlobal::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + const auto* param_inst = + ir_context->get_def_use_mgr()->GetDef(message_.parameter_id()); + assert(param_inst && "Parameter must exist"); + + // Create global variable to store parameter's value. + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): + // Mark the global variable's pointee as irrelevant if replaced parameter is + // irrelevant. + fuzzerutil::AddGlobalVariable( + ir_context, message_.global_variable_fresh_id(), + fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(), + SpvStorageClassPrivate), + SpvStorageClassPrivate, + fuzzerutil::MaybeGetZeroConstant(ir_context, param_inst->type_id())); + + auto* function = + GetFunctionFromParameterId(ir_context, message_.parameter_id()); + assert(function && "Function must exist"); + + // Insert an OpLoad instruction right after OpVariable instructions. + auto it = function->begin()->begin(); + while (it != function->begin()->end() && + !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, it)) { + ++it; + } + + assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, it) && + "Can't insert OpLoad or OpCopyMemory into the first basic block of " + "the function"); + + it.InsertBefore(MakeUnique( + ir_context, SpvOpLoad, param_inst->type_id(), param_inst->result_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}}})); + + // Calculate the index of the replaced parameter (we need to know this to + // remove operands from the OpFunctionCall). + auto params = fuzzerutil::GetParameters(ir_context, function->result_id()); + auto parameter_index = static_cast(params.size()); + for (uint32_t i = 0, n = static_cast(params.size()); i < n; ++i) { + if (params[i]->result_id() == message_.parameter_id()) { + parameter_index = i; + break; + } + } + + assert(parameter_index != params.size() && + "Parameter must exist in the function"); + + // Update all relevant OpFunctionCall instructions. + ir_context->get_def_use_mgr()->ForEachUser( + function->result_id(), + [ir_context, parameter_index, this](opt::Instruction* inst) { + if (inst->opcode() != SpvOpFunctionCall) { + return; + } + + assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, inst) && + "Can't insert OpStore right before the function call"); + + // Insert an OpStore before the OpFunctionCall. +1 since the first + // operand of OpFunctionCall is an id of the function. + inst->InsertBefore(MakeUnique( + ir_context, SpvOpStore, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}}, + {SPV_OPERAND_TYPE_ID, + {inst->GetSingleWordInOperand(parameter_index + 1)}}})); + + // +1 since the first operand of OpFunctionCall is an id of the + // function. + inst->RemoveInOperand(parameter_index + 1); + }); + + // Remove the parameter from the function. + function->RemoveParameter(message_.parameter_id()); + + // Update function's type. + auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function); + assert(old_function_type && "Function has invalid type"); + + // Preemptively add function's return type id. + std::vector type_ids = { + old_function_type->GetSingleWordInOperand(0)}; + + // +1 and -1 since the first operand is the return type id. + for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) { + if (i - 1 != parameter_index) { + type_ids.push_back(old_function_type->GetSingleWordInOperand(i)); + } + } + + if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) { + // Change the old type in place. +1 since the first operand is the result + // type id of the function. + old_function_type->RemoveInOperand(parameter_index + 1); + } else { + // Find an existing or create a new function type. + function->DefInst().SetInOperand( + 1, {fuzzerutil::FindOrCreateFunctionType( + ir_context, message_.function_type_fresh_id(), type_ids)}); + } + + // Make sure our changes are analyzed + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationReplaceParameterWithGlobal::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_replace_parameter_with_global() = message_; + return result; +} + +bool TransformationReplaceParameterWithGlobal::CanReplaceFunctionParameterType( + const opt::analysis::Type& type) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403): + // Think about other type instructions we can add here. + switch (type.kind()) { + case opt::analysis::Type::kBool: + case opt::analysis::Type::kInteger: + case opt::analysis::Type::kFloat: + case opt::analysis::Type::kArray: + case opt::analysis::Type::kMatrix: + case opt::analysis::Type::kVector: + return true; + case opt::analysis::Type::kStruct: + return std::all_of( + type.AsStruct()->element_types().begin(), + type.AsStruct()->element_types().end(), + [](const opt::analysis::Type* element_type) { + return CanReplaceFunctionParameterType(*element_type); + }); + default: + return false; + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.h b/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.h new file mode 100644 index 000000000..cd379b0f6 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.h @@ -0,0 +1,67 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceParameterWithGlobal : public Transformation { + public: + explicit TransformationReplaceParameterWithGlobal( + const protobufs::TransformationReplaceParameterWithGlobal& message); + + TransformationReplaceParameterWithGlobal(uint32_t function_type_fresh_id, + uint32_t parameter_id, + uint32_t global_variable_fresh_id); + + // - |function_type_fresh_id| is a fresh id. + // - |parameter_id| is the result id of the parameter to replace. + // - |global_variable_fresh_id| is a fresh id. + // - |function_type_fresh_id| is not equal to |global_variable_fresh_id|. + // - the function that contains |parameter_id| may not be an entry-point + // function. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Removes parameter with result id |parameter_id| from its function + // - Adds a global variable to store the value for the parameter + // - Add an OpStore instruction before each function call to + // store parameter's value into the variable + // - Adds OpLoad at the beginning of the function to load the + // value from the variable into the old parameter's id + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if the type of the parameter is supported by this + // transformation. + static bool CanReplaceFunctionParameterType(const opt::analysis::Type& type); + + private: + protobufs::TransformationReplaceParameterWithGlobal message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_ diff --git a/3rdparty/spirv-tools/source/opcode.cpp b/3rdparty/spirv-tools/source/opcode.cpp index 079def626..0a7a95dcc 100644 --- a/3rdparty/spirv-tools/source/opcode.cpp +++ b/3rdparty/spirv-tools/source/opcode.cpp @@ -666,6 +666,26 @@ bool spvOpcodeIsLinearAlgebra(SpvOp opcode) { } } +bool spvOpcodeIsImageSample(const SpvOp opcode) { + switch (opcode) { + case SpvOpImageSampleImplicitLod: + case SpvOpImageSampleExplicitLod: + case SpvOpImageSampleDrefImplicitLod: + case SpvOpImageSampleDrefExplicitLod: + case SpvOpImageSampleProjImplicitLod: + case SpvOpImageSampleProjExplicitLod: + case SpvOpImageSampleProjDrefImplicitLod: + case SpvOpImageSampleProjDrefExplicitLod: + case SpvOpImageSparseSampleImplicitLod: + case SpvOpImageSparseSampleExplicitLod: + case SpvOpImageSparseSampleDrefImplicitLod: + case SpvOpImageSparseSampleDrefExplicitLod: + return true; + default: + return false; + } +} + std::vector spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) { switch (opcode) { case SpvOpMemoryBarrier: diff --git a/3rdparty/spirv-tools/source/opcode.h b/3rdparty/spirv-tools/source/opcode.h index 0d8ec9257..9aeba76e0 100644 --- a/3rdparty/spirv-tools/source/opcode.h +++ b/3rdparty/spirv-tools/source/opcode.h @@ -137,6 +137,9 @@ bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode); // Returns true for opcodes that represents linear algebra instructions. bool spvOpcodeIsLinearAlgebra(SpvOp opcode); +// Returns true for opcodes that represents an image sample instruction. +bool spvOpcodeIsImageSample(SpvOp opcode); + // Returns a vector containing the indices of the memory semantics // operands for |opcode|. std::vector spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode); diff --git a/3rdparty/spirv-tools/source/opt/copy_prop_arrays.cpp b/3rdparty/spirv-tools/source/opt/copy_prop_arrays.cpp index b3b90da53..67a97b6af 100644 --- a/3rdparty/spirv-tools/source/opt/copy_prop_arrays.cpp +++ b/3rdparty/spirv-tools/source/opt/copy_prop_arrays.cpp @@ -29,6 +29,12 @@ const uint32_t kCompositeExtractObjectInOperand = 0; const uint32_t kTypePointerStorageClassInIdx = 0; const uint32_t kTypePointerPointeeInIdx = 1; +bool IsOpenCL100DebugDeclareOrValue(Instruction* di) { + auto dbg_opcode = di->GetOpenCL100DebugOpcode(); + return dbg_opcode == OpenCLDebugInfo100DebugDeclare || + dbg_opcode == OpenCLDebugInfo100DebugValue; +} + } // namespace Pass::Status CopyPropagateArrays::Process() { @@ -188,6 +194,8 @@ bool CopyPropagateArrays::HasValidReferencesOnly(Instruction* ptr_inst, return ptr_inst->opcode() == SpvOpVariable && store_inst->GetSingleWordInOperand(kStorePointerInOperand) == ptr_inst->result_id(); + } else if (IsOpenCL100DebugDeclareOrValue(use)) { + return true; } // Some other instruction. Be conservative. return false; @@ -492,6 +500,8 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst, const_mgr, type](Instruction* use, uint32_t) { + if (IsOpenCL100DebugDeclareOrValue(use)) return true; + switch (use->opcode()) { case SpvOpLoad: { analysis::Pointer* pointer_type = type->AsPointer(); @@ -565,6 +575,7 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst, } }); } + void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, Instruction* new_ptr_inst) { analysis::TypeManager* type_mgr = context()->get_type_mgr(); @@ -580,6 +591,52 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, for (auto pair : uses) { Instruction* use = pair.first; uint32_t index = pair.second; + + if (use->IsOpenCL100DebugInstr()) { + switch (use->GetOpenCL100DebugOpcode()) { + case OpenCLDebugInfo100DebugDeclare: { + if (new_ptr_inst->opcode() == SpvOpVariable || + new_ptr_inst->opcode() == SpvOpFunctionParameter) { + context()->ForgetUses(use); + use->SetOperand(index, {new_ptr_inst->result_id()}); + context()->AnalyzeUses(use); + } else { + // Based on the spec, we cannot use a pointer other than OpVariable + // or OpFunctionParameter for DebugDeclare. We have to use + // DebugValue with Deref. + + context()->ForgetUses(use); + + // Change DebugDeclare to DebugValue. + use->SetOperand( + index - 2, + {static_cast(OpenCLDebugInfo100DebugValue)}); + use->SetOperand(index, {new_ptr_inst->result_id()}); + + // Add Deref operation. + Instruction* dbg_expr = + def_use_mgr->GetDef(use->GetSingleWordOperand(index + 1)); + auto* deref_expr_instr = + context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr); + use->SetOperand(index + 1, {deref_expr_instr->result_id()}); + + context()->AnalyzeUses(deref_expr_instr); + context()->AnalyzeUses(use); + } + break; + } + case OpenCLDebugInfo100DebugValue: + context()->ForgetUses(use); + use->SetOperand(index, {new_ptr_inst->result_id()}); + context()->AnalyzeUses(use); + break; + default: + assert(false && "Don't know how to rewrite instruction"); + break; + } + continue; + } + switch (use->opcode()) { case SpvOpLoad: { // Replace the actual use. diff --git a/3rdparty/spirv-tools/source/opt/debug_info_manager.cpp b/3rdparty/spirv-tools/source/opt/debug_info_manager.cpp index 6dc3e16a4..55e3f4a5e 100644 --- a/3rdparty/spirv-tools/source/opt/debug_info_manager.cpp +++ b/3rdparty/spirv-tools/source/opt/debug_info_manager.cpp @@ -36,6 +36,7 @@ static const uint32_t kDebugValueOperandExpressionIndex = 6; static const uint32_t kDebugOperationOperandOperationIndex = 4; static const uint32_t kOpVariableOperandStorageClassIndex = 2; static const uint32_t kDebugLocalVariableOperandParentIndex = 9; +static const uint32_t kDebugOperationOperandOpCodeIndex = 4; namespace spvtools { namespace opt { @@ -244,6 +245,51 @@ uint32_t DebugInfoManager::BuildDebugInlinedAtChain( return chain_head_id; } +Instruction* DebugInfoManager::GetDebugOperationWithDeref() { + if (deref_operation_ != nullptr) return deref_operation_; + + uint32_t result_id = context()->TakeNextId(); + std::unique_ptr deref_operation(new Instruction( + context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), + result_id, + { + {SPV_OPERAND_TYPE_ID, + {context() + ->get_feature_mgr() + ->GetExtInstImportId_OpenCL100DebugInfo()}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(OpenCLDebugInfo100DebugOperation)}}, + {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION, + {static_cast(OpenCLDebugInfo100Deref)}}, + })); + + // Add to the front of |ext_inst_debuginfo_|. + deref_operation_ = + context()->module()->ext_inst_debuginfo_begin()->InsertBefore( + std::move(deref_operation)); + + RegisterDbgInst(deref_operation_); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_operation_); + return deref_operation_; +} + +Instruction* DebugInfoManager::DerefDebugExpression(Instruction* dbg_expr) { + assert(dbg_expr->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugExpression); + std::unique_ptr deref_expr(dbg_expr->Clone(context())); + deref_expr->SetResultId(context()->TakeNextId()); + deref_expr->InsertOperand( + kDebugExpressOperandOperationIndex, + {SPV_OPERAND_TYPE_ID, {GetDebugOperationWithDeref()->result_id()}}); + auto* deref_expr_instr = + context()->ext_inst_debuginfo_end()->InsertBefore(std::move(deref_expr)); + AnalyzeDebugInst(deref_expr_instr); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_expr_instr); + return deref_expr_instr; +} + Instruction* DebugInfoManager::GetDebugInfoNone() { if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_; @@ -323,6 +369,29 @@ Instruction* DebugInfoManager::CloneDebugInlinedAt(uint32_t clone_inlined_at_id, std::move(new_inlined_at)); } +bool DebugInfoManager::IsDebugDeclared(uint32_t variable_id) { + auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id); + return dbg_decl_itr != var_id_to_dbg_decl_.end(); +} + +void DebugInfoManager::KillDebugDeclares(uint32_t variable_id) { + bool done = false; + while (!done) { + Instruction* kill_inst = nullptr; + auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id); + if (dbg_decl_itr != var_id_to_dbg_decl_.end()) { + for (auto dbg_decl : dbg_decl_itr->second) { + kill_inst = dbg_decl; + break; + } + } + if (kill_inst) + context()->KillInst(kill_inst); + else + done = true; + } +} + uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) { auto dbg_scope_itr = id_to_dbg_inst_.find(child_scope); assert(dbg_scope_itr != id_to_dbg_inst_.end()); @@ -434,6 +503,11 @@ void DebugInfoManager::AddDebugValue(Instruction* scope_and_line, AnalyzeDebugInst(added_dbg_value); if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_value); + if (context()->AreAnalysesValid( + IRContext::Analysis::kAnalysisInstrToBlockMapping)) { + auto insert_blk = context()->get_instr_block(insert_before); + context()->set_instr_block(added_dbg_value, insert_blk); + } } } @@ -472,8 +546,7 @@ uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare( } void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) { - if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax) - return; + if (!dbg_inst->IsOpenCL100DebugInstr()) return; RegisterDbgInst(dbg_inst); @@ -484,6 +557,13 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) { RegisterDbgFunction(dbg_inst); } + if (deref_operation_ == nullptr && + dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation && + dbg_inst->GetSingleWordOperand(kDebugOperationOperandOpCodeIndex) == + OpenCLDebugInfo100Deref) { + deref_operation_ = dbg_inst; + } + if (debug_info_none_inst_ == nullptr && dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) { debug_info_none_inst_ = dbg_inst; @@ -505,6 +585,7 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) { } void DebugInfoManager::AnalyzeDebugInsts(Module& module) { + deref_operation_ = nullptr; debug_info_none_inst_ = nullptr; empty_debug_expr_inst_ = nullptr; module.ForEachInst([this](Instruction* cpi) { AnalyzeDebugInst(cpi); }); @@ -513,8 +594,7 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) { // list. if (empty_debug_expr_inst_ != nullptr && empty_debug_expr_inst_->PreviousNode() != nullptr && - empty_debug_expr_inst_->PreviousNode()->GetOpenCL100DebugOpcode() != - OpenCLDebugInfo100InstructionsMax) { + empty_debug_expr_inst_->PreviousNode()->IsOpenCL100DebugInstr()) { empty_debug_expr_inst_->InsertBefore( &*context()->module()->ext_inst_debuginfo_begin()); } @@ -523,16 +603,14 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) { // list. if (debug_info_none_inst_ != nullptr && debug_info_none_inst_->PreviousNode() != nullptr && - debug_info_none_inst_->PreviousNode()->GetOpenCL100DebugOpcode() != - OpenCLDebugInfo100InstructionsMax) { + debug_info_none_inst_->PreviousNode()->IsOpenCL100DebugInstr()) { debug_info_none_inst_->InsertBefore( &*context()->module()->ext_inst_debuginfo_begin()); } } void DebugInfoManager::ClearDebugInfo(Instruction* instr) { - if (instr == nullptr || - instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax) { + if (instr == nullptr || !instr->IsOpenCL100DebugInstr()) { return; } @@ -554,6 +632,22 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { } } + if (deref_operation_ == instr) { + deref_operation_ = nullptr; + for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin(); + dbg_instr_itr != context()->module()->ext_inst_debuginfo_end(); + ++dbg_instr_itr) { + if (instr != &*dbg_instr_itr && + dbg_instr_itr->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugOperation && + dbg_instr_itr->GetSingleWordOperand( + kDebugOperationOperandOpCodeIndex) == OpenCLDebugInfo100Deref) { + deref_operation_ = &*dbg_instr_itr; + break; + } + } + } + if (debug_info_none_inst_ == instr) { debug_info_none_inst_ = nullptr; for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin(); @@ -563,6 +657,7 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { dbg_instr_itr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) { debug_info_none_inst_ = &*dbg_instr_itr; + break; } } } @@ -574,6 +669,7 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) { ++dbg_instr_itr) { if (instr != &*dbg_instr_itr && IsEmptyDebugExpression(&*dbg_instr_itr)) { empty_debug_expr_inst_ = &*dbg_instr_itr; + break; } } } diff --git a/3rdparty/spirv-tools/source/opt/debug_info_manager.h b/3rdparty/spirv-tools/source/opt/debug_info_manager.h index 7353d56db..d7c270037 100644 --- a/3rdparty/spirv-tools/source/opt/debug_info_manager.h +++ b/3rdparty/spirv-tools/source/opt/debug_info_manager.h @@ -95,6 +95,10 @@ class DebugInfoManager { uint32_t CreateDebugInlinedAt(const Instruction* line, const DebugScope& scope); + // Clones DebugExpress instruction |dbg_expr| and add Deref Operation + // in the front of the Operation list of |dbg_expr|. + Instruction* DerefDebugExpression(Instruction* dbg_expr); + // Returns a DebugInfoNone instruction. Instruction* GetDebugInfoNone(); @@ -129,6 +133,12 @@ class DebugInfoManager { uint32_t BuildDebugInlinedAtChain(uint32_t callee_inlined_at, DebugInlinedAtContext* inlined_at_ctx); + // Return true if |variable_id| has DebugDeclare or DebugVal. + bool IsDebugDeclared(uint32_t variable_id); + + // Kill all DebugDeclares for |variable_id| + void KillDebugDeclares(uint32_t variable_id); + // Generates a DebugValue instruction with value |value_id| for every local // variable that is in the scope of |scope_and_line| and whose memory is // |variable_id| and inserts it after the instruction |insert_pos|. @@ -149,6 +159,9 @@ class DebugInfoManager { // does not exists. Instruction* GetDbgInst(uint32_t id); + // Returns a DebugOperation instruction with OpCode Deref. + Instruction* GetDebugOperationWithDeref(); + // Registers the debug instruction |inst| into |id_to_dbg_inst_| using id of // |inst| as a key. void RegisterDbgInst(Instruction* inst); @@ -197,6 +210,9 @@ class DebugInfoManager { std::unordered_map> var_id_to_dbg_decl_; + // DebugOperation whose OpCode is OpenCLDebugInfo100Deref. + Instruction* deref_operation_; + // DebugInfoNone instruction. We need only a single DebugInfoNone. // To reuse the existing one, we keep it using this member variable. Instruction* debug_info_none_inst_; diff --git a/3rdparty/spirv-tools/source/opt/instruction.h b/3rdparty/spirv-tools/source/opt/instruction.h index 7d8fed847..e99a5ae8f 100644 --- a/3rdparty/spirv-tools/source/opt/instruction.h +++ b/3rdparty/spirv-tools/source/opt/instruction.h @@ -306,6 +306,10 @@ class Instruction : public utils::IntrusiveNodeBase { void RemoveOperand(uint32_t index) { operands_.erase(operands_.begin() + index); } + // Insert an operand before the |index|-th operand + void InsertOperand(uint32_t index, Operand&& operand) { + operands_.insert(operands_.begin() + index, operand); + } // The following methods are similar to the above, but are for in operands. uint32_t NumInOperands() const { @@ -535,6 +539,11 @@ class Instruction : public utils::IntrusiveNodeBase { // OpenCLDebugInfo100InstructionsMax. OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const; + // Returns true if it is an OpenCL.DebugInfo.100 instruction. + bool IsOpenCL100DebugInstr() const { + return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax; + } + // Dump this instruction on stderr. Useful when running interactive // debuggers. void Dump() const; diff --git a/3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp b/3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp index 401dad8e3..57572825d 100644 --- a/3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp @@ -31,6 +31,11 @@ const uint32_t kStoreValIdInIdx = 1; bool LocalSingleBlockLoadStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) { if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true; if (get_def_use_mgr()->WhileEachUser(ptrId, [this](Instruction* user) { + auto dbg_op = user->GetOpenCL100DebugOpcode(); + if (dbg_op == OpenCLDebugInfo100DebugDeclare || + dbg_op == OpenCLDebugInfo100DebugValue) { + return true; + } SpvOp op = user->opcode(); if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) { if (!HasOnlySupportedRefs(user->result_id())) { @@ -73,10 +78,12 @@ bool LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElim( // variable. if (ptrInst->opcode() == SpvOpVariable) { // If a previous store to same variable, mark the store - // for deletion if not still used. + // for deletion if not still used. Don't delete store + // if debugging; let ssa-rewrite and DCE handle it auto prev_store = var2store_.find(varId); if (prev_store != var2store_.end() && - instructions_to_save.count(prev_store->second) == 0) { + instructions_to_save.count(prev_store->second) == 0 && + !context()->get_debug_info_mgr()->IsDebugDeclared(varId)) { instructions_to_kill.push_back(prev_store->second); modified = true; } diff --git a/3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp b/3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp index 3f6185347..6626d87f3 100644 --- a/3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp @@ -133,7 +133,27 @@ bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) { return false; } - return RewriteLoads(store_inst, users); + bool all_rewritten; + bool modified = RewriteLoads(store_inst, users, &all_rewritten); + + // If all uses are rewritten and the variable has a DebugDeclare and the + // variable is not an aggregate, add a DebugValue after the store and remove + // the DebugDeclare. + uint32_t var_id = var_inst->result_id(); + if (all_rewritten && + context()->get_debug_info_mgr()->IsDebugDeclared(var_id)) { + const analysis::Type* var_type = + context()->get_type_mgr()->GetType(var_inst->type_id()); + const analysis::Type* store_type = var_type->AsPointer()->pointee_type(); + if (!(store_type->AsStruct() || store_type->AsArray())) { + context()->get_debug_info_mgr()->AddDebugValue( + store_inst, var_id, store_inst->GetSingleWordInOperand(1), + store_inst); + context()->get_debug_info_mgr()->KillDebugDeclares(var_id); + } + } + + return modified; } Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses( @@ -172,6 +192,14 @@ Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses( case SpvOpName: case SpvOpCopyObject: break; + case SpvOpExtInst: { + auto dbg_op = user->GetOpenCL100DebugOpcode(); + if (dbg_op == OpenCLDebugInfo100DebugDeclare || + dbg_op == OpenCLDebugInfo100DebugValue) { + break; + } + return nullptr; + } default: if (!user->IsDecoration()) { // Don't know if this instruction modifies the variable. @@ -218,7 +246,8 @@ bool LocalSingleStoreElimPass::FeedsAStore(Instruction* inst) const { } bool LocalSingleStoreElimPass::RewriteLoads( - Instruction* store_inst, const std::vector& uses) { + Instruction* store_inst, const std::vector& uses, + bool* all_rewritten) { BasicBlock* store_block = context()->get_instr_block(store_inst); DominatorAnalysis* dominator_analysis = context()->GetDominatorAnalysis(store_block->GetParent()); @@ -229,16 +258,22 @@ bool LocalSingleStoreElimPass::RewriteLoads( else stored_id = store_inst->GetSingleWordInOperand(kVariableInitIdInIdx); - std::vector uses_in_store_block; + *all_rewritten = true; bool modified = false; for (Instruction* use : uses) { - if (use->opcode() == SpvOpLoad) { - if (dominator_analysis->Dominates(store_inst, use)) { - modified = true; - context()->KillNamesAndDecorates(use->result_id()); - context()->ReplaceAllUsesWith(use->result_id(), stored_id); - context()->KillInst(use); - } + if (use->opcode() == SpvOpStore) continue; + auto dbg_op = use->GetOpenCL100DebugOpcode(); + if (dbg_op == OpenCLDebugInfo100DebugDeclare || + dbg_op == OpenCLDebugInfo100DebugValue) + continue; + if (use->opcode() == SpvOpLoad && + dominator_analysis->Dominates(store_inst, use)) { + modified = true; + context()->KillNamesAndDecorates(use->result_id()); + context()->ReplaceAllUsesWith(use->result_id(), stored_id); + context()->KillInst(use); + } else { + *all_rewritten = false; } } diff --git a/3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.h b/3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.h index a7cdd1920..d66a441a0 100644 --- a/3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.h +++ b/3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.h @@ -89,9 +89,10 @@ class LocalSingleStoreElimPass : public Pass { bool FeedsAStore(Instruction* inst) const; // Replaces all of the loads in |uses| by the value stored in |store_inst|. - // The load instructions are then killed. + // The load instructions are then killed. |all_rewritten| is true iff all + // uses have been rewritten. bool RewriteLoads(Instruction* store_inst, - const std::vector& uses); + const std::vector& uses, bool* all_rewritten); // Extensions supported by this pass. std::unordered_set extensions_allowlist_; diff --git a/3rdparty/spirv-tools/source/opt/pass.h b/3rdparty/spirv-tools/source/opt/pass.h index a11920792..a8c9c4b43 100644 --- a/3rdparty/spirv-tools/source/opt/pass.h +++ b/3rdparty/spirv-tools/source/opt/pass.h @@ -71,10 +71,6 @@ class Pass { return context()->get_def_use_mgr(); } - analysis::DebugInfoManager* get_debug_info_mgr() const { - return context()->get_debug_info_mgr(); - } - analysis::DecorationManager* get_decoration_mgr() const { return context()->get_decoration_mgr(); } diff --git a/3rdparty/spirv-tools/source/opt/reduce_load_size.cpp b/3rdparty/spirv-tools/source/opt/reduce_load_size.cpp index 7b5a015c4..c58adf47c 100644 --- a/3rdparty/spirv-tools/source/opt/reduce_load_size.cpp +++ b/3rdparty/spirv-tools/source/opt/reduce_load_size.cpp @@ -112,10 +112,10 @@ bool ReduceLoadSize::ReplaceExtract(Instruction* inst) { Instruction* new_access_chain = ir_builder.AddAccessChain( pointer_to_result_type_id, composite_inst->GetSingleWordInOperand(kLoadPointerInIdx), ids); - Instruction* new_laod = + Instruction* new_load = ir_builder.AddLoad(inst->type_id(), new_access_chain->result_id()); - context()->ReplaceAllUsesWith(inst->result_id(), new_laod->result_id()); + context()->ReplaceAllUsesWith(inst->result_id(), new_load->result_id()); context()->KillInst(inst); return true; } @@ -139,6 +139,7 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) { all_elements_used = !def_use_mgr->WhileEachUser(op_inst, [&elements_used](Instruction* use) { + if (use->IsOpenCL100DebugInstr()) return true; if (use->opcode() != SpvOpCompositeExtract || use->NumInOperands() == 1) { return false; diff --git a/3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.cpp b/3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.cpp index 6eed1fdd4..1477db44e 100644 --- a/3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.cpp @@ -307,7 +307,8 @@ void SSARewriter::ProcessStore(Instruction* inst, BasicBlock* bb) { } if (pass_->IsTargetVar(var_id)) { WriteVariable(var_id, bb, val_id); - pass_->get_debug_info_mgr()->AddDebugValue(inst, var_id, val_id, inst); + pass_->context()->get_debug_info_mgr()->AddDebugValue(inst, var_id, val_id, + inst); #if SSA_REWRITE_DEBUGGING_LEVEL > 1 std::cerr << "\tFound store '%" << var_id << " = %" << val_id << "': " @@ -490,7 +491,7 @@ bool SSARewriter::ApplyReplacements() { // Add DebugValue for the new OpPhi instruction. insert_it->SetDebugScope(local_var->GetDebugScope()); - pass_->get_debug_info_mgr()->AddDebugValue( + pass_->context()->get_debug_info_mgr()->AddDebugValue( &*insert_it, phi_candidate->var_id(), phi_candidate->result_id(), &*insert_it); diff --git a/3rdparty/spirv-tools/source/opt/vector_dce.cpp b/3rdparty/spirv-tools/source/opt/vector_dce.cpp index 92532e31a..14a55c1ea 100644 --- a/3rdparty/spirv-tools/source/opt/vector_dce.cpp +++ b/3rdparty/spirv-tools/source/opt/vector_dce.cpp @@ -52,6 +52,9 @@ void VectorDCE::FindLiveComponents(Function* function, // components are live because of arbitrary nesting of structs. function->ForEachInst( [&work_list, this, live_components](Instruction* current_inst) { + if (current_inst->IsOpenCL100DebugInstr()) { + return; + } if (!HasVectorOrScalarResult(current_inst) || !context()->IsCombinatorInstruction(current_inst)) { MarkUsesAsLive(current_inst, all_components_live_, live_components, @@ -297,51 +300,60 @@ bool VectorDCE::HasScalarResult(const Instruction* inst) const { bool VectorDCE::RewriteInstructions( Function* function, const VectorDCE::LiveComponentMap& live_components) { bool modified = false; - function->ForEachInst( - [&modified, this, live_components](Instruction* current_inst) { - if (!context()->IsCombinatorInstruction(current_inst)) { - return; - } - auto live_component = live_components.find(current_inst->result_id()); - if (live_component == live_components.end()) { - // If this instruction is not in live_components then it does not - // produce a vector, or it is never referenced and ADCE will remove - // it. No point in trying to differentiate. - return; - } + // Kill DebugValue in the middle of the instruction iteration will result + // in accessing a dangling pointer. We keep dead DebugValue instructions + // in |dead_dbg_value| to kill them once after the iteration. + std::vector dead_dbg_value; - // If no element in the current instruction is used replace it with an - // OpUndef. - if (live_component->second.Empty()) { - modified = true; - uint32_t undef_id = this->Type2Undef(current_inst->type_id()); - context()->KillNamesAndDecorates(current_inst); - context()->ReplaceAllUsesWith(current_inst->result_id(), undef_id); - context()->KillInst(current_inst); - return; - } + function->ForEachInst([&modified, this, live_components, + &dead_dbg_value](Instruction* current_inst) { + if (!context()->IsCombinatorInstruction(current_inst)) { + return; + } - switch (current_inst->opcode()) { - case SpvOpCompositeInsert: - modified |= - RewriteInsertInstruction(current_inst, live_component->second); - break; - case SpvOpCompositeConstruct: - // TODO: The members that are not live can be replaced by an undef - // or constant. This will remove uses of those values, and possibly - // create opportunities for ADCE. - break; - default: - // Do nothing. - break; - } - }); + auto live_component = live_components.find(current_inst->result_id()); + if (live_component == live_components.end()) { + // If this instruction is not in live_components then it does not + // produce a vector, or it is never referenced and ADCE will remove + // it. No point in trying to differentiate. + return; + } + + // If no element in the current instruction is used replace it with an + // OpUndef. + if (live_component->second.Empty()) { + modified = true; + MarkDebugValueUsesAsDead(current_inst, &dead_dbg_value); + uint32_t undef_id = this->Type2Undef(current_inst->type_id()); + context()->KillNamesAndDecorates(current_inst); + context()->ReplaceAllUsesWith(current_inst->result_id(), undef_id); + context()->KillInst(current_inst); + return; + } + + switch (current_inst->opcode()) { + case SpvOpCompositeInsert: + modified |= RewriteInsertInstruction( + current_inst, live_component->second, &dead_dbg_value); + break; + case SpvOpCompositeConstruct: + // TODO: The members that are not live can be replaced by an undef + // or constant. This will remove uses of those values, and possibly + // create opportunities for ADCE. + break; + default: + // Do nothing. + break; + } + }); + for (auto* i : dead_dbg_value) context()->KillInst(i); return modified; } bool VectorDCE::RewriteInsertInstruction( - Instruction* current_inst, const utils::BitVector& live_components) { + Instruction* current_inst, const utils::BitVector& live_components, + std::vector* dead_dbg_value) { // If the value being inserted is not live, then we can skip the insert. if (current_inst->NumInOperands() == 2) { @@ -355,6 +367,7 @@ bool VectorDCE::RewriteInsertInstruction( uint32_t insert_index = current_inst->GetSingleWordInOperand(2); if (!live_components.Get(insert_index)) { + MarkDebugValueUsesAsDead(current_inst, dead_dbg_value); context()->KillNamesAndDecorates(current_inst->result_id()); uint32_t composite_id = current_inst->GetSingleWordInOperand(kInsertCompositeIdInIdx); @@ -377,6 +390,15 @@ bool VectorDCE::RewriteInsertInstruction( return false; } +void VectorDCE::MarkDebugValueUsesAsDead( + Instruction* composite, std::vector* dead_dbg_value) { + context()->get_def_use_mgr()->ForEachUser( + composite, [&dead_dbg_value](Instruction* use) { + if (use->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) + dead_dbg_value->push_back(use); + }); +} + void VectorDCE::AddItemToWorkListIfNeeded( WorkListItem work_item, VectorDCE::LiveComponentMap* live_components, std::vector* work_list) { diff --git a/3rdparty/spirv-tools/source/opt/vector_dce.h b/3rdparty/spirv-tools/source/opt/vector_dce.h index 4f039c53f..0df9aee13 100644 --- a/3rdparty/spirv-tools/source/opt/vector_dce.h +++ b/3rdparty/spirv-tools/source/opt/vector_dce.h @@ -73,6 +73,11 @@ class VectorDCE : public MemPass { bool RewriteInstructions(Function* function, const LiveComponentMap& live_components); + // Makrs all DebugValue instructions that use |composite| for their values as + // dead instructions by putting them into |dead_dbg_value|. + void MarkDebugValueUsesAsDead(Instruction* composite, + std::vector* dead_dbg_value); + // Rewrites the OpCompositeInsert instruction |current_inst| to avoid // unnecessary computes given that the only components of the result that are // live are |live_components|. @@ -83,7 +88,8 @@ class VectorDCE : public MemPass { // If the composite input to |current_inst| is not live, then it is replaced // by and OpUndef in |current_inst|. bool RewriteInsertInstruction(Instruction* current_inst, - const utils::BitVector& live_components); + const utils::BitVector& live_components, + std::vector* dead_dbg_value); // Returns true if the result of |inst| is a vector or a scalar. bool HasVectorOrScalarResult(const Instruction* inst) const; diff --git a/3rdparty/spirv-tools/utils/check_copyright.py b/3rdparty/spirv-tools/utils/check_copyright.py index 4467a3251..39d27cb7d 100755 --- a/3rdparty/spirv-tools/utils/check_copyright.py +++ b/3rdparty/spirv-tools/utils/check_copyright.py @@ -35,7 +35,8 @@ AUTHORS = ['The Khronos Group Inc.', 'Samsung Inc', 'André Perez Maselco', 'Vasyl Teliman', - 'Advanced Micro Devices, Inc.'] + 'Advanced Micro Devices, Inc.', + 'Stefano Milizia'] CURRENT_YEAR='2020' YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020)'