From fd71b4060d8e2dfda02a40888c3fbe0a1581e09c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=91=D1=80=D0=B0=D0=BD=D0=B8=D0=BC=D0=B8=D1=80=20=D0=9A?= =?UTF-8?q?=D0=B0=D1=80=D0=B0=D1=9F=D0=B8=D1=9B?= Date: Sun, 26 Jul 2020 17:02:28 -0700 Subject: [PATCH] Updated spirv-tools. --- 3rdparty/spirv-tools/CHANGES | 38 +- .../include/generated/build-version.inc | 2 +- .../include/generated/core.insts-unified1.inc | 7 +- .../include/generated/enum_string_mapping.inc | 12 +- .../include/generated/extension_enum.inc | 2 + .../generated/operand.kinds-unified1.inc | 5 +- .../include/spirv-tools/optimizer.hpp | 6 +- .../spirv-tools/source/fuzz/CMakeLists.txt | 16 + .../spirv-tools/source/fuzz/fact_manager.cpp | 52 ++- .../spirv-tools/source/fuzz/fact_manager.h | 19 +- 3rdparty/spirv-tools/source/fuzz/fuzzer.cpp | 4 + .../source/fuzz/fuzzer_context.cpp | 18 + .../spirv-tools/source/fuzz/fuzzer_context.h | 26 ++ .../spirv-tools/source/fuzz/fuzzer_pass.cpp | 189 +++------ .../spirv-tools/source/fuzz/fuzzer_pass.h | 60 ++- .../fuzz/fuzzer_pass_add_access_chains.cpp | 2 +- .../fuzz/fuzzer_pass_add_composite_types.cpp | 2 +- .../fuzz/fuzzer_pass_add_copy_memory.cpp | 2 +- .../fuzz/fuzzer_pass_add_dead_blocks.cpp | 2 +- .../fuzz/fuzzer_pass_add_dead_breaks.cpp | 11 +- .../fuzz/fuzzer_pass_add_dead_continues.cpp | 11 +- .../fuzzer_pass_add_equation_instructions.cpp | 7 +- .../fuzz/fuzzer_pass_add_function_calls.cpp | 214 ++++------ .../fuzz/fuzzer_pass_add_function_calls.h | 8 - .../fuzz/fuzzer_pass_add_global_variables.cpp | 2 +- ...ass_add_image_sample_unused_components.cpp | 2 +- .../fuzz/fuzzer_pass_add_local_variables.cpp | 2 +- .../fuzz/fuzzer_pass_add_parameters.cpp | 6 +- .../fuzzer_pass_add_relaxed_decorations.cpp | 55 +++ .../fuzzer_pass_add_relaxed_decorations.h | 39 ++ .../source/fuzz/fuzzer_pass_add_synonyms.cpp | 13 +- ...r_pass_add_vector_shuffle_instructions.cpp | 16 +- .../fuzz/fuzzer_pass_apply_id_synonyms.cpp | 3 + .../fuzz/fuzzer_pass_construct_composites.cpp | 21 +- .../source/fuzz/fuzzer_pass_copy_objects.cpp | 8 +- .../fuzz/fuzzer_pass_donate_modules.cpp | 40 +- ...r_pass_interchange_zero_like_constants.cpp | 13 +- .../source/fuzz/fuzzer_pass_merge_blocks.cpp | 5 +- .../fuzz/fuzzer_pass_obfuscate_constants.cpp | 6 +- ...fuzzer_pass_push_ids_through_variables.cpp | 16 +- ...eplace_copy_memories_with_loads_stores.cpp | 58 +++ ..._replace_copy_memories_with_loads_stores.h | 41 ++ ...replace_copy_objects_with_stores_loads.cpp | 83 ++++ ...s_replace_copy_objects_with_stores_loads.h | 41 ++ ...zer_pass_replace_parameter_with_global.cpp | 2 +- ...fuzzer_pass_replace_params_with_struct.cpp | 115 ++++++ .../fuzzer_pass_replace_params_with_struct.h | 40 ++ .../spirv-tools/source/fuzz/fuzzer_util.cpp | 383 ++++++++++++------ .../spirv-tools/source/fuzz/fuzzer_util.h | 106 +++-- .../source/fuzz/protobufs/spvtoolsfuzz.proto | 101 +++++ .../source/fuzz/transformation.cpp | 19 + .../transformation_add_constant_boolean.cpp | 22 +- .../transformation_add_constant_boolean.h | 5 +- .../transformation_add_constant_composite.cpp | 11 +- .../transformation_add_constant_composite.h | 10 +- .../transformation_add_constant_scalar.cpp | 12 +- .../fuzz/transformation_add_constant_scalar.h | 4 +- .../fuzz/transformation_add_dead_block.cpp | 11 +- .../fuzz/transformation_add_dead_break.cpp | 19 +- .../fuzz/transformation_add_dead_break.h | 3 +- .../fuzz/transformation_add_dead_continue.cpp | 20 +- .../fuzz/transformation_add_dead_continue.h | 3 +- .../fuzz/transformation_add_parameter.cpp | 11 +- .../transformation_add_relaxed_decoration.cpp | 146 +++++++ .../transformation_add_relaxed_decoration.h | 62 +++ .../fuzz/transformation_add_synonym.cpp | 50 ++- .../source/fuzz/transformation_add_synonym.h | 12 +- .../transformation_composite_construct.cpp | 36 +- .../fuzz/transformation_composite_extract.cpp | 34 +- .../fuzz/transformation_composite_extract.h | 3 +- .../transformation_equation_instruction.cpp | 6 +- ...sformation_permute_function_parameters.cpp | 78 ++-- ...ransformation_push_id_through_variable.cpp | 24 +- .../transformation_push_id_through_variable.h | 4 +- ...sformation_record_synonymous_constants.cpp | 115 +++--- ...ansformation_record_synonymous_constants.h | 17 +- ...on_replace_copy_memory_with_load_store.cpp | 127 ++++++ ...tion_replace_copy_memory_with_load_store.h | 57 +++ ...on_replace_copy_object_with_store_load.cpp | 155 +++++++ ...tion_replace_copy_object_with_store_load.h | 63 +++ ...ormation_replace_parameter_with_global.cpp | 120 +++--- ...nsformation_replace_params_with_struct.cpp | 306 ++++++++++++++ ...ransformation_replace_params_with_struct.h | 83 ++++ .../fuzz/transformation_vector_shuffle.cpp | 29 +- .../fuzz/transformation_vector_shuffle.h | 5 +- 3rdparty/spirv-tools/source/opcode.cpp | 4 +- .../opt/aggressive_dead_code_elim_pass.cpp | 48 +++ 3rdparty/spirv-tools/source/opt/code_sink.cpp | 1 + .../source/opt/debug_info_manager.h | 10 +- .../spirv-tools/source/opt/dominator_tree.cpp | 3 +- .../spirv-tools/source/opt/inline_pass.cpp | 13 +- 3rdparty/spirv-tools/source/opt/inline_pass.h | 5 +- .../opt/local_access_chain_convert_pass.cpp | 1 + .../opt/local_single_block_elim_pass.cpp | 1 + .../opt/local_single_store_elim_pass.cpp | 1 + .../spirv-tools/source/opt/loop_unroller.cpp | 3 +- .../source/opt/merge_return_pass.cpp | 48 ++- 3rdparty/spirv-tools/source/opt/reflect.h | 3 +- .../source/opt/replace_invalid_opc.cpp | 1 + .../spirv-tools/source/opt/wrap_opkill.cpp | 53 ++- 3rdparty/spirv-tools/source/opt/wrap_opkill.h | 19 +- .../source/val/validate_atomics.cpp | 65 ++- .../spirv-tools/source/val/validate_cfg.cpp | 6 + .../source/val/validate_instruction.cpp | 7 +- 3rdparty/spirv-tools/utils/roll_deps.sh | 8 +- 105 files changed, 3057 insertions(+), 857 deletions(-) create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.h create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.h create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h diff --git a/3rdparty/spirv-tools/CHANGES b/3rdparty/spirv-tools/CHANGES index a46755ecc..d10f55be3 100644 --- a/3rdparty/spirv-tools/CHANGES +++ b/3rdparty/spirv-tools/CHANGES @@ -1,7 +1,41 @@ Revision history for SPIRV-Tools -v2020.4-dev 2020-05-27 - - Start v2020.4-dev +v2020.5 2020-07-22 + - Start SPIRV-Tools v2020.5 + +v2020.4 2020-07-22 + - General + - Changed variable names to be more descriptive (#3433) + - Add support to GPU-AV instrumentation for Task and Mesh shaders (#3512) + - Permit Simple and GLSL450 memory model in WEBGPU_0 (#3463) + - Support SPV_KHR_terminate_invocation (#3568) + - Optimizer + - Preserving debug information in optimizations + (#3389,#3420,#3425,#3356,#3459,#3444,#3492,#3451,#3497i,#3498,#3542) + - Eliminate branches with condition of OpConstantNull (#3438) + - Use structured order to unroll loops. (#3443) + - Updated desc_sroa to support flattening structures (#3448) + - Support OpCompositeExtract pattern in desc_sroa (#3456) + - Fix ADCE pass bug for mulitple entries (#3470) + - Sink pointer instructions in merge return (#3569) + - Validator + - Validate location assignments (#3308) + - Fix reachability in the validator (#3541) + - Reduce + - Fuzz + - Add support for OpSpecConstant* (#3373) + - Add replace linear algebra instruction transformation (#3402) + - Implement vector shuffle fuzzer pass (#3412) + - Swap operands in OpBranchConditional (#3423) + - Permute OpPhi instruction operands (#3421) + - Add FuzzerPassAddCopyMemoryInstructions (#3391) + - TransformationInvertComparisonOperator (#3475) + - Add variables with workgroup storage class (#3485) + - Add image sample unused components transformation (#3439) + - TransformationReplaceParameterWithGlobal (#3434) + - Support adding dead break from back-edge block (#3519) + - Fuzzer pass to interchange zero-like constants (#3524) + - Linker v2020.3 2020-05-27 - General diff --git a/3rdparty/spirv-tools/include/generated/build-version.inc b/3rdparty/spirv-tools/include/generated/build-version.inc index 689de7a8c..15b72c088 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 8d5c7eae3a89ee8b898c9771119abb8a3f977898" +"v2020.5", "SPIRV-Tools v2020.5 aebdae4b9c5b3384041b88803e83fb244f5c8067" diff --git a/3rdparty/spirv-tools/include/generated/core.insts-unified1.inc b/3rdparty/spirv-tools/include/generated/core.insts-unified1.inc index c335acbd6..3b1ad6594 100644 --- a/3rdparty/spirv-tools/include/generated/core.insts-unified1.inc +++ b/3rdparty/spirv-tools/include/generated/core.insts-unified1.inc @@ -2,6 +2,7 @@ static const SpvCapability pygen_variable_caps_Addresses[] = {SpvCapabilityAddre static const SpvCapability pygen_variable_caps_AddressesPhysicalStorageBufferAddresses[] = {SpvCapabilityAddresses, SpvCapabilityPhysicalStorageBufferAddresses}; static const SpvCapability pygen_variable_caps_AddressesVariablePointersVariablePointersStorageBuffer[] = {SpvCapabilityAddresses, SpvCapabilityVariablePointers, SpvCapabilityVariablePointersStorageBuffer}; static const SpvCapability pygen_variable_caps_AddressesVariablePointersVariablePointersStorageBufferPhysicalStorageBufferAddresses[] = {SpvCapabilityAddresses, SpvCapabilityVariablePointers, SpvCapabilityVariablePointersStorageBuffer, SpvCapabilityPhysicalStorageBufferAddresses}; +static const SpvCapability pygen_variable_caps_AtomicFloat32AddEXTAtomicFloat64AddEXT[] = {SpvCapabilityAtomicFloat32AddEXT, SpvCapabilityAtomicFloat64AddEXT}; static const SpvCapability pygen_variable_caps_BlockingPipesINTEL[] = {SpvCapabilityBlockingPipesINTEL}; static const SpvCapability pygen_variable_caps_CooperativeMatrixNV[] = {SpvCapabilityCooperativeMatrixNV}; static const SpvCapability pygen_variable_caps_DemoteToHelperInvocationEXT[] = {SpvCapabilityDemoteToHelperInvocationEXT}; @@ -55,6 +56,7 @@ static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_ballot[] = { static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_fragment_mask[] = {spvtools::Extension::kSPV_AMD_shader_fragment_mask}; static const spvtools::Extension pygen_variable_exts_SPV_EXT_demote_to_helper_invocation[] = {spvtools::Extension::kSPV_EXT_demote_to_helper_invocation}; static const spvtools::Extension pygen_variable_exts_SPV_EXT_fragment_shader_interlock[] = {spvtools::Extension::kSPV_EXT_fragment_shader_interlock}; +static const spvtools::Extension pygen_variable_exts_SPV_EXT_shader_atomic_float_add[] = {spvtools::Extension::kSPV_EXT_shader_atomic_float_add}; static const spvtools::Extension pygen_variable_exts_SPV_GOOGLE_decorate_stringSPV_GOOGLE_hlsl_functionality1[] = {spvtools::Extension::kSPV_GOOGLE_decorate_string, spvtools::Extension::kSPV_GOOGLE_hlsl_functionality1}; static const spvtools::Extension pygen_variable_exts_SPV_GOOGLE_hlsl_functionality1[] = {spvtools::Extension::kSPV_GOOGLE_hlsl_functionality1}; static const spvtools::Extension pygen_variable_exts_SPV_INTEL_blocking_pipes[] = {spvtools::Extension::kSPV_INTEL_blocking_pipes}; @@ -65,6 +67,7 @@ static const spvtools::Extension pygen_variable_exts_SPV_KHR_ray_query[] = {spvt static const spvtools::Extension pygen_variable_exts_SPV_KHR_shader_ballot[] = {spvtools::Extension::kSPV_KHR_shader_ballot}; static const spvtools::Extension pygen_variable_exts_SPV_KHR_shader_clock[] = {spvtools::Extension::kSPV_KHR_shader_clock}; static const spvtools::Extension pygen_variable_exts_SPV_KHR_subgroup_vote[] = {spvtools::Extension::kSPV_KHR_subgroup_vote}; +static const spvtools::Extension pygen_variable_exts_SPV_KHR_terminate_invocation[] = {spvtools::Extension::kSPV_KHR_terminate_invocation}; static const spvtools::Extension pygen_variable_exts_SPV_NV_cooperative_matrix[] = {spvtools::Extension::kSPV_NV_cooperative_matrix}; static const spvtools::Extension pygen_variable_exts_SPV_NV_mesh_shader[] = {spvtools::Extension::kSPV_NV_mesh_shader}; static const spvtools::Extension pygen_variable_exts_SPV_NV_ray_tracingSPV_KHR_ray_tracing[] = {spvtools::Extension::kSPV_NV_ray_tracing, spvtools::Extension::kSPV_KHR_ray_tracing}; @@ -417,6 +420,7 @@ static const spv_opcode_desc_t kOpcodeTableEntries[] = { {"PtrEqual", SpvOpPtrEqual, 0, nullptr, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu}, {"PtrNotEqual", SpvOpPtrNotEqual, 0, nullptr, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu}, {"PtrDiff", SpvOpPtrDiff, 3, pygen_variable_caps_AddressesVariablePointersVariablePointersStorageBuffer, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu}, + {"TerminateInvocation", SpvOpTerminateInvocation, 1, pygen_variable_caps_Shader, 0, {}, 0, 0, 1, pygen_variable_exts_SPV_KHR_terminate_invocation, 0xffffffffu, 0xffffffffu}, {"SubgroupBallotKHR", SpvOpSubgroupBallotKHR, 1, pygen_variable_caps_SubgroupBallotKHR, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_shader_ballot, 0xffffffffu, 0xffffffffu}, {"SubgroupFirstInvocationKHR", SpvOpSubgroupFirstInvocationKHR, 1, pygen_variable_caps_SubgroupBallotKHR, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_shader_ballot, 0xffffffffu, 0xffffffffu}, {"SubgroupAllKHR", SpvOpSubgroupAllKHR, 1, pygen_variable_caps_SubgroupVoteKHR, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_subgroup_vote, 0xffffffffu, 0xffffffffu}, @@ -633,5 +637,6 @@ static const spv_opcode_desc_t kOpcodeTableEntries[] = { {"RayQueryGetWorldRayDirectionKHR", SpvOpRayQueryGetWorldRayDirectionKHR, 1, pygen_variable_caps_RayQueryProvisionalKHR, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_ray_query, 0xffffffffu, 0xffffffffu}, {"RayQueryGetWorldRayOriginKHR", SpvOpRayQueryGetWorldRayOriginKHR, 1, pygen_variable_caps_RayQueryProvisionalKHR, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_ray_query, 0xffffffffu, 0xffffffffu}, {"RayQueryGetIntersectionObjectToWorldKHR", SpvOpRayQueryGetIntersectionObjectToWorldKHR, 1, pygen_variable_caps_RayQueryProvisionalKHR, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_ray_query, 0xffffffffu, 0xffffffffu}, - {"RayQueryGetIntersectionWorldToObjectKHR", SpvOpRayQueryGetIntersectionWorldToObjectKHR, 1, pygen_variable_caps_RayQueryProvisionalKHR, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_ray_query, 0xffffffffu, 0xffffffffu} + {"RayQueryGetIntersectionWorldToObjectKHR", SpvOpRayQueryGetIntersectionWorldToObjectKHR, 1, pygen_variable_caps_RayQueryProvisionalKHR, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_ray_query, 0xffffffffu, 0xffffffffu}, + {"AtomicFAddEXT", SpvOpAtomicFAddEXT, 2, pygen_variable_caps_AtomicFloat32AddEXTAtomicFloat64AddEXT, 6, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_SCOPE_ID, SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_EXT_shader_atomic_float_add, 0xffffffffu, 0xffffffffu} }; \ No newline at end of file diff --git a/3rdparty/spirv-tools/include/generated/enum_string_mapping.inc b/3rdparty/spirv-tools/include/generated/enum_string_mapping.inc index 3a6038034..3e672245e 100644 --- a/3rdparty/spirv-tools/include/generated/enum_string_mapping.inc +++ b/3rdparty/spirv-tools/include/generated/enum_string_mapping.inc @@ -32,6 +32,8 @@ const char* ExtensionToString(Extension extension) { return "SPV_EXT_fragment_shader_interlock"; case Extension::kSPV_EXT_physical_storage_buffer: return "SPV_EXT_physical_storage_buffer"; + case Extension::kSPV_EXT_shader_atomic_float_add: + return "SPV_EXT_shader_atomic_float_add"; case Extension::kSPV_EXT_shader_stencil_export: return "SPV_EXT_shader_stencil_export"; case Extension::kSPV_EXT_shader_viewport_index_layer: @@ -98,6 +100,8 @@ const char* ExtensionToString(Extension extension) { return "SPV_KHR_storage_buffer_storage_class"; case Extension::kSPV_KHR_subgroup_vote: return "SPV_KHR_subgroup_vote"; + case Extension::kSPV_KHR_terminate_invocation: + return "SPV_KHR_terminate_invocation"; case Extension::kSPV_KHR_variable_pointers: return "SPV_KHR_variable_pointers"; case Extension::kSPV_KHR_vulkan_memory_model: @@ -139,8 +143,8 @@ const char* ExtensionToString(Extension extension) { bool GetExtensionFromString(const char* str, Extension* extension) { - static const char* known_ext_strs[] = { "SPV_AMD_gcn_shader", "SPV_AMD_gpu_shader_half_float", "SPV_AMD_gpu_shader_half_float_fetch", "SPV_AMD_gpu_shader_int16", "SPV_AMD_shader_ballot", "SPV_AMD_shader_explicit_vertex_parameter", "SPV_AMD_shader_fragment_mask", "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_trinary_minmax", "SPV_AMD_texture_gather_bias_lod", "SPV_EXT_demote_to_helper_invocation", "SPV_EXT_descriptor_indexing", "SPV_EXT_fragment_fully_covered", "SPV_EXT_fragment_invocation_density", "SPV_EXT_fragment_shader_interlock", "SPV_EXT_physical_storage_buffer", "SPV_EXT_shader_stencil_export", "SPV_EXT_shader_viewport_index_layer", "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", "SPV_GOOGLE_user_type", "SPV_INTEL_blocking_pipes", "SPV_INTEL_device_side_avc_motion_estimation", "SPV_INTEL_fpga_loop_controls", "SPV_INTEL_fpga_memory_attributes", "SPV_INTEL_fpga_reg", "SPV_INTEL_function_pointers", "SPV_INTEL_kernel_attributes", "SPV_INTEL_media_block_io", "SPV_INTEL_shader_integer_functions2", "SPV_INTEL_subgroups", "SPV_INTEL_unstructured_loop_controls", "SPV_KHR_16bit_storage", "SPV_KHR_8bit_storage", "SPV_KHR_device_group", "SPV_KHR_float_controls", "SPV_KHR_multiview", "SPV_KHR_no_integer_wrap_decoration", "SPV_KHR_non_semantic_info", "SPV_KHR_physical_storage_buffer", "SPV_KHR_post_depth_coverage", "SPV_KHR_ray_query", "SPV_KHR_ray_tracing", "SPV_KHR_shader_atomic_counter_ops", "SPV_KHR_shader_ballot", "SPV_KHR_shader_clock", "SPV_KHR_shader_draw_parameters", "SPV_KHR_storage_buffer_storage_class", "SPV_KHR_subgroup_vote", "SPV_KHR_variable_pointers", "SPV_KHR_vulkan_memory_model", "SPV_NVX_multiview_per_view_attributes", "SPV_NV_compute_shader_derivatives", "SPV_NV_cooperative_matrix", "SPV_NV_fragment_shader_barycentric", "SPV_NV_geometry_shader_passthrough", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing", "SPV_NV_sample_mask_override_coverage", "SPV_NV_shader_image_footprint", "SPV_NV_shader_sm_builtins", "SPV_NV_shader_subgroup_partitioned", "SPV_NV_shading_rate", "SPV_NV_stereo_view_rendering", "SPV_NV_viewport_array2", "SPV_VALIDATOR_ignore_type_decl_unique" }; - static const Extension known_ext_ids[] = { Extension::kSPV_AMD_gcn_shader, Extension::kSPV_AMD_gpu_shader_half_float, Extension::kSPV_AMD_gpu_shader_half_float_fetch, Extension::kSPV_AMD_gpu_shader_int16, Extension::kSPV_AMD_shader_ballot, Extension::kSPV_AMD_shader_explicit_vertex_parameter, Extension::kSPV_AMD_shader_fragment_mask, Extension::kSPV_AMD_shader_image_load_store_lod, Extension::kSPV_AMD_shader_trinary_minmax, Extension::kSPV_AMD_texture_gather_bias_lod, Extension::kSPV_EXT_demote_to_helper_invocation, Extension::kSPV_EXT_descriptor_indexing, Extension::kSPV_EXT_fragment_fully_covered, Extension::kSPV_EXT_fragment_invocation_density, Extension::kSPV_EXT_fragment_shader_interlock, Extension::kSPV_EXT_physical_storage_buffer, Extension::kSPV_EXT_shader_stencil_export, Extension::kSPV_EXT_shader_viewport_index_layer, Extension::kSPV_GOOGLE_decorate_string, Extension::kSPV_GOOGLE_hlsl_functionality1, Extension::kSPV_GOOGLE_user_type, Extension::kSPV_INTEL_blocking_pipes, Extension::kSPV_INTEL_device_side_avc_motion_estimation, Extension::kSPV_INTEL_fpga_loop_controls, Extension::kSPV_INTEL_fpga_memory_attributes, Extension::kSPV_INTEL_fpga_reg, Extension::kSPV_INTEL_function_pointers, Extension::kSPV_INTEL_kernel_attributes, Extension::kSPV_INTEL_media_block_io, Extension::kSPV_INTEL_shader_integer_functions2, Extension::kSPV_INTEL_subgroups, Extension::kSPV_INTEL_unstructured_loop_controls, Extension::kSPV_KHR_16bit_storage, Extension::kSPV_KHR_8bit_storage, Extension::kSPV_KHR_device_group, Extension::kSPV_KHR_float_controls, Extension::kSPV_KHR_multiview, Extension::kSPV_KHR_no_integer_wrap_decoration, Extension::kSPV_KHR_non_semantic_info, Extension::kSPV_KHR_physical_storage_buffer, Extension::kSPV_KHR_post_depth_coverage, Extension::kSPV_KHR_ray_query, Extension::kSPV_KHR_ray_tracing, Extension::kSPV_KHR_shader_atomic_counter_ops, Extension::kSPV_KHR_shader_ballot, Extension::kSPV_KHR_shader_clock, Extension::kSPV_KHR_shader_draw_parameters, Extension::kSPV_KHR_storage_buffer_storage_class, Extension::kSPV_KHR_subgroup_vote, Extension::kSPV_KHR_variable_pointers, Extension::kSPV_KHR_vulkan_memory_model, Extension::kSPV_NVX_multiview_per_view_attributes, Extension::kSPV_NV_compute_shader_derivatives, Extension::kSPV_NV_cooperative_matrix, Extension::kSPV_NV_fragment_shader_barycentric, Extension::kSPV_NV_geometry_shader_passthrough, Extension::kSPV_NV_mesh_shader, Extension::kSPV_NV_ray_tracing, Extension::kSPV_NV_sample_mask_override_coverage, Extension::kSPV_NV_shader_image_footprint, Extension::kSPV_NV_shader_sm_builtins, Extension::kSPV_NV_shader_subgroup_partitioned, Extension::kSPV_NV_shading_rate, Extension::kSPV_NV_stereo_view_rendering, Extension::kSPV_NV_viewport_array2, Extension::kSPV_VALIDATOR_ignore_type_decl_unique }; + static const char* known_ext_strs[] = { "SPV_AMD_gcn_shader", "SPV_AMD_gpu_shader_half_float", "SPV_AMD_gpu_shader_half_float_fetch", "SPV_AMD_gpu_shader_int16", "SPV_AMD_shader_ballot", "SPV_AMD_shader_explicit_vertex_parameter", "SPV_AMD_shader_fragment_mask", "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_trinary_minmax", "SPV_AMD_texture_gather_bias_lod", "SPV_EXT_demote_to_helper_invocation", "SPV_EXT_descriptor_indexing", "SPV_EXT_fragment_fully_covered", "SPV_EXT_fragment_invocation_density", "SPV_EXT_fragment_shader_interlock", "SPV_EXT_physical_storage_buffer", "SPV_EXT_shader_atomic_float_add", "SPV_EXT_shader_stencil_export", "SPV_EXT_shader_viewport_index_layer", "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", "SPV_GOOGLE_user_type", "SPV_INTEL_blocking_pipes", "SPV_INTEL_device_side_avc_motion_estimation", "SPV_INTEL_fpga_loop_controls", "SPV_INTEL_fpga_memory_attributes", "SPV_INTEL_fpga_reg", "SPV_INTEL_function_pointers", "SPV_INTEL_kernel_attributes", "SPV_INTEL_media_block_io", "SPV_INTEL_shader_integer_functions2", "SPV_INTEL_subgroups", "SPV_INTEL_unstructured_loop_controls", "SPV_KHR_16bit_storage", "SPV_KHR_8bit_storage", "SPV_KHR_device_group", "SPV_KHR_float_controls", "SPV_KHR_multiview", "SPV_KHR_no_integer_wrap_decoration", "SPV_KHR_non_semantic_info", "SPV_KHR_physical_storage_buffer", "SPV_KHR_post_depth_coverage", "SPV_KHR_ray_query", "SPV_KHR_ray_tracing", "SPV_KHR_shader_atomic_counter_ops", "SPV_KHR_shader_ballot", "SPV_KHR_shader_clock", "SPV_KHR_shader_draw_parameters", "SPV_KHR_storage_buffer_storage_class", "SPV_KHR_subgroup_vote", "SPV_KHR_terminate_invocation", "SPV_KHR_variable_pointers", "SPV_KHR_vulkan_memory_model", "SPV_NVX_multiview_per_view_attributes", "SPV_NV_compute_shader_derivatives", "SPV_NV_cooperative_matrix", "SPV_NV_fragment_shader_barycentric", "SPV_NV_geometry_shader_passthrough", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing", "SPV_NV_sample_mask_override_coverage", "SPV_NV_shader_image_footprint", "SPV_NV_shader_sm_builtins", "SPV_NV_shader_subgroup_partitioned", "SPV_NV_shading_rate", "SPV_NV_stereo_view_rendering", "SPV_NV_viewport_array2", "SPV_VALIDATOR_ignore_type_decl_unique" }; + static const Extension known_ext_ids[] = { Extension::kSPV_AMD_gcn_shader, Extension::kSPV_AMD_gpu_shader_half_float, Extension::kSPV_AMD_gpu_shader_half_float_fetch, Extension::kSPV_AMD_gpu_shader_int16, Extension::kSPV_AMD_shader_ballot, Extension::kSPV_AMD_shader_explicit_vertex_parameter, Extension::kSPV_AMD_shader_fragment_mask, Extension::kSPV_AMD_shader_image_load_store_lod, Extension::kSPV_AMD_shader_trinary_minmax, Extension::kSPV_AMD_texture_gather_bias_lod, Extension::kSPV_EXT_demote_to_helper_invocation, Extension::kSPV_EXT_descriptor_indexing, Extension::kSPV_EXT_fragment_fully_covered, Extension::kSPV_EXT_fragment_invocation_density, Extension::kSPV_EXT_fragment_shader_interlock, Extension::kSPV_EXT_physical_storage_buffer, Extension::kSPV_EXT_shader_atomic_float_add, Extension::kSPV_EXT_shader_stencil_export, Extension::kSPV_EXT_shader_viewport_index_layer, Extension::kSPV_GOOGLE_decorate_string, Extension::kSPV_GOOGLE_hlsl_functionality1, Extension::kSPV_GOOGLE_user_type, Extension::kSPV_INTEL_blocking_pipes, Extension::kSPV_INTEL_device_side_avc_motion_estimation, Extension::kSPV_INTEL_fpga_loop_controls, Extension::kSPV_INTEL_fpga_memory_attributes, Extension::kSPV_INTEL_fpga_reg, Extension::kSPV_INTEL_function_pointers, Extension::kSPV_INTEL_kernel_attributes, Extension::kSPV_INTEL_media_block_io, Extension::kSPV_INTEL_shader_integer_functions2, Extension::kSPV_INTEL_subgroups, Extension::kSPV_INTEL_unstructured_loop_controls, Extension::kSPV_KHR_16bit_storage, Extension::kSPV_KHR_8bit_storage, Extension::kSPV_KHR_device_group, Extension::kSPV_KHR_float_controls, Extension::kSPV_KHR_multiview, Extension::kSPV_KHR_no_integer_wrap_decoration, Extension::kSPV_KHR_non_semantic_info, Extension::kSPV_KHR_physical_storage_buffer, Extension::kSPV_KHR_post_depth_coverage, Extension::kSPV_KHR_ray_query, Extension::kSPV_KHR_ray_tracing, Extension::kSPV_KHR_shader_atomic_counter_ops, Extension::kSPV_KHR_shader_ballot, Extension::kSPV_KHR_shader_clock, Extension::kSPV_KHR_shader_draw_parameters, Extension::kSPV_KHR_storage_buffer_storage_class, Extension::kSPV_KHR_subgroup_vote, Extension::kSPV_KHR_terminate_invocation, Extension::kSPV_KHR_variable_pointers, Extension::kSPV_KHR_vulkan_memory_model, Extension::kSPV_NVX_multiview_per_view_attributes, Extension::kSPV_NV_compute_shader_derivatives, Extension::kSPV_NV_cooperative_matrix, Extension::kSPV_NV_fragment_shader_barycentric, Extension::kSPV_NV_geometry_shader_passthrough, Extension::kSPV_NV_mesh_shader, Extension::kSPV_NV_ray_tracing, Extension::kSPV_NV_sample_mask_override_coverage, Extension::kSPV_NV_shader_image_footprint, Extension::kSPV_NV_shader_sm_builtins, Extension::kSPV_NV_shader_subgroup_partitioned, Extension::kSPV_NV_shading_rate, Extension::kSPV_NV_stereo_view_rendering, Extension::kSPV_NV_viewport_array2, Extension::kSPV_VALIDATOR_ignore_type_decl_unique }; const auto b = std::begin(known_ext_strs); const auto e = std::end(known_ext_strs); const auto found = std::equal_range( @@ -460,6 +464,10 @@ const char* CapabilityToString(SpvCapability capability) { return "BlockingPipesINTEL"; case SpvCapabilityFPGARegINTEL: return "FPGARegINTEL"; + case SpvCapabilityAtomicFloat32AddEXT: + return "AtomicFloat32AddEXT"; + case SpvCapabilityAtomicFloat64AddEXT: + return "AtomicFloat64AddEXT"; case SpvCapabilityMax: assert(0 && "Attempting to convert SpvCapabilityMax to string"); return ""; diff --git a/3rdparty/spirv-tools/include/generated/extension_enum.inc b/3rdparty/spirv-tools/include/generated/extension_enum.inc index 9cde11c8a..a5c14eedb 100644 --- a/3rdparty/spirv-tools/include/generated/extension_enum.inc +++ b/3rdparty/spirv-tools/include/generated/extension_enum.inc @@ -14,6 +14,7 @@ kSPV_EXT_fragment_fully_covered, kSPV_EXT_fragment_invocation_density, kSPV_EXT_fragment_shader_interlock, kSPV_EXT_physical_storage_buffer, +kSPV_EXT_shader_atomic_float_add, kSPV_EXT_shader_stencil_export, kSPV_EXT_shader_viewport_index_layer, kSPV_GOOGLE_decorate_string, @@ -47,6 +48,7 @@ kSPV_KHR_shader_clock, kSPV_KHR_shader_draw_parameters, kSPV_KHR_storage_buffer_storage_class, kSPV_KHR_subgroup_vote, +kSPV_KHR_terminate_invocation, kSPV_KHR_variable_pointers, kSPV_KHR_vulkan_memory_model, kSPV_NVX_multiview_per_view_attributes, diff --git a/3rdparty/spirv-tools/include/generated/operand.kinds-unified1.inc b/3rdparty/spirv-tools/include/generated/operand.kinds-unified1.inc index c7b2f699f..f07003d8d 100644 --- a/3rdparty/spirv-tools/include/generated/operand.kinds-unified1.inc +++ b/3rdparty/spirv-tools/include/generated/operand.kinds-unified1.inc @@ -108,6 +108,7 @@ static const spvtools::Extension pygen_variable_exts_SPV_EXT_fragment_fully_cove static const spvtools::Extension pygen_variable_exts_SPV_EXT_fragment_invocation_densitySPV_NV_shading_rate[] = {spvtools::Extension::kSPV_EXT_fragment_invocation_density, spvtools::Extension::kSPV_NV_shading_rate}; static const spvtools::Extension pygen_variable_exts_SPV_EXT_fragment_shader_interlock[] = {spvtools::Extension::kSPV_EXT_fragment_shader_interlock}; static const spvtools::Extension pygen_variable_exts_SPV_EXT_physical_storage_bufferSPV_KHR_physical_storage_buffer[] = {spvtools::Extension::kSPV_EXT_physical_storage_buffer, spvtools::Extension::kSPV_KHR_physical_storage_buffer}; +static const spvtools::Extension pygen_variable_exts_SPV_EXT_shader_atomic_float_add[] = {spvtools::Extension::kSPV_EXT_shader_atomic_float_add}; static const spvtools::Extension pygen_variable_exts_SPV_EXT_shader_stencil_export[] = {spvtools::Extension::kSPV_EXT_shader_stencil_export}; static const spvtools::Extension pygen_variable_exts_SPV_EXT_shader_viewport_index_layerSPV_NV_viewport_array2[] = {spvtools::Extension::kSPV_EXT_shader_viewport_index_layer, spvtools::Extension::kSPV_NV_viewport_array2}; static const spvtools::Extension pygen_variable_exts_SPV_GOOGLE_hlsl_functionality1[] = {spvtools::Extension::kSPV_GOOGLE_hlsl_functionality1}; @@ -962,7 +963,9 @@ static const spv_operand_desc_t pygen_variable_CapabilityEntries[] = { {"KernelAttributesINTEL", 5892, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_kernel_attributes, {}, 0xffffffffu, 0xffffffffu}, {"FPGAKernelAttributesINTEL", 5897, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_kernel_attributes, {}, 0xffffffffu, 0xffffffffu}, {"BlockingPipesINTEL", 5945, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_blocking_pipes, {}, 0xffffffffu, 0xffffffffu}, - {"FPGARegINTEL", 5948, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_fpga_reg, {}, 0xffffffffu, 0xffffffffu} + {"FPGARegINTEL", 5948, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_fpga_reg, {}, 0xffffffffu, 0xffffffffu}, + {"AtomicFloat32AddEXT", 6033, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_EXT_shader_atomic_float_add, {}, 0xffffffffu, 0xffffffffu}, + {"AtomicFloat64AddEXT", 6034, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_EXT_shader_atomic_float_add, {}, 0xffffffffu, 0xffffffffu} }; static const spv_operand_desc_t pygen_variable_RayQueryIntersectionEntries[] = { diff --git a/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp b/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp index d393495ba..741f9476d 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp +++ b/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp @@ -876,8 +876,10 @@ Optimizer::PassToken CreateGraphicsRobustAccessPass(); // for the first index. Optimizer::PassToken CreateDescriptorScalarReplacementPass(); -// Create a pass to replace all OpKill instruction with a function call to a -// function that has a single OpKill. This allows more code to be inlined. +// Create a pass to replace each OpKill instruction with a function call to a +// function that has a single OpKill. Also replace each OpTerminateInvocation +// instruction with a function call to a function that has a single +// OpTerminateInvocation. This allows more code to be inlined. Optimizer::PassToken CreateWrapOpKillPass(); // Replaces the extensions VK_AMD_shader_ballot,VK_AMD_gcn_shader, and diff --git a/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt b/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt index a5f67ca31..f1309f3e5 100644 --- a/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt @@ -52,6 +52,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_add_local_variables.h fuzzer_pass_add_no_contraction_decorations.h fuzzer_pass_add_parameters.h + fuzzer_pass_add_relaxed_decorations.h fuzzer_pass_add_stores.h fuzzer_pass_add_vector_shuffle_instructions.h fuzzer_pass_adjust_branch_weights.h @@ -72,8 +73,11 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_permute_function_parameters.h fuzzer_pass_permute_phi_operands.h fuzzer_pass_push_ids_through_variables.h + fuzzer_pass_replace_copy_memories_with_loads_stores.h + fuzzer_pass_replace_copy_objects_with_stores_loads.h fuzzer_pass_replace_linear_algebra_instructions.h fuzzer_pass_replace_parameter_with_global.h + fuzzer_pass_replace_params_with_struct.h fuzzer_pass_split_blocks.h fuzzer_pass_swap_commutable_operands.h fuzzer_pass_swap_conditional_branch_operands.h @@ -104,6 +108,7 @@ if(SPIRV_BUILD_FUZZER) transformation_add_local_variable.h transformation_add_no_contraction_decoration.h transformation_add_parameter.h + transformation_add_relaxed_decoration.h transformation_add_spec_constant_op.h transformation_add_synonym.h transformation_add_type_array.h @@ -133,9 +138,12 @@ if(SPIRV_BUILD_FUZZER) transformation_record_synonymous_constants.h transformation_replace_boolean_constant_with_constant_binary.h transformation_replace_constant_with_uniform.h + transformation_replace_copy_memory_with_load_store.h + transformation_replace_copy_object_with_store_load.h transformation_replace_id_with_synonym.h transformation_replace_linear_algebra_instruction.h transformation_replace_parameter_with_global.h + transformation_replace_params_with_struct.h transformation_set_function_control.h transformation_set_loop_control.h transformation_set_memory_operands_mask.h @@ -171,6 +179,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_add_local_variables.cpp fuzzer_pass_add_no_contraction_decorations.cpp fuzzer_pass_add_parameters.cpp + fuzzer_pass_add_relaxed_decorations.cpp fuzzer_pass_add_stores.cpp fuzzer_pass_add_vector_shuffle_instructions.cpp fuzzer_pass_adjust_branch_weights.cpp @@ -191,8 +200,11 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_permute_function_parameters.cpp fuzzer_pass_permute_phi_operands.cpp fuzzer_pass_push_ids_through_variables.cpp + fuzzer_pass_replace_copy_memories_with_loads_stores.cpp + fuzzer_pass_replace_copy_objects_with_stores_loads.cpp fuzzer_pass_replace_linear_algebra_instructions.cpp fuzzer_pass_replace_parameter_with_global.cpp + fuzzer_pass_replace_params_with_struct.cpp fuzzer_pass_split_blocks.cpp fuzzer_pass_swap_commutable_operands.cpp fuzzer_pass_swap_conditional_branch_operands.cpp @@ -222,6 +234,7 @@ if(SPIRV_BUILD_FUZZER) transformation_add_local_variable.cpp transformation_add_no_contraction_decoration.cpp transformation_add_parameter.cpp + transformation_add_relaxed_decoration.cpp transformation_add_spec_constant_op.cpp transformation_add_synonym.cpp transformation_add_type_array.cpp @@ -251,9 +264,12 @@ if(SPIRV_BUILD_FUZZER) transformation_record_synonymous_constants.cpp transformation_replace_boolean_constant_with_constant_binary.cpp transformation_replace_constant_with_uniform.cpp + transformation_replace_copy_memory_with_load_store.cpp + transformation_replace_copy_object_with_store_load.cpp transformation_replace_id_with_synonym.cpp transformation_replace_linear_algebra_instruction.cpp transformation_replace_parameter_with_global.cpp + transformation_replace_params_with_struct.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/fact_manager.cpp b/3rdparty/spirv-tools/source/fuzz/fact_manager.cpp index 320049a86..6dff669f2 100644 --- a/3rdparty/spirv-tools/source/fuzz/fact_manager.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fact_manager.cpp @@ -1343,32 +1343,49 @@ bool FactManager::LivesafeFunctionFacts::FunctionIsLivesafe( //============================== //============================== -// Irrelevant pointee value facts +// Irrelevant value facts // The purpose of this class is to group the fields and data used to represent -// facts about pointers whose pointee values are irrelevant. -class FactManager::IrrelevantPointeeValueFacts { +// facts about various irrelevant values in the module. +class FactManager::IrrelevantValueFacts { public: // See method in FactManager which delegates to this method. void AddFact(const protobufs::FactPointeeValueIsIrrelevant& fact); + // See method in FactManager which delegates to this method. + void AddFact(const protobufs::FactIdIsIrrelevant& fact); + // See method in FactManager which delegates to this method. bool PointeeValueIsIrrelevant(uint32_t pointer_id) const; + // See method in FactManager which delegates to this method. + bool IdIsIrrelevant(uint32_t pointer_id) const; + private: - std::set pointers_to_irrelevant_pointees_ids_; + std::unordered_set pointers_to_irrelevant_pointees_ids_; + std::unordered_set irrelevant_ids_; }; -void FactManager::IrrelevantPointeeValueFacts::AddFact( +void FactManager::IrrelevantValueFacts::AddFact( const protobufs::FactPointeeValueIsIrrelevant& fact) { pointers_to_irrelevant_pointees_ids_.insert(fact.pointer_id()); } -bool FactManager::IrrelevantPointeeValueFacts::PointeeValueIsIrrelevant( +void FactManager::IrrelevantValueFacts::AddFact( + const protobufs::FactIdIsIrrelevant& fact) { + irrelevant_ids_.insert(fact.result_id()); +} + +bool FactManager::IrrelevantValueFacts::PointeeValueIsIrrelevant( uint32_t pointer_id) const { return pointers_to_irrelevant_pointees_ids_.count(pointer_id) != 0; } +bool FactManager::IrrelevantValueFacts::IdIsIrrelevant( + uint32_t pointer_id) const { + return irrelevant_ids_.count(pointer_id) != 0; +} + // End of arbitrarily-valued variable facts //============================== @@ -1378,8 +1395,7 @@ FactManager::FactManager() MakeUnique()), dead_block_facts_(MakeUnique()), livesafe_function_facts_(MakeUnique()), - irrelevant_pointee_value_facts_( - MakeUnique()) {} + irrelevant_value_facts_(MakeUnique()) {} FactManager::~FactManager() = default; @@ -1420,6 +1436,8 @@ bool FactManager::AddFact(const fuzz::protobufs::Fact& fact, void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1, const protobufs::DataDescriptor& data2, opt::IRContext* context) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550): + // assert that neither |data1| nor |data2| are irrelevant. protobufs::FactDataSynonym fact; *fact.mutable_data1() = data1; *fact.mutable_data2() = data2; @@ -1500,18 +1518,32 @@ void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) { } bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const { - return irrelevant_pointee_value_facts_->PointeeValueIsIrrelevant(pointer_id); + return irrelevant_value_facts_->PointeeValueIsIrrelevant(pointer_id); +} + +bool FactManager::IdIsIrrelevant(uint32_t result_id) const { + return irrelevant_value_facts_->IdIsIrrelevant(result_id); } void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) { protobufs::FactPointeeValueIsIrrelevant fact; fact.set_pointer_id(pointer_id); - irrelevant_pointee_value_facts_->AddFact(fact); + irrelevant_value_facts_->AddFact(fact); +} + +void FactManager::AddFactIdIsIrrelevant(uint32_t result_id) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550): + // assert that |result_id| is not a part of any DataSynonym fact. + protobufs::FactIdIsIrrelevant fact; + fact.set_result_id(result_id); + irrelevant_value_facts_->AddFact(fact); } void FactManager::AddFactIdEquation(uint32_t lhs_id, SpvOp opcode, const std::vector& rhs_id, opt::IRContext* context) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550): + // assert that elements of |rhs_id| and |lhs_id| are not irrelevant. protobufs::FactIdEquation fact; fact.set_lhs_id(lhs_id); fact.set_opcode(opcode); diff --git a/3rdparty/spirv-tools/source/fuzz/fact_manager.h b/3rdparty/spirv-tools/source/fuzz/fact_manager.h index f520e424c..f83e2ff60 100644 --- a/3rdparty/spirv-tools/source/fuzz/fact_manager.h +++ b/3rdparty/spirv-tools/source/fuzz/fact_manager.h @@ -68,6 +68,10 @@ class FactManager { // is irrelevant: it does not affect the observable behaviour of the module. void AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id); + // Records a fact that the |result_id| is irrelevant (i.e. it doesn't affect + // the semantics of the module) + void AddFactIdIsIrrelevant(uint32_t result_id); + // Records the fact that |lhs_id| is defined by the equation: // // |lhs_id| = |opcode| |rhs_id[0]| ... |rhs_id[N-1]| @@ -181,13 +185,16 @@ class FactManager { //============================== //============================== - // Querying facts about pointers with irrelevant pointee values + // Querying facts about irrelevant values // Returns true if and ony if the value of the pointee associated with // |pointer_id| is irrelevant. bool PointeeValueIsIrrelevant(uint32_t pointer_id) const; - // End of irrelevant pointee value facts + // Returns true iff there exists a fact that the |result_id| is irrelevant. + bool IdIsIrrelevant(uint32_t result_id) const; + + // End of irrelevant value facts //============================== private: @@ -213,10 +220,10 @@ class FactManager { std::unique_ptr livesafe_function_facts_; // Unique pointer to internal data. - class IrrelevantPointeeValueFacts; // Opaque class for management of - // facts about pointers whose pointee values do not matter. - std::unique_ptr - irrelevant_pointee_value_facts_; // Unique pointer to internal data. + class IrrelevantValueFacts; // Opaque class for management of + // facts about various irrelevant values in the module. + std::unique_ptr + irrelevant_value_facts_; // Unique pointer to internal data. }; } // namespace fuzz diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp index edd6e1794..f54959000 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp @@ -57,6 +57,7 @@ #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_replace_params_with_struct.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" @@ -288,6 +289,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 f2b81704e..205e1903a 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp @@ -42,6 +42,7 @@ const std::pair kChanceOfAddingMatrixType = {20, 70}; const std::pair kChanceOfAddingNoContractionDecoration = { 5, 70}; const std::pair kChanceOfAddingParameters = {5, 70}; +const std::pair kChanceOfAddingRelaxedDecoration = {20, 90}; const std::pair kChanceOfAddingStore = {5, 50}; const std::pair kChanceOfAddingSynonyms = {20, 50}; const std::pair kChanceOfAddingVectorType = {20, 70}; @@ -76,11 +77,17 @@ const std::pair kChanceOfOutliningFunction = {10, 90}; const std::pair kChanceOfPermutingParameters = {30, 90}; const std::pair kChanceOfPermutingPhiOperands = {30, 90}; const std::pair kChanceOfPushingIdThroughVariable = {5, 50}; +const std::pair kChanceOfReplacingCopyMemoryWithLoadStore = + {20, 90}; +const std::pair kChanceOfReplacingCopyObjectWithStoreLoad = + {20, 90}; const std::pair kChanceOfReplacingIdWithSynonym = {10, 90}; const std::pair kChanceOfReplacingLinearAlgebraInstructions = {10, 90}; const std::pair kChanceOfReplacingParametersWithGlobals = { 30, 70}; +const std::pair kChanceOfReplacingParametersWithStruct = { + 20, 40}; const std::pair kChanceOfSplittingBlock = {40, 95}; const std::pair kChanceOfSwappingConditionalBranchOperands = {10, 70}; @@ -98,6 +105,7 @@ const uint32_t kDefaultMaxNewArraySizeLimit = 100; // think whether there is a better limit on the maximum number of parameters. const uint32_t kDefaultMaxNumberOfFunctionParameters = 128; const uint32_t kDefaultMaxNumberOfNewParameters = 15; +const uint32_t kGetDefaultMaxNumberOfParametersReplacedWithStruct = 5; // Default functions for controlling how deep to go during recursive // generation/transformation. Keep them in alphabetical order. @@ -123,6 +131,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, max_new_array_size_limit_(kDefaultMaxNewArraySizeLimit), max_number_of_function_parameters_(kDefaultMaxNumberOfFunctionParameters), max_number_of_new_parameters_(kDefaultMaxNumberOfNewParameters), + max_number_of_parameters_replaced_with_struct_( + kGetDefaultMaxNumberOfParametersReplacedWithStruct), go_deeper_in_constant_obfuscation_( kDefaultGoDeeperInConstantObfuscation) { chance_of_adding_access_chain_ = @@ -154,6 +164,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration); chance_of_adding_parameters = ChooseBetweenMinAndMax(kChanceOfAddingParameters); + chance_of_adding_relaxed_decoration_ = + ChooseBetweenMinAndMax(kChanceOfAddingRelaxedDecoration); chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore); chance_of_adding_vector_shuffle_ = ChooseBetweenMinAndMax(kChanceOfAddingVectorShuffle); @@ -202,12 +214,18 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfPermutingPhiOperands); chance_of_pushing_id_through_variable_ = ChooseBetweenMinAndMax(kChanceOfPushingIdThroughVariable); + chance_of_replacing_copy_memory_with_load_store_ = + ChooseBetweenMinAndMax(kChanceOfReplacingCopyMemoryWithLoadStore); + chance_of_replacing_copyobject_with_store_load_ = + ChooseBetweenMinAndMax(kChanceOfReplacingCopyObjectWithStoreLoad); chance_of_replacing_id_with_synonym_ = ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym); chance_of_replacing_linear_algebra_instructions_ = ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions); chance_of_replacing_parameters_with_globals_ = ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals); + chance_of_replacing_parameters_with_struct_ = + ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithStruct); 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 90539c88c..8fc6c15f2 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h @@ -143,6 +143,9 @@ class FuzzerContext { return chance_of_adding_no_contraction_decoration_; } uint32_t GetChanceOfAddingParameters() { return chance_of_adding_parameters; } + uint32_t GetChanceOfAddingRelaxedDecoration() { + return chance_of_adding_relaxed_decoration_; + } uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; } uint32_t GetChanceOfAddingSynonyms() { return chance_of_adding_synonyms_; } uint32_t GetChanceOfAddingVectorShuffle() { @@ -209,6 +212,12 @@ class FuzzerContext { uint32_t GetChanceOfPushingIdThroughVariable() { return chance_of_pushing_id_through_variable_; } + uint32_t GetChanceOfReplacingCopyMemoryWithLoadStore() { + return chance_of_replacing_copy_memory_with_load_store_; + } + uint32_t GetChanceOfReplacingCopyObjectWithStoreLoad() { + return chance_of_replacing_copyobject_with_store_load_; + } uint32_t GetChanceOfReplacingIdWithSynonym() { return chance_of_replacing_id_with_synonym_; } @@ -218,6 +227,9 @@ class FuzzerContext { uint32_t GetChanceOfReplacingParametersWithGlobals() { return chance_of_replacing_parameters_with_globals_; } + uint32_t GetChanceOfReplacingParametersWithStruct() { + return chance_of_replacing_parameters_with_struct_; + } uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; } uint32_t GetChanceOfSwappingConditionalBranchOperands() { return chance_of_swapping_conditional_branch_operands_; @@ -234,6 +246,9 @@ class FuzzerContext { uint32_t GetMaximumNumberOfFunctionParameters() { return max_number_of_function_parameters_; } + uint32_t GetMaximumNumberOfParametersReplacedWithStruct() { + return max_number_of_parameters_replaced_with_struct_; + } std::pair GetRandomBranchWeights() { std::pair branch_weights = {0, 0}; @@ -275,6 +290,12 @@ class FuzzerContext { {1, std::min(max_number_of_new_parameters_, GetMaximumNumberOfFunctionParameters() - num_of_params)}); } + uint32_t GetRandomNumberOfParametersReplacedWithStruct(uint32_t num_params) { + assert(num_params != 0 && "A function must have parameters to replace"); + return ChooseBetweenMinAndMax( + {1, std::min(num_params, + GetMaximumNumberOfParametersReplacedWithStruct())}); + } uint32_t GetRandomSizeForNewArray() { // Ensure that the array size is non-zero. return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1; @@ -312,6 +333,7 @@ class FuzzerContext { uint32_t chance_of_adding_matrix_type_; uint32_t chance_of_adding_no_contraction_decoration_; uint32_t chance_of_adding_parameters; + uint32_t chance_of_adding_relaxed_decoration_; uint32_t chance_of_adding_store_; uint32_t chance_of_adding_synonyms_; uint32_t chance_of_adding_vector_shuffle_; @@ -338,9 +360,12 @@ class FuzzerContext { uint32_t chance_of_permuting_parameters_; uint32_t chance_of_permuting_phi_operands_; uint32_t chance_of_pushing_id_through_variable_; + uint32_t chance_of_replacing_copy_memory_with_load_store_; + uint32_t chance_of_replacing_copyobject_with_store_load_; 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_replacing_parameters_with_struct_; uint32_t chance_of_splitting_block_; uint32_t chance_of_swapping_conditional_branch_operands_; uint32_t chance_of_toggling_access_chain_instruction_; @@ -355,6 +380,7 @@ class FuzzerContext { uint32_t max_new_array_size_limit_; uint32_t max_number_of_function_parameters_; uint32_t max_number_of_new_parameters_; + uint32_t max_number_of_parameters_replaced_with_struct_; // Functions to determine with what probability to go deeper when generating // or mutating constructs recursively. diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp index bf409c9ae..ebd88d010 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp @@ -153,9 +153,7 @@ void FuzzerPass::ForEachInstructionWithInstructionDescriptor( } uint32_t FuzzerPass::FindOrCreateBoolType() { - opt::analysis::Bool bool_type; - auto existing_id = GetIRContext()->get_type_mgr()->GetId(&bool_type); - if (existing_id) { + if (auto existing_id = fuzzerutil::MaybeGetBoolType(GetIRContext())) { return existing_id; } auto result = GetFuzzerContext()->GetFreshId(); @@ -277,62 +275,52 @@ uint32_t FuzzerPass::FindOrCreatePointerToIntegerType( } uint32_t FuzzerPass::FindOrCreateIntegerConstant( - const std::vector& words, uint32_t width, bool is_signed) { + const std::vector& words, uint32_t width, bool is_signed, + bool is_irrelevant) { auto int_type_id = FindOrCreateIntegerType(width, is_signed); - opt::analysis::IntConstant int_constant( - GetIRContext()->get_type_mgr()->GetType(int_type_id)->AsInteger(), words); - auto existing_constant = - GetIRContext()->get_constant_mgr()->FindConstant(&int_constant); - if (existing_constant) { - return GetIRContext() - ->get_constant_mgr() - ->GetDefiningInstruction(existing_constant) - ->result_id(); + if (auto constant_id = fuzzerutil::MaybeGetScalarConstant( + GetIRContext(), *GetTransformationContext(), words, int_type_id, + is_irrelevant)) { + return constant_id; } auto result = GetFuzzerContext()->GetFreshId(); - ApplyTransformation( - TransformationAddConstantScalar(result, int_type_id, words)); + ApplyTransformation(TransformationAddConstantScalar(result, int_type_id, + words, is_irrelevant)); return result; } uint32_t FuzzerPass::FindOrCreateFloatConstant( - const std::vector& words, uint32_t width) { + const std::vector& words, uint32_t width, bool is_irrelevant) { auto float_type_id = FindOrCreateFloatType(width); opt::analysis::FloatConstant float_constant( GetIRContext()->get_type_mgr()->GetType(float_type_id)->AsFloat(), words); - auto existing_constant = - GetIRContext()->get_constant_mgr()->FindConstant(&float_constant); - if (existing_constant) { - return GetIRContext() - ->get_constant_mgr() - ->GetDefiningInstruction(existing_constant) - ->result_id(); + if (auto constant_id = fuzzerutil::MaybeGetScalarConstant( + GetIRContext(), *GetTransformationContext(), words, float_type_id, + is_irrelevant)) { + return constant_id; } auto result = GetFuzzerContext()->GetFreshId(); - ApplyTransformation( - TransformationAddConstantScalar(result, float_type_id, words)); + ApplyTransformation(TransformationAddConstantScalar(result, float_type_id, + words, is_irrelevant)); return result; } -uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value) { +uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value, bool is_irrelevant) { auto bool_type_id = FindOrCreateBoolType(); - opt::analysis::BoolConstant bool_constant( - GetIRContext()->get_type_mgr()->GetType(bool_type_id)->AsBool(), value); - auto existing_constant = - GetIRContext()->get_constant_mgr()->FindConstant(&bool_constant); - if (existing_constant) { - return GetIRContext() - ->get_constant_mgr() - ->GetDefiningInstruction(existing_constant) - ->result_id(); + if (auto constant_id = fuzzerutil::MaybeGetScalarConstant( + GetIRContext(), *GetTransformationContext(), {value ? 1u : 0u}, + bool_type_id, is_irrelevant)) { + return constant_id; } auto result = GetFuzzerContext()->GetFreshId(); - ApplyTransformation(TransformationAddConstantBoolean(result, value)); + ApplyTransformation( + TransformationAddConstantBoolean(result, value, is_irrelevant)); return result; } uint32_t FuzzerPass::FindOrCreateConstant(const std::vector& words, - uint32_t type_id) { + uint32_t type_id, + bool is_irrelevant) { assert(type_id && "Constant's type id can't be 0."); const auto* type = GetIRContext()->get_type_mgr()->GetType(type_id); @@ -340,12 +328,12 @@ uint32_t FuzzerPass::FindOrCreateConstant(const std::vector& words, if (type->AsBool()) { assert(words.size() == 1); - return FindOrCreateBoolConstant(words[0]); + return FindOrCreateBoolConstant(words[0], is_irrelevant); } else if (const auto* integer = type->AsInteger()) { return FindOrCreateIntegerConstant(words, integer->width(), - integer->IsSigned()); + integer->IsSigned(), is_irrelevant); } else if (const auto* floating = type->AsFloat()) { - return FindOrCreateFloatConstant(words, floating->width()); + return FindOrCreateFloatConstant(words, floating->width(), is_irrelevant); } // This assertion will fail in debug build but not in release build @@ -355,21 +343,17 @@ uint32_t FuzzerPass::FindOrCreateConstant(const std::vector& words, } uint32_t FuzzerPass::FindOrCreateCompositeConstant( - const std::vector& component_ids, uint32_t type_id) { - assert(type_id && "|type_id| can't be 0"); - const auto* type_inst = GetIRContext()->get_def_use_mgr()->GetDef(type_id); - assert(type_inst && "|type_id| is invalid"); - - std::vector constants; - for (auto id : component_ids) { - assert(id && "Component's id can't be 0"); - const auto* constant = - GetIRContext()->get_constant_mgr()->FindDeclaredConstant(id); - assert(constant && "Component's id is invalid"); - constants.push_back(constant); + const std::vector& component_ids, uint32_t type_id, + bool is_irrelevant) { + if (auto existing_constant = fuzzerutil::MaybeGetCompositeConstant( + GetIRContext(), *GetTransformationContext(), component_ids, type_id, + is_irrelevant)) { + return existing_constant; } - - return FindOrCreateCompositeConstant(*type_inst, constants, component_ids); + uint32_t result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantComposite( + result, type_id, component_ids, is_irrelevant)); + return result; } uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) { @@ -474,51 +458,55 @@ FuzzerPass::GetAvailableBasicTypesAndPointers( } uint32_t FuzzerPass::FindOrCreateZeroConstant( - uint32_t scalar_or_composite_type_id) { + uint32_t scalar_or_composite_type_id, bool is_irrelevant) { auto type_instruction = GetIRContext()->get_def_use_mgr()->GetDef(scalar_or_composite_type_id); assert(type_instruction && "The type instruction must exist."); switch (type_instruction->opcode()) { case SpvOpTypeBool: - return FindOrCreateBoolConstant(false); + return FindOrCreateBoolConstant(false, is_irrelevant); case SpvOpTypeFloat: { auto width = type_instruction->GetSingleWordInOperand(0); auto num_words = (width + 32 - 1) / 32; return FindOrCreateFloatConstant(std::vector(num_words, 0), - width); + width, is_irrelevant); } case SpvOpTypeInt: { auto width = type_instruction->GetSingleWordInOperand(0); auto num_words = (width + 32 - 1) / 32; return FindOrCreateIntegerConstant( std::vector(num_words, 0), width, - type_instruction->GetSingleWordInOperand(1)); + type_instruction->GetSingleWordInOperand(1), is_irrelevant); } case SpvOpTypeArray: { - return GetZeroConstantForHomogeneousComposite( - *type_instruction, type_instruction->GetSingleWordInOperand(0), - fuzzerutil::GetArraySize(*type_instruction, GetIRContext())); + auto component_type_id = type_instruction->GetSingleWordInOperand(0); + auto num_components = + fuzzerutil::GetArraySize(*type_instruction, GetIRContext()); + return FindOrCreateCompositeConstant( + std::vector( + num_components, + FindOrCreateZeroConstant(component_type_id, is_irrelevant)), + scalar_or_composite_type_id, is_irrelevant); } case SpvOpTypeMatrix: case SpvOpTypeVector: { - return GetZeroConstantForHomogeneousComposite( - *type_instruction, type_instruction->GetSingleWordInOperand(0), - type_instruction->GetSingleWordInOperand(1)); + auto component_type_id = type_instruction->GetSingleWordInOperand(0); + auto num_components = type_instruction->GetSingleWordInOperand(1); + return FindOrCreateCompositeConstant( + std::vector( + num_components, + FindOrCreateZeroConstant(component_type_id, is_irrelevant)), + scalar_or_composite_type_id, is_irrelevant); } case SpvOpTypeStruct: { - std::vector field_zero_constants; std::vector field_zero_ids; for (uint32_t index = 0; index < type_instruction->NumInOperands(); index++) { - uint32_t field_constant_id = FindOrCreateZeroConstant( - type_instruction->GetSingleWordInOperand(index)); - field_zero_ids.push_back(field_constant_id); - field_zero_constants.push_back( - GetIRContext()->get_constant_mgr()->FindDeclaredConstant( - field_constant_id)); + field_zero_ids.push_back(FindOrCreateZeroConstant( + type_instruction->GetSingleWordInOperand(index), is_irrelevant)); } return FindOrCreateCompositeConstant( - *type_instruction, field_zero_constants, field_zero_ids); + field_zero_ids, scalar_or_composite_type_id, is_irrelevant); } default: assert(false && "Unknown type."); @@ -526,62 +514,5 @@ uint32_t FuzzerPass::FindOrCreateZeroConstant( } } -uint32_t FuzzerPass::FindOrCreateCompositeConstant( - const opt::Instruction& composite_type_instruction, - const std::vector& constants, - const std::vector& constant_ids) { - assert(constants.size() == constant_ids.size() && - "Precondition: |constants| and |constant_ids| must be in " - "correspondence."); - - opt::analysis::Type* composite_type = GetIRContext()->get_type_mgr()->GetType( - composite_type_instruction.result_id()); - std::unique_ptr composite_constant; - if (composite_type->AsArray()) { - composite_constant = MakeUnique( - composite_type->AsArray(), constants); - } else if (composite_type->AsMatrix()) { - composite_constant = MakeUnique( - composite_type->AsMatrix(), constants); - } else if (composite_type->AsStruct()) { - composite_constant = MakeUnique( - composite_type->AsStruct(), constants); - } else if (composite_type->AsVector()) { - composite_constant = MakeUnique( - composite_type->AsVector(), constants); - } else { - assert(false && - "Precondition: |composite_type| must declare a composite type."); - return 0; - } - - uint32_t existing_constant = - GetIRContext()->get_constant_mgr()->FindDeclaredConstant( - composite_constant.get(), composite_type_instruction.result_id()); - if (existing_constant) { - return existing_constant; - } - uint32_t result = GetFuzzerContext()->GetFreshId(); - ApplyTransformation(TransformationAddConstantComposite( - result, composite_type_instruction.result_id(), constant_ids)); - return result; -} - -uint32_t FuzzerPass::GetZeroConstantForHomogeneousComposite( - const opt::Instruction& composite_type_instruction, - uint32_t component_type_id, uint32_t num_components) { - std::vector zero_constants; - std::vector zero_ids; - uint32_t zero_component = FindOrCreateZeroConstant(component_type_id); - const opt::analysis::Constant* registered_zero_component = - GetIRContext()->get_constant_mgr()->FindDeclaredConstant(zero_component); - for (uint32_t i = 0; i < num_components; i++) { - zero_constants.push_back(registered_zero_component); - zero_ids.push_back(zero_component); - } - return FindOrCreateCompositeConstant(composite_type_instruction, - zero_constants, zero_ids); -} - } // namespace fuzz } // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h index 0603c940d..d34de5dc2 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h @@ -174,32 +174,48 @@ class FuzzerPass { // width and signedness specified by |width| and |is_signed|, respectively, // with |words| as its value. If either the required integer type or the // constant do not exist, transformations are applied to add them. + // The returned id either participates in IdIsIrrelevant fact or not, + // depending + // on the |is_irrelevant| parameter. uint32_t FindOrCreateIntegerConstant(const std::vector& words, - uint32_t width, bool is_signed); + uint32_t width, bool is_signed, + bool is_irrelevant); // Returns the id of an OpConstant instruction, with a floating-point // type of width specified by |width|, with |words| as its value. If either // the required floating-point type or the constant do not exist, - // transformations are applied to add them. + // transformations are applied to add them. The returned id either + // participates in IdIsIrrelevant fact or not, depending on the + // |is_irrelevant| parameter. uint32_t FindOrCreateFloatConstant(const std::vector& words, - uint32_t width); + uint32_t width, bool is_irrelevant); // Returns the id of an OpConstantTrue or OpConstantFalse instruction, // according to |value|. If either the required instruction or the bool // type do not exist, transformations are applied to add them. - uint32_t FindOrCreateBoolConstant(bool value); + // The returned id either participates in IdIsIrrelevant fact or not, + // depending on the |is_irrelevant| parameter. + uint32_t FindOrCreateBoolConstant(bool value, bool is_irrelevant); // Returns the id of an OpConstant instruction of type with |type_id| // that consists of |words|. If that instruction doesn't exist, // transformations are applied to add it. |type_id| must be a valid // result id of either scalar or boolean OpType* instruction that exists - // in the module. + // in the module. The returned id either participates in IdIsIrrelevant fact + // or not, depending on the |is_irrelevant| parameter. uint32_t FindOrCreateConstant(const std::vector& words, - uint32_t type_id); + uint32_t type_id, bool is_irrelevant); - // Returns the id of an OpConstantComposite + // Returns the id of an OpConstantComposite instruction of type with |type_id| + // that consists of |component_ids|. If that instruction doesn't exist, + // transformations are applied to add it. |type_id| must be a valid + // result id of an OpType* instruction that represents a composite type + // (i.e. a vector, matrix, struct or array). + // The returned id either participates in IdIsIrrelevant fact or not, + // depending on the |is_irrelevant| parameter. uint32_t FindOrCreateCompositeConstant( - const std::vector& component_ids, uint32_t type_id); + const std::vector& component_ids, uint32_t type_id, + bool is_irrelevant); // Returns the result id of an instruction of the form: // %id = OpUndef %|type_id| @@ -232,7 +248,8 @@ class FuzzerPass { // some scalar or composite type, returns the result id of an instruction // defining a constant of the given type that is zero or false at everywhere. // If such an instruction does not yet exist, transformations are applied to - // add it. + // add it. The returned id either participates in IdIsIrrelevant fact or not, + // depending on the |is_irrelevant| parameter. // // Examples: // --------------+------------------------------- @@ -254,31 +271,10 @@ class FuzzerPass { // uint2 u; | // } | // --------------+------------------------------- - uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id); + uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id, + bool is_irrelevant); private: - // Array, matrix and vector are *homogeneous* composite types in the sense - // that every component of one of these types has the same type. Given a - // homogeneous composite type instruction, |composite_type_instruction|, - // returns the id of a composite constant instruction for which every element - // is zero/false. If such an instruction does not yet exist, transformations - // are applied to add it. - uint32_t GetZeroConstantForHomogeneousComposite( - const opt::Instruction& composite_type_instruction, - uint32_t component_type_id, uint32_t num_components); - - // Helper to find an existing composite constant instruction of the given - // composite type with the given constant components, or to apply - // transformations to create such an instruction if it does not yet exist. - // Parameter |composite_type_instruction| must be a composite type - // instruction. The parameters |constants| and |constant_ids| must have the - // same size, and it must be the case that for each i, |constant_ids[i]| is - // the result id of an instruction that defines |constants[i]|. - uint32_t FindOrCreateCompositeConstant( - const opt::Instruction& composite_type_instruction, - const std::vector& constants, - const std::vector& constant_ids); - opt::IRContext* ir_context_; TransformationContext* transformation_context_; FuzzerContext* fuzzer_context_; diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp index 6d5164eeb..cc4c296f8 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp @@ -138,7 +138,7 @@ void FuzzerPassAddAccessChains::Apply() { uint32_t index_value = GetFuzzerContext()->GetRandomIndexForAccessChain(bound); index_ids.push_back(FindOrCreateIntegerConstant( - {index_value}, 32, GetFuzzerContext()->ChooseEven())); + {index_value}, 32, GetFuzzerContext()->ChooseEven(), false)); switch (subobject_type->opcode()) { case SpvOpTypeArray: case SpvOpTypeMatrix: diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp index 304826cc6..653d7848a 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp @@ -97,7 +97,7 @@ void FuzzerPassAddCompositeTypes::AddNewArrayType() { ApplyTransformation(TransformationAddTypeArray( GetFuzzerContext()->GetFreshId(), ChooseScalarOrCompositeType(), FindOrCreateIntegerConstant( - {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false))); + {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false, false))); } void FuzzerPassAddCompositeTypes::AddNewStructType() { diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp index ed375a1d7..d98619c26 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp @@ -74,7 +74,7 @@ void FuzzerPassAddCopyMemory::Apply() { ApplyTransformation(TransformationAddCopyMemory( instruction_descriptor, GetFuzzerContext()->GetFreshId(), inst->result_id(), storage_class, - FindOrCreateZeroConstant(pointee_type_id))); + FindOrCreateZeroConstant(pointee_type_id, false))); }); } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp index 56d33e56f..84ed1fb99 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp @@ -45,7 +45,7 @@ void FuzzerPassAddDeadBlocks::Apply() { // Make sure the module contains a boolean constant equal to // |condition_value|. bool condition_value = GetFuzzerContext()->ChooseEven(); - FindOrCreateBoolConstant(condition_value); + FindOrCreateBoolConstant(condition_value, false); // We speculatively create a transformation, and then apply it (below) if // it turns out to be applicable. This avoids duplicating the logic for diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp index 120661670..cf4ecee1d 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp @@ -68,19 +68,16 @@ void FuzzerPassAddDeadBreaks::Apply() { merge_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) { // Add an additional operand for OpPhi instruction. // - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): - // If we have a way to communicate to the fact manager - // that a specific id use is irrelevant and could be replaced with - // something else, we should add such a fact about the zero - // provided as an OpPhi operand - phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id())); + // We mark the constant as irrelevant so that we can replace it with + // a more interesting value later. + phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id(), true)); }); } // Make sure the module has a required boolean constant to be used in // OpBranchConditional instruction. auto break_condition = GetFuzzerContext()->ChooseEven(); - FindOrCreateBoolConstant(break_condition); + FindOrCreateBoolConstant(break_condition, false); auto candidate_transformation = TransformationAddDeadBreak( block.id(), merge_block->id(), break_condition, std::move(phi_ids)); diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp index a383c2ebd..61a7c6daa 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp @@ -59,19 +59,16 @@ void FuzzerPassAddDeadContinues::Apply() { continue_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) { // Add an additional operand for OpPhi instruction. // - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): - // If we have a way to communicate to the fact manager - // that a specific id use is irrelevant and could be replaced with - // something else, we should add such a fact about the zero - // provided as an OpPhi operand - phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id())); + // We mark the constant as irrelevant so that we can replace it with a + // more interesting value later. + phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id(), true)); }); } // Make sure the module contains a boolean constant equal to // |condition_value|. bool condition_value = GetFuzzerContext()->ChooseEven(); - FindOrCreateBoolConstant(condition_value); + FindOrCreateBoolConstant(condition_value, false); // Make a transformation to add a dead continue from this node; if the // node turns out to be inappropriate (e.g. by not being in a loop) the diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp index c8f8681fe..dc811e620 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp @@ -57,9 +57,12 @@ void FuzzerPassAddEquationInstructions::Apply() { std::vector available_instructions = FindAvailableInstructions( function, block, inst_it, - [](opt::IRContext*, opt::Instruction* instruction) -> bool { + [this](opt::IRContext*, opt::Instruction* instruction) -> bool { return instruction->result_id() && instruction->type_id() && - instruction->opcode() != SpvOpUndef; + instruction->opcode() != SpvOpUndef && + !GetTransformationContext() + ->GetFactManager() + ->IdIsIrrelevant(instruction->result_id()); }); // Try the opcodes for which we know how to make ids at random until diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp index 569df103e..b6f4c85d0 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp @@ -106,148 +106,92 @@ void FuzzerPassAddFunctionCalls::Apply() { }); } -std::map> -FuzzerPassAddFunctionCalls::GetAvailableInstructionsSuitableForActualParameters( - opt::Function* function, opt::BasicBlock* block, - const opt::BasicBlock::iterator& inst_it) { - // Find all instructions in scope that could potentially be used as actual - // parameters. Weed out unsuitable pointer arguments immediately. - std::vector potentially_suitable_instructions = - FindAvailableInstructions( - function, block, inst_it, - [this, block](opt::IRContext* context, - opt::Instruction* inst) -> bool { - if (!inst->HasResultId() || !inst->type_id()) { - // An instruction needs a result id and type in order - // to be suitable as an actual parameter. - return false; - } - if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() == - SpvOpTypePointer) { - switch (inst->opcode()) { - case SpvOpFunctionParameter: - case SpvOpVariable: - // Function parameters and variables are the only - // kinds of pointer that can be used as actual - // parameters. - break; - default: - return false; - } - if (!GetTransformationContext()->GetFactManager()->BlockIsDead( - block->id()) && - !GetTransformationContext() - ->GetFactManager() - ->PointeeValueIsIrrelevant(inst->result_id())) { - // We can only pass a pointer as an actual parameter - // if the pointee value for the pointer is irrelevant, - // or if the block from which we would make the - // function call is dead. - return false; - } - } - return true; - }); - - // Group all the instructions that are potentially viable as function actual - // parameters by their result types. - std::map> result; - for (auto inst : potentially_suitable_instructions) { - if (result.count(inst->type_id()) == 0) { - // This is the first instruction of this type we have seen, so populate - // the map with an entry. - result.insert({inst->type_id(), {}}); - } - // Add the instruction to the sequence of instructions already associated - // with this type. - result.at(inst->type_id()).push_back(inst); - } - return result; -} - std::vector FuzzerPassAddFunctionCalls::ChooseFunctionCallArguments( const opt::Function& callee, opt::Function* caller_function, opt::BasicBlock* caller_block, const opt::BasicBlock::iterator& caller_inst_it) { - auto type_to_available_instructions = - GetAvailableInstructionsSuitableForActualParameters( - caller_function, caller_block, caller_inst_it); - - opt::Instruction* function_type = GetIRContext()->get_def_use_mgr()->GetDef( - callee.DefInst().GetSingleWordInOperand(1)); - assert(function_type->opcode() == SpvOpTypeFunction && - "The function type does not have the expected opcode."); - std::vector result; - for (uint32_t arg_index = 1; arg_index < function_type->NumInOperands(); - arg_index++) { - auto arg_type_id = - GetIRContext() - ->get_def_use_mgr() - ->GetDef(function_type->GetSingleWordInOperand(arg_index)) - ->result_id(); - if (type_to_available_instructions.count(arg_type_id)) { - std::vector& candidate_arguments = - type_to_available_instructions.at(arg_type_id); - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177) The value - // selected here is arbitrary. We should consider adding this - // information as a fact so that the passed parameter could be - // transformed/changed. - result.push_back(candidate_arguments[GetFuzzerContext()->RandomIndex( - candidate_arguments)] - ->result_id()); - } else { - // We don't have a suitable id in scope to pass, so we must make - // something up. - auto type_instruction = - GetIRContext()->get_def_use_mgr()->GetDef(arg_type_id); - - if (type_instruction->opcode() == SpvOpTypePointer) { - // In the case of a pointer, we make a new variable, at function - // or global scope depending on the storage class of the - // pointer. - - // Get a fresh id for the new variable. - uint32_t fresh_variable_id = GetFuzzerContext()->GetFreshId(); - - // The id of this variable is what we pass as the parameter to - // the call. - result.push_back(fresh_variable_id); - - // Now bring the variable into existence. - auto storage_class = static_cast( - type_instruction->GetSingleWordInOperand(0)); - if (storage_class == SpvStorageClassFunction) { - // Add a new zero-initialized local variable to the current - // function, noting that its pointee value is irrelevant. - ApplyTransformation(TransformationAddLocalVariable( - fresh_variable_id, arg_type_id, caller_function->result_id(), - FindOrCreateZeroConstant( - type_instruction->GetSingleWordInOperand(1)), - true)); - } else { - assert((storage_class == SpvStorageClassPrivate || - storage_class == SpvStorageClassWorkgroup) && - "Only Function, Private and Workgroup storage classes are " - "supported at present."); - // Add a new global variable to the module, zero-initializing it if - // it has Private storage class, and noting that its pointee value is - // irrelevant. - ApplyTransformation(TransformationAddGlobalVariable( - fresh_variable_id, arg_type_id, storage_class, - storage_class == SpvStorageClassPrivate - ? FindOrCreateZeroConstant( - type_instruction->GetSingleWordInOperand(1)) - : 0, - true)); + auto available_pointers = FindAvailableInstructions( + caller_function, caller_block, caller_inst_it, + [this, caller_block](opt::IRContext* /*unused*/, opt::Instruction* inst) { + if (inst->opcode() != SpvOpVariable || + inst->opcode() != SpvOpFunctionParameter) { + // Function parameters and variables are the only + // kinds of pointer that can be used as actual + // parameters. + return false; } - } else { - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): We use - // constant zero for the parameter, but could consider adding a fact - // to allow further passes to obfuscate it. - result.push_back(FindOrCreateZeroConstant(arg_type_id)); - } + + return GetTransformationContext()->GetFactManager()->BlockIsDead( + caller_block->id()) || + GetTransformationContext() + ->GetFactManager() + ->PointeeValueIsIrrelevant(inst->result_id()); + }); + + std::unordered_map> type_id_to_result_id; + for (const auto* inst : available_pointers) { + type_id_to_result_id[inst->type_id()].push_back(inst->result_id()); + } + + std::vector result; + for (const auto* param : + fuzzerutil::GetParameters(GetIRContext(), callee.result_id())) { + const auto* param_type = + GetIRContext()->get_type_mgr()->GetType(param->type_id()); + assert(param_type && "Parameter has invalid type"); + + if (!param_type->AsPointer()) { + // We mark the constant as irrelevant so that we can replace it with a + // more interesting value later. + result.push_back(FindOrCreateZeroConstant(param->type_id(), true)); + continue; + } + + if (type_id_to_result_id.count(param->type_id())) { + // Use an existing pointer if there are any. + const auto& candidates = type_id_to_result_id[param->type_id()]; + result.push_back(candidates[GetFuzzerContext()->RandomIndex(candidates)]); + continue; + } + + // Make a new variable, at function or global scope depending on the storage + // class of the pointer. + + // Get a fresh id for the new variable. + uint32_t fresh_variable_id = GetFuzzerContext()->GetFreshId(); + + // The id of this variable is what we pass as the parameter to + // the call. + result.push_back(fresh_variable_id); + type_id_to_result_id[param->type_id()].push_back(fresh_variable_id); + + // Now bring the variable into existence. + auto storage_class = param_type->AsPointer()->storage_class(); + auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), param->type_id()); + if (storage_class == SpvStorageClassFunction) { + // Add a new zero-initialized local variable to the current + // function, noting that its pointee value is irrelevant. + ApplyTransformation(TransformationAddLocalVariable( + fresh_variable_id, param->type_id(), caller_function->result_id(), + FindOrCreateZeroConstant(pointee_type_id, false), true)); + } else { + assert((storage_class == SpvStorageClassPrivate || + storage_class == SpvStorageClassWorkgroup) && + "Only Function, Private and Workgroup storage classes are " + "supported at present."); + // Add a new global variable to the module, zero-initializing it if + // it has Private storage class, and noting that its pointee value is + // irrelevant. + ApplyTransformation(TransformationAddGlobalVariable( + fresh_variable_id, param->type_id(), storage_class, + storage_class == SpvStorageClassPrivate + ? FindOrCreateZeroConstant(pointee_type_id, false) + : 0, + true)); } } + return result; } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h index 8f75e8cf9..4ed879116 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h @@ -34,14 +34,6 @@ class FuzzerPassAddFunctionCalls : public FuzzerPass { void Apply() override; private: - // Identify all instructions available at |instr_it|, in block |block| of - // |function|, that are potentially suitable as function call actual - // parameters. The results are grouped by type. - std::map> - GetAvailableInstructionsSuitableForActualParameters( - opt::Function* function, opt::BasicBlock* block, - const opt::BasicBlock::iterator& inst_it); - // Randomly chooses suitable arguments to invoke |callee| right before // instruction |caller_inst_it| of block |caller_block| in |caller_function|, // based on both existing available instructions and the addition of new 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 2a6fdc28f..9a45a374d 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 @@ -85,7 +85,7 @@ void FuzzerPassAddGlobalVariables::Apply() { GetFuzzerContext()->GetFreshId(), pointer_type_id, variable_storage_class, variable_storage_class == SpvStorageClassPrivate - ? FindOrCreateZeroConstant(basic_type) + ? FindOrCreateZeroConstant(basic_type, false) : 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 index 01fd282dd..313bb0d1f 100644 --- 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 @@ -184,7 +184,7 @@ void FuzzerPassAddImageSampleUnusedComponents::Apply() { // FindOrCreateZeroConstant // %20 = OpConstant %4 0 // %21 = OpConstantComposite %5 %20 %20 - FindOrCreateZeroConstant(zero_constant_type_id)}, + FindOrCreateZeroConstant(zero_constant_type_id, false)}, MakeInstructionDescriptor(GetIRContext(), instruction), coordinate_with_unused_components_id)); diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp index 661159e65..ef8b5d0e9 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp @@ -71,7 +71,7 @@ void FuzzerPassAddLocalVariables::Apply() { } ApplyTransformation(TransformationAddLocalVariable( GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(), - FindOrCreateZeroConstant(basic_type), true)); + FindOrCreateZeroConstant(basic_type, false), true)); } } } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp index c03b4f4ee..c5c9c3363 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp @@ -72,9 +72,11 @@ void FuzzerPassAddParameters::Apply() { for (uint32_t i = 0; i < num_new_parameters; ++i) { ApplyTransformation(TransformationAddParameter( function.result_id(), GetFuzzerContext()->GetFreshId(), + // We mark the constant as irrelevant so that we can replace it with a + // more interesting value later. FindOrCreateZeroConstant( - type_candidates[GetFuzzerContext()->RandomIndex( - type_candidates)]), + type_candidates[GetFuzzerContext()->RandomIndex(type_candidates)], + true), GetFuzzerContext()->GetFreshId())); } } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp new file mode 100644 index 000000000..a2497df78 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp @@ -0,0 +1,55 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_relaxed_decorations.h" + +#include "source/fuzz/transformation_add_relaxed_decoration.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddRelaxedDecorations::FuzzerPassAddRelaxedDecorations( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddRelaxedDecorations::~FuzzerPassAddRelaxedDecorations() = default; + +void FuzzerPassAddRelaxedDecorations::Apply() { + // Consider every instruction in every block in every function. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& inst : block) { + // Randomly choose whether to apply the RelaxedPrecision decoration + // to this instruction. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingRelaxedDecoration())) { + TransformationAddRelaxedDecoration transformation(inst.result_id()); + // Restrict attention to numeric instructions (returning 32-bit + // floats or ints according to SPIR-V documentation) in dead blocks. + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + transformation.Apply(GetIRContext(), GetTransformationContext()); + *GetTransformations()->add_transformation() = + transformation.ToMessage(); + } + } + } + } + } +} +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h new file mode 100644 index 000000000..dad5dfc9a --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h @@ -0,0 +1,39 @@ +// 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 SPIRV_TOOLS_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H +#define SPIRV_TOOLS_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that applies the Relaxed decoration to numeric instructions. +class FuzzerPassAddRelaxedDecorations : public FuzzerPass { + public: + FuzzerPassAddRelaxedDecorations( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddRelaxedDecorations() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SPIRV_TOOLS_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp index 032f3697a..f1091740b 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp @@ -53,11 +53,12 @@ void FuzzerPassAddSynonyms::Apply() { // Select all instructions that can be used to create a synonym to. auto available_instructions = FindAvailableInstructions( function, block, inst_it, - [synonym_type](opt::IRContext* ir_context, opt::Instruction* inst) { + [synonym_type, this](opt::IRContext* ir_context, + opt::Instruction* inst) { // Check that we can create a synonym to |inst| as described by // the |synonym_type| and insert it before |inst_it|. return TransformationAddSynonym::IsInstructionValid( - ir_context, inst, synonym_type); + ir_context, *GetTransformationContext(), inst, synonym_type); }); if (available_instructions.empty()) { @@ -76,7 +77,7 @@ void FuzzerPassAddSynonyms::Apply() { case protobufs::TransformationAddSynonym::LOGICAL_OR: // Create a zero constant to be used as an operand of the synonymous // instruction. - FindOrCreateZeroConstant(existing_synonym->type_id()); + FindOrCreateZeroConstant(existing_synonym->type_id(), false); break; case protobufs::TransformationAddSynonym::MUL_ONE: case protobufs::TransformationAddSynonym::LOGICAL_AND: { @@ -96,13 +97,13 @@ void FuzzerPassAddSynonyms::Apply() { FindOrCreateCompositeConstant( std::vector( vector->element_count(), - FindOrCreateConstant({one_word}, element_type_id)), - existing_synonym->type_id()); + FindOrCreateConstant({one_word}, element_type_id, false)), + existing_synonym->type_id(), false); } else { FindOrCreateConstant( {existing_synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u}, - existing_synonym->type_id()); + existing_synonym->type_id(), false); } } break; default: diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp index 21b5310c0..453448ba2 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp @@ -60,10 +60,10 @@ void FuzzerPassAddVectorShuffleInstructions::Apply() { std::vector vector_instructions = FindAvailableInstructions( function, block, instruction_iterator, - [instruction_descriptor]( + [this, instruction_descriptor]( opt::IRContext* ir_context, opt::Instruction* instruction) -> bool { - if (!instruction->type_id()) { + if (!instruction->result_id() || !instruction->type_id()) { return false; } @@ -73,6 +73,18 @@ void FuzzerPassAddVectorShuffleInstructions::Apply() { return false; } + if (!GetTransformationContext() + ->GetFactManager() + ->IdIsIrrelevant(instruction->result_id()) && + !fuzzerutil::CanMakeSynonymOf(ir_context, + *GetTransformationContext(), + instruction)) { + // If the id is irrelevant, we can use it since it will not + // participate in DataSynonym fact. Otherwise, we should be + // able to produce a synonym out of the id. + return false; + } + return fuzzerutil::IdIsAvailableBeforeInstruction( ir_context, FindInstruction(instruction_descriptor, ir_context), diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp index 122c2ddfe..2808ad561 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp @@ -142,6 +142,9 @@ void FuzzerPassApplyIdSynonyms::Apply() { : parent_block->terminator(); } + assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + synonym_to_try->object()) && + "Irrelevant ids can't participate in DataSynonym facts"); ApplyTransformation(TransformationCompositeExtract( MakeInstructionDescriptor(GetIRContext(), instruction_to_insert_before), diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp index e78f8ece0..6443e896f 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp @@ -14,12 +14,10 @@ #include "source/fuzz/fuzzer_pass_construct_composites.h" -#include #include #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/transformation_composite_construct.h" -#include "source/util/make_unique.h" namespace spvtools { namespace fuzz { @@ -67,8 +65,23 @@ void FuzzerPassConstructComposites::Apply() { // program point) and suitable for making a synonym of, associate it // with the id of its result type. TypeIdToInstructions type_id_to_available_instructions; - for (auto instruction : FindAvailableInstructions( - function, block, inst_it, fuzzerutil::CanMakeSynonymOf)) { + auto available_instructions = FindAvailableInstructions( + function, block, inst_it, + [this](opt::IRContext* ir_context, opt::Instruction* inst) { + if (!inst->result_id() || !inst->type_id()) { + return false; + } + + // If the id is irrelevant, we can use it since it will not + // participate in DataSynonym fact. Otherwise, we should be able + // to produce a synonym out of the id. + return GetTransformationContext() + ->GetFactManager() + ->IdIsIrrelevant(inst->result_id()) || + fuzzerutil::CanMakeSynonymOf( + ir_context, *GetTransformationContext(), inst); + }); + for (auto instruction : available_instructions) { RecordAvailableInstruction(instruction, &type_id_to_available_instructions); } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp index 5933bf6cf..81326ac7f 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp @@ -55,8 +55,12 @@ void FuzzerPassCopyObjects::Apply() { } std::vector relevant_instructions = - FindAvailableInstructions(function, block, inst_it, - fuzzerutil::CanMakeSynonymOf); + FindAvailableInstructions( + function, block, inst_it, + [this](opt::IRContext* ir_context, opt::Instruction* inst) { + return fuzzerutil::CanMakeSynonymOf( + ir_context, *GetTransformationContext(), inst); + }); // At this point, |relevant_instructions| contains all the instructions // we might think of copying. diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp index ffccd3c1a..74bd7ed11 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -328,7 +328,8 @@ void FuzzerPassDonateModules::HandleTypeOrValue( ApplyTransformation(TransformationAddTypeArray( new_result_id, original_id_to_donated_id->at(component_type_id), FindOrCreateIntegerConstant( - {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false))); + {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false, + false))); } break; case SpvOpTypeStruct: { // Similar to SpvOpTypeArray. @@ -446,7 +447,7 @@ void FuzzerPassDonateModules::HandleTypeOrValue( auto value = type_or_value.opcode() == SpvOpConstantTrue || type_or_value.opcode() == SpvOpSpecConstantTrue; ApplyTransformation( - TransformationAddConstantBoolean(new_result_id, value)); + TransformationAddConstantBoolean(new_result_id, value, false)); } break; case SpvOpSpecConstant: case SpvOpConstant: { @@ -459,7 +460,7 @@ void FuzzerPassDonateModules::HandleTypeOrValue( }); ApplyTransformation(TransformationAddConstantScalar( new_result_id, original_id_to_donated_id->at(type_or_value.type_id()), - data_words)); + data_words, false)); } break; case SpvOpSpecConstantComposite: case SpvOpConstantComposite: { @@ -482,7 +483,7 @@ void FuzzerPassDonateModules::HandleTypeOrValue( }); ApplyTransformation(TransformationAddConstantComposite( new_result_id, original_id_to_donated_id->at(type_or_value.type_id()), - constituent_ids)); + constituent_ids, false)); } break; case SpvOpConstantNull: { if (!original_id_to_donated_id->count(type_or_value.type_id())) { @@ -544,7 +545,8 @@ void FuzzerPassDonateModules::HandleTypeOrValue( ? 0 : FindOrCreateZeroConstant( fuzzerutil::GetPointeeTypeIdFromPointerType( - GetIRContext(), remapped_pointer_type)); + GetIRContext(), remapped_pointer_type), + false); } else { // The variable already had an initializer; use its remapped id. initializer_id = original_id_to_donated_id->at( @@ -919,11 +921,10 @@ void FuzzerPassDonateModules::HandleDifficultInstruction( // We find or add a zero constant to the receiving module for the type in // question, and add an OpCopyObject instruction that copies this zero. - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): - // Using this particular constant is arbitrary, so if we have a - // mechanism for noting that an id use is arbitrary and could be - // fuzzed we should use it here. - auto zero_constant = FindOrCreateZeroConstant(remapped_type_id); + // + // We mark the constant as irrelevant so that we can replace it with a + // more interesting value later. + auto zero_constant = FindOrCreateZeroConstant(remapped_type_id, true); donated_instructions->push_back(MakeInstructionMessage( SpvOpCopyObject, remapped_type_id, original_id_to_donated_id->at(instruction.result_id()), @@ -980,9 +981,11 @@ void FuzzerPassDonateModules::PrepareInstructionForDonation( // This is an uninitialized local variable. Initialize it to zero. input_operands.push_back( {SPV_OPERAND_TYPE_ID, - {FindOrCreateZeroConstant(fuzzerutil::GetPointeeTypeIdFromPointerType( - GetIRContext(), - original_id_to_donated_id->at(instruction.type_id())))}}); + {FindOrCreateZeroConstant( + fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), + original_id_to_donated_id->at(instruction.type_id())), + false)}}); } if (instruction.result_id() && @@ -1013,9 +1016,9 @@ void FuzzerPassDonateModules::AddLivesafeFunction( FindOrCreateBoolType(); // Needed for comparisons FindOrCreatePointerToIntegerType( 32, false, SpvStorageClassFunction); // Needed for adding loop limiters - FindOrCreateIntegerConstant({0}, 32, + FindOrCreateIntegerConstant({0}, 32, false, false); // Needed for initializing loop limiters - FindOrCreateIntegerConstant({1}, 32, + FindOrCreateIntegerConstant({1}, 32, false, false); // Needed for incrementing loop limiters // Get a fresh id for the variable that will be used as a loop limiter. @@ -1023,7 +1026,7 @@ void FuzzerPassDonateModules::AddLivesafeFunction( // Choose a random loop limit, and add the required constant to the // module if not already there. const uint32_t loop_limit = FindOrCreateIntegerConstant( - {GetFuzzerContext()->GetRandomLoopLimit()}, 32, false); + {GetFuzzerContext()->GetRandomLoopLimit()}, 32, false, false); // Consider every loop header in the function to donate, and create a // structure capturing the ids to be used for manipulating the loop @@ -1118,7 +1121,7 @@ void FuzzerPassDonateModules::AddLivesafeFunction( // whose value is one less than the bound, to compare // against and to use as the clamped value. FindOrCreateIntegerConstant({bound - 1}, 32, - index_int_type->IsSigned()); + index_int_type->IsSigned(), false); } should_be_composite_type = TransformationAddFunction::FollowCompositeIndex( @@ -1147,7 +1150,8 @@ void FuzzerPassDonateModules::AddLivesafeFunction( assert(function_return_type_inst->opcode() != SpvOpTypePointer && "Function return type must not be a pointer."); kill_unreachable_return_value_id = FindOrCreateZeroConstant( - original_id_to_donated_id.at(function_return_type_inst->result_id())); + original_id_to_donated_id.at(function_return_type_inst->result_id()), + false); } // Add the function in a livesafe manner. ApplyTransformation(TransformationAddFunction( diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp index 201c6ffc0..727132e55 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp @@ -14,6 +14,7 @@ // limitations under the License. #include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h" + #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/id_use_descriptor.h" #include "source/fuzz/transformation_record_synonymous_constants.h" @@ -49,7 +50,7 @@ uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant( if (kind == opt::analysis::Type::kBool || kind == opt::analysis::Type::kInteger || kind == opt::analysis::Type::kFloat) { - return FindOrCreateZeroConstant(declaration->type_id()); + return FindOrCreateZeroConstant(declaration->type_id(), false); } } @@ -83,13 +84,21 @@ void FuzzerPassInterchangeZeroLikeConstants::Apply() { for (auto constant : GetIRContext()->GetConstants()) { uint32_t constant_id = constant->result_id(); - uint32_t toggled_id = FindOrCreateToggledConstant(constant); + if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + constant_id)) { + continue; + } + uint32_t toggled_id = FindOrCreateToggledConstant(constant); if (!toggled_id) { // Not a zero-like constant continue; } + assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + toggled_id) && + "FindOrCreateToggledConstant can't produce an irrelevant id"); + // Record synonymous constants ApplyTransformation( TransformationRecordSynonymousConstants(constant_id, toggled_id)); diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp index ac2a22f87..e66fc4447 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp @@ -53,9 +53,8 @@ void FuzzerPassMergeBlocks::Apply() { } while (!potential_transformations.empty()) { - uint32_t index = GetFuzzerContext()->RandomIndex(potential_transformations); - auto transformation = potential_transformations.at(index); - potential_transformations.erase(potential_transformations.begin() + index); + auto transformation = + GetFuzzerContext()->RemoveAtRandomIndex(&potential_transformations); MaybeApplyTransformation(transformation); } } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp index c92639a2c..2775bb8e1 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp @@ -311,9 +311,9 @@ void FuzzerPassObfuscateConstants::ObfuscateBoolConstant( } while (constant_index_1 == constant_index_2); auto constant_id_1 = FindOrCreateConstant( - available_constant_words[constant_index_1], chosen_type_id); + available_constant_words[constant_index_1], chosen_type_id, false); auto constant_id_2 = FindOrCreateConstant( - available_constant_words[constant_index_2], chosen_type_id); + available_constant_words[constant_index_2], chosen_type_id, false); assert(constant_id_1 != 0 && constant_id_2 != 0 && "We should not find an available constant with an id of 0."); @@ -361,7 +361,7 @@ void FuzzerPassObfuscateConstants::ObfuscateScalarConstant( // Make sure the module has OpConstant instructions for each index used to // access a uniform. for (auto index : uniform_descriptor.index()) { - FindOrCreateIntegerConstant({index}, 32, true); + FindOrCreateIntegerConstant({index}, 32, true, false); } // Make sure the module has OpTypePointer that points to the element type of diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp index 0501456c7..8d9acaa01 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp @@ -80,7 +80,7 @@ void FuzzerPassPushIdsThroughVariables::Apply() { std::vector value_instructions = FindAvailableInstructions( function, block, instruction_iterator, - [basic_type_id, instruction_descriptor]( + [this, basic_type_id, instruction_descriptor]( opt::IRContext* ir_context, opt::Instruction* instruction) -> bool { if (!instruction->result_id() || !instruction->type_id()) { @@ -91,6 +91,18 @@ void FuzzerPassPushIdsThroughVariables::Apply() { return false; } + // If the id is irrelevant, we can use it since it will not + // participate in DataSynonym fact. Otherwise, we should be + // able to produce a synonym out of the id. + if (!GetTransformationContext() + ->GetFactManager() + ->IdIsIrrelevant(instruction->result_id()) && + !fuzzerutil::CanMakeSynonymOf(ir_context, + *GetTransformationContext(), + instruction)) { + return false; + } + return fuzzerutil::IdIsAvailableBeforeInstruction( ir_context, FindInstruction(instruction_descriptor, ir_context), @@ -126,7 +138,7 @@ void FuzzerPassPushIdsThroughVariables::Apply() { // Create a constant to initialize the variable from. This might update // module's id bound so it must be done before any fresh ids are // computed. - auto initializer_id = FindOrCreateZeroConstant(basic_type_id); + auto initializer_id = FindOrCreateZeroConstant(basic_type_id, false); // Applies the push id through variable transformation. ApplyTransformation(TransformationPushIdThroughVariable( diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp new file mode 100644 index 000000000..684714666 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceCopyMemoriesWithLoadsStores:: + FuzzerPassReplaceCopyMemoriesWithLoadsStores( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassReplaceCopyMemoriesWithLoadsStores:: + ~FuzzerPassReplaceCopyMemoriesWithLoadsStores() = default; + +void FuzzerPassReplaceCopyMemoriesWithLoadsStores::Apply() { + GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { + // Randomly decide whether to replace the OpCopyMemory. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingCopyMemoryWithLoadStore())) { + return; + } + + // The instruction must be OpCopyMemory. + if (instruction->opcode() != SpvOpCopyMemory) { + return; + } + + // Apply the transformation replacing OpCopyMemory with OpLoad and OpStore. + ApplyTransformation(TransformationReplaceCopyMemoryWithLoadStore( + GetFuzzerContext()->GetFreshId(), + MakeInstructionDescriptor(GetIRContext(), instruction))); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h new file mode 100644 index 000000000..2a890064b --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h @@ -0,0 +1,41 @@ +// 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 SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H +#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Replaces instructions OpCopyMemory with loading the source variable to +// an intermediate value and storing this value into the target variable of +// the original OpCopyMemory instruction. +class FuzzerPassReplaceCopyMemoriesWithLoadsStores : public FuzzerPass { + public: + FuzzerPassReplaceCopyMemoriesWithLoadsStores( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassReplaceCopyMemoriesWithLoadsStores() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp new file mode 100644 index 000000000..e21ea5ed7 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp @@ -0,0 +1,83 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_replace_copy_object_with_store_load.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceCopyObjectsWithStoresLoads:: + FuzzerPassReplaceCopyObjectsWithStoresLoads( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassReplaceCopyObjectsWithStoresLoads:: + ~FuzzerPassReplaceCopyObjectsWithStoresLoads() = default; + +void FuzzerPassReplaceCopyObjectsWithStoresLoads::Apply() { + GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { + // Randomly decide whether to replace OpCopyObject. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingCopyObjectWithStoreLoad())) { + return; + } + // The instruction must be OpCopyObject. + if (instruction->opcode() != SpvOpCopyObject) { + return; + } + // The opcode of the type_id instruction cannot be a OpTypePointer, + // because we cannot define a pointer to pointer. + if (GetIRContext() + ->get_def_use_mgr() + ->GetDef(instruction->type_id()) + ->opcode() == SpvOpTypePointer) { + return; + } + // It must be valid to insert OpStore and OpLoad instructions + // before the instruction OpCopyObject. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, + instruction) || + !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, instruction)) { + return; + } + + // Randomly decides whether a global or local variable will be added. + auto variable_storage_class = GetFuzzerContext()->ChooseEven() + ? SpvStorageClassPrivate + : SpvStorageClassFunction; + + // Find or create a constant to initialize the variable from. + auto variable_initializer_id = + FindOrCreateZeroConstant(instruction->type_id(), false); + + // Make sure that pointer type is defined. + FindOrCreatePointerType(instruction->type_id(), variable_storage_class); + // Apply the transformation replacing OpCopyObject with Store and Load. + ApplyTransformation(TransformationReplaceCopyObjectWithStoreLoad( + instruction->result_id(), GetFuzzerContext()->GetFreshId(), + variable_storage_class, variable_initializer_id)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h new file mode 100644 index 000000000..15ec10b0c --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h @@ -0,0 +1,41 @@ +// 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 SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H +#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Replaces instructions OpCopyObject with storing into a new variable +// and immediately loading this variable to |result_id| of the +// original OpCopyObject instruction. +class FuzzerPassReplaceCopyObjectsWithStoresLoads : public FuzzerPass { + public: + FuzzerPassReplaceCopyObjectsWithStoresLoads( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassReplaceCopyObjectsWithStoresLoads() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp index 907c5b41d..8672a3bbe 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp @@ -81,7 +81,7 @@ void FuzzerPassReplaceParameterWithGlobal::Apply() { FindOrCreatePointerType(replaced_param->type_id(), SpvStorageClassPrivate); // Make sure initializer for the global variable exists in the module. - FindOrCreateZeroConstant(replaced_param->type_id()); + FindOrCreateZeroConstant(replaced_param->type_id(), false); ApplyTransformation(TransformationReplaceParameterWithGlobal( GetFuzzerContext()->GetFreshId(), replaced_param->result_id(), diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp new file mode 100644 index 000000000..e49eacb7a --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp @@ -0,0 +1,115 @@ +// 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_params_with_struct.h" + +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_replace_params_with_struct.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceParamsWithStruct::FuzzerPassReplaceParamsWithStruct( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassReplaceParamsWithStruct::~FuzzerPassReplaceParamsWithStruct() = + default; + +void FuzzerPassReplaceParamsWithStruct::Apply() { + for (const auto& function : *GetIRContext()->module()) { + auto params = + fuzzerutil::GetParameters(GetIRContext(), function.result_id()); + + if (params.empty() || fuzzerutil::FunctionIsEntryPoint( + GetIRContext(), function.result_id())) { + continue; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfReplacingParametersWithStruct())) { + continue; + } + + std::vector parameter_index(params.size()); + std::iota(parameter_index.begin(), parameter_index.end(), 0); + + // Remove the indices of unsupported parameters. + auto new_end = std::remove_if( + parameter_index.begin(), parameter_index.end(), + [this, ¶ms](uint32_t index) { + const auto* type = + GetIRContext()->get_type_mgr()->GetType(params[index]->type_id()); + assert(type && "Parameter has invalid type"); + return !TransformationReplaceParamsWithStruct:: + IsParameterTypeSupported(*type); + }); + + // std::remove_if changes the vector so that removed elements are placed at + // the end (i.e. [new_end, parameter_index.end()) is a range of removed + // elements). However, the size of the vector is not changed so we need to + // change that explicitly by popping those elements from the vector. + parameter_index.erase(new_end, parameter_index.end()); + + if (parameter_index.empty()) { + continue; + } + + // Select |num_replaced_params| parameters at random. We shuffle the vector + // of indices for randomization and shrink it to select first + // |num_replaced_params| parameters. + auto num_replaced_params = std::min( + parameter_index.size(), + GetFuzzerContext()->GetRandomNumberOfParametersReplacedWithStruct( + static_cast(params.size()))); + + GetFuzzerContext()->Shuffle(¶meter_index); + parameter_index.resize(num_replaced_params); + + // Make sure OpTypeStruct exists in the module. + std::vector component_type_ids; + for (auto index : parameter_index) { + component_type_ids.push_back(params[index]->type_id()); + } + + FindOrCreateStructType(component_type_ids); + + // Map parameters' indices to parameters' ids. + std::vector parameter_id; + for (auto index : parameter_index) { + parameter_id.push_back(params[index]->result_id()); + } + + std::unordered_map caller_id_to_fresh_id; + for (const auto* inst : + fuzzerutil::GetCallers(GetIRContext(), function.result_id())) { + caller_id_to_fresh_id[inst->result_id()] = + GetFuzzerContext()->GetFreshId(); + } + + ApplyTransformation(TransformationReplaceParamsWithStruct( + parameter_id, GetFuzzerContext()->GetFreshId(), + GetFuzzerContext()->GetFreshId(), caller_id_to_fresh_id)); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h new file mode 100644 index 000000000..ed1aa6b5f --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.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_PARAMS_WITH_STRUCT_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Iterates over all functions in the module and randomly decides for each one +// whether to replace a subset of its parameters with a struct value. +class FuzzerPassReplaceParamsWithStruct : public FuzzerPass { + public: + FuzzerPassReplaceParamsWithStruct( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassReplaceParamsWithStruct() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp index a9b03b3bd..953685a51 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp @@ -23,6 +23,25 @@ namespace spvtools { namespace fuzz { namespace fuzzerutil { +namespace { + +uint32_t MaybeGetOpConstant(opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, + uint32_t type_id, bool is_irrelevant) { + for (const auto& inst : ir_context->types_values()) { + if (inst.opcode() == SpvOpConstant && inst.type_id() == type_id && + inst.GetInOperand(0).words == words && + transformation_context.GetFactManager()->IdIsIrrelevant( + inst.result_id()) == is_irrelevant) { + return inst.result_id(); + } + } + + return 0; +} + +} // namespace bool IsFreshId(opt::IRContext* context, uint32_t id) { return !context->get_def_use_mgr()->GetDef(id); @@ -114,7 +133,7 @@ bool PhiIdsOkForNewEdge( void AddUnreachableEdgeAndUpdateOpPhis( opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, - bool condition_value, + uint32_t bool_id, const google::protobuf::RepeatedField& phi_ids) { assert(PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) && "Precondition on phi_ids is not satisfied"); @@ -122,10 +141,13 @@ 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 = MaybeGetBoolConstant(context, condition_value); - assert( - bool_id && - "Precondition that condition value must be available is not satisfied"); + auto condition_inst = context->get_def_use_mgr()->GetDef(bool_id); + assert(condition_inst && + (condition_inst->opcode() == SpvOpConstantTrue || + condition_inst->opcode() == SpvOpConstantFalse) && + "|bool_id| is invalid"); + + auto condition_value = condition_inst->opcode() == SpvOpConstantTrue; const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to); auto successor = bb_from->terminator()->GetSingleWordInOperand(0); @@ -226,7 +248,9 @@ bool CanInsertOpcodeBeforeInstruction( return opcode == SpvOpPhi || instruction_in_block->opcode() != SpvOpPhi; } -bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) { +bool CanMakeSynonymOf(opt::IRContext* ir_context, + const TransformationContext& transformation_context, + opt::Instruction* inst) { if (inst->opcode() == SpvOpSampledImage) { // The SPIR-V data rules say that only very specific instructions may // may consume the result id of an OpSampledImage, and this excludes the @@ -237,6 +261,11 @@ bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) { // We can only make a synonym of an instruction that generates an id. return false; } + if (transformation_context.GetFactManager()->IdIsIrrelevant( + inst->result_id())) { + // An irrelevant id can't be a synonym of anything. + return false; + } if (!inst->type_id()) { // We can only make a synonym of an instruction that has a type. return false; @@ -706,6 +735,101 @@ std::vector GetParameters(opt::IRContext* ir_context, return result; } +std::vector GetCallers(opt::IRContext* ir_context, + uint32_t function_id) { + assert(FindFunction(ir_context, function_id) && + "|function_id| is not a result id of a function"); + + std::vector result; + ir_context->get_def_use_mgr()->ForEachUser( + function_id, [&result, function_id](opt::Instruction* inst) { + if (inst->opcode() == SpvOpFunctionCall && + inst->GetSingleWordInOperand(0) == function_id) { + result.push_back(inst); + } + }); + + return result; +} + +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 (InstructionIsFunctionParameter(param_inst, &function)) { + return &function; + } + } + + return nullptr; +} + +uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id, + uint32_t new_function_type_result_id, + uint32_t return_type_id, + const std::vector& parameter_type_ids) { + // Check some initial constraints. + assert(ir_context->get_type_mgr()->GetType(return_type_id) && + "Return type is invalid"); + for (auto id : parameter_type_ids) { + const auto* type = ir_context->get_type_mgr()->GetType(id); + (void)type; // Make compilers happy in release mode. + // Parameters can't be OpTypeVoid. + assert(type && !type->AsVoid() && "Parameter has invalid type"); + } + + auto* function = FindFunction(ir_context, function_id); + assert(function && "|function_id| is invalid"); + + auto* old_function_type = GetFunctionType(ir_context, function); + assert(old_function_type && "Function has invalid type"); + + std::vector operand_ids = {return_type_id}; + operand_ids.insert(operand_ids.end(), parameter_type_ids.begin(), + parameter_type_ids.end()); + + if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1 && + FindFunctionType(ir_context, operand_ids) == 0) { + // We can change |old_function_type| only if it's used once in the module + // and we are certain we won't create a duplicate as a result of the change. + + // Update |old_function_type| in-place. + opt::Instruction::OperandList operands; + for (auto id : operand_ids) { + operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + } + + old_function_type->SetInOperands(std::move(operands)); + + // |operands| may depend on result ids defined below the |old_function_type| + // in the module. + old_function_type->RemoveFromList(); + ir_context->AddType(std::unique_ptr(old_function_type)); + return old_function_type->result_id(); + } else { + // We can't modify the |old_function_type| so we have to either use an + // existing one or create a new one. + auto type_id = FindOrCreateFunctionType( + ir_context, new_function_type_result_id, operand_ids); + + if (type_id != old_function_type->result_id()) { + function->DefInst().SetInOperand(1, {type_id}); + + // DefUseManager hasn't been updated yet, so if the following condition is + // true, then |old_function_type| will have no users when this function + // returns. We might as well remove it. + if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) { + old_function_type->RemoveFromList(); + delete old_function_type; + } + } + + return type_id; + } +} + void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id, const std::vector& type_ids) { assert(result_id != 0 && "Result id can't be 0"); @@ -755,6 +879,11 @@ uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width) { return ir_context->get_type_mgr()->GetId(&type); } +uint32_t MaybeGetBoolType(opt::IRContext* ir_context) { + opt::analysis::Bool type; + return ir_context->get_type_mgr()->GetId(&type); +} + uint32_t MaybeGetVectorType(opt::IRContext* ir_context, uint32_t component_type_id, uint32_t element_count) { @@ -786,15 +915,18 @@ 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) { +uint32_t MaybeGetZeroConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + uint32_t scalar_or_composite_type_id, bool is_irrelevant) { 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); + return MaybeGetBoolConstant(ir_context, transformation_context, false, + is_irrelevant); case opt::analysis::Type::kFloat: case opt::analysis::Type::kInteger: { std::vector words = {0}; @@ -803,8 +935,8 @@ uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context, words.push_back(0); } - return MaybeGetScalarConstant(ir_context, words, - scalar_or_composite_type_id); + return MaybeGetScalarConstant(ir_context, transformation_context, words, + scalar_or_composite_type_id, is_irrelevant); } case opt::analysis::Type::kStruct: { std::vector component_ids; @@ -813,7 +945,16 @@ uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context, 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); + auto component_id = + MaybeGetZeroConstant(ir_context, transformation_context, + component_type_id, is_irrelevant); + if (component_id == 0 && is_irrelevant) { + // Irrelevant constants can use either relevant or irrelevant + // constituents. + component_id = MaybeGetZeroConstant( + ir_context, transformation_context, component_type_id, false); + } + if (component_id == 0) { return 0; } @@ -821,8 +962,9 @@ uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context, component_ids.push_back(component_id); } - return MaybeGetCompositeConstant(ir_context, component_ids, - scalar_or_composite_type_id); + return MaybeGetCompositeConstant( + ir_context, transformation_context, component_ids, + scalar_or_composite_type_id, is_irrelevant); } case opt::analysis::Type::kMatrix: case opt::analysis::Type::kVector: { @@ -833,39 +975,56 @@ uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context, 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); + auto component_id = MaybeGetZeroConstant( + ir_context, transformation_context, component_type_id, is_irrelevant); + + if (component_id == 0 && is_irrelevant) { + // Irrelevant constants can use either relevant or irrelevant + // constituents. + component_id = MaybeGetZeroConstant(ir_context, transformation_context, + component_type_id, false); } - return 0; + if (component_id == 0) { + return 0; + } + + auto component_count = type->AsVector() + ? type->AsVector()->element_count() + : type->AsMatrix()->element_count(); + return MaybeGetCompositeConstant( + ir_context, transformation_context, + std::vector(component_count, component_id), + scalar_or_composite_type_id, is_irrelevant); } 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"); + auto component_id = MaybeGetZeroConstant( + ir_context, transformation_context, component_type_id, is_irrelevant); - 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); + if (component_id == 0 && is_irrelevant) { + component_id = MaybeGetZeroConstant(ir_context, transformation_context, + component_type_id, false); } - return 0; + if (component_id == 0) { + return 0; + } + + 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, transformation_context, + std::vector(GetArraySize(*type_inst, ir_context), + component_id), + scalar_or_composite_type_id, is_irrelevant); } default: assert(false && "Type is not supported"); @@ -873,110 +1032,106 @@ uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context, } } -uint32_t MaybeGetScalarConstant(opt::IRContext* ir_context, - const std::vector& words, - uint32_t scalar_type_id) { +uint32_t MaybeGetScalarConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t scalar_type_id, + bool is_irrelevant) { 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()); + return MaybeGetIntegerConstant(ir_context, transformation_context, words, + int_type->width(), int_type->IsSigned(), + is_irrelevant); } else if (const auto* float_type = type->AsFloat()) { - return MaybeGetFloatConstant(ir_context, words, float_type->width()); + return MaybeGetFloatConstant(ir_context, transformation_context, words, + float_type->width(), is_irrelevant); } else { assert(type->AsBool() && words.size() == 1 && "|scalar_type_id| doesn't represent a scalar type"); - return MaybeGetBoolConstant(ir_context, words[0]); + return MaybeGetBoolConstant(ir_context, transformation_context, words[0], + is_irrelevant); } } -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); - } - +uint32_t MaybeGetCompositeConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& component_ids, uint32_t composite_type_id, + bool is_irrelevant) { const auto* type = ir_context->get_type_mgr()->GetType(composite_type_id); - assert(type && "|composite_type_id| is invalid"); + (void)type; // Make compilers happy in release mode. + assert(type && + (type->AsArray() || type->AsStruct() || type->AsVector() || + type->AsMatrix()) && + "|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; + for (const auto& inst : ir_context->types_values()) { + if (inst.opcode() == SpvOpConstantComposite && + inst.type_id() == composite_type_id && + transformation_context.GetFactManager()->IdIsIrrelevant( + inst.result_id()) == is_irrelevant && + inst.NumInOperands() == component_ids.size()) { + bool is_match = true; + + for (uint32_t i = 0; i < inst.NumInOperands(); ++i) { + if (inst.GetSingleWordInOperand(i) != component_ids[i]) { + is_match = false; + break; + } + } + + if (is_match) { + return inst.result_id(); + } + } } - return ir_context->get_constant_mgr()->FindDeclaredConstant( - composite_constant.get(), composite_type_id); + return 0; } -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; +uint32_t MaybeGetIntegerConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t width, bool is_signed, + bool is_irrelevant) { + if (auto type_id = MaybeGetIntegerType(ir_context, width, is_signed)) { + return MaybeGetOpConstant(ir_context, transformation_context, words, + type_id, is_irrelevant); } - 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); + return 0; } -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; +uint32_t MaybeGetFloatConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t width, bool is_irrelevant) { + if (auto type_id = MaybeGetFloatType(ir_context, width)) { + return MaybeGetOpConstant(ir_context, transformation_context, words, + type_id, is_irrelevant); } - 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); + return 0; } -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; +uint32_t MaybeGetBoolConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, bool value, + bool is_irrelevant) { + if (auto type_id = MaybeGetBoolType(ir_context)) { + for (const auto& inst : ir_context->types_values()) { + if (inst.opcode() == (value ? SpvOpConstantTrue : SpvOpConstantFalse) && + inst.type_id() == type_id && + transformation_context.GetFactManager()->IdIsIrrelevant( + inst.result_id()) == is_irrelevant) { + return inst.result_id(); + } + } } - 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)); + + return 0; } void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id, diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h index 94dcc837b..72d83f1ae 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h @@ -53,15 +53,17 @@ bool PhiIdsOkForNewEdge( opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, const google::protobuf::RepeatedField& phi_ids); -// 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 -// "OpBranchConditional |condition_value| ...", such that control will branch -// to %some_block, with |bb_to| being the unreachable alternative. Updates -// OpPhi instructions in |bb_to| using |phi_ids| so that the new edge is valid. +// Requires that |bool_id| is a valid result id of either OpConstantTrue or +// OpConstantFalse, that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) +// holds, and that bb_from ends with "OpBranch %some_block". Turns OpBranch +// into "OpBranchConditional |condition_value| ...", such that control will +// branch to %some_block, with |bb_to| being the unreachable alternative. +// Updates OpPhi instructions in |bb_to| using |phi_ids| so that the new edge is +// valid. |condition_value| above is equal to |true| if |bool_id| is a result id +// of an OpConstantTrue instruction. void AddUnreachableEdgeAndUpdateOpPhis( opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, - bool condition_value, + uint32_t bool_id, const google::protobuf::RepeatedField& phi_ids); // Returns true if and only if |loop_header_id| is a loop header and @@ -91,7 +93,11 @@ bool CanInsertOpcodeBeforeInstruction( SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block); // Determines whether it is OK to make a synonym of |inst|. -bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst); +// |transformation_context| is used to verify that the result id of |inst| +// does not participate in IdIsIrrelevant fact. +bool CanMakeSynonymOf(opt::IRContext* ir_context, + const TransformationContext& transformation_context, + opt::Instruction* inst); // Determines whether the given type is a composite; that is: an array, matrix, // struct or vector. @@ -275,6 +281,29 @@ bool IsPermutationOfRange(const std::vector& arr, uint32_t lo, std::vector GetParameters(opt::IRContext* ir_context, uint32_t function_id); +// Returns all OpFunctionCall instructions that call a function with result id +// |function_id|. +std::vector GetCallers(opt::IRContext* ir_context, + uint32_t function_id); + +// Returns a function that contains OpFunctionParameter instruction with result +// id |param_id|. Returns nullptr if the module has no such function. +opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context, + uint32_t param_id); + +// Changes the type of function |function_id| so that its return type is +// |return_type_id| and its parameters' types are |parameter_type_ids|. If a +// suitable function type already exists in the module, it is used, otherwise +// |new_function_type_result_id| is used as the result id of a suitable new +// function type instruction. If the old type of the function doesn't have any +// more users, it is removed from the module. Returns the result id of the +// OpTypeFunction instruction that is used as a type of the function with +// |function_id|. +uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id, + uint32_t new_function_type_result_id, + uint32_t return_type_id, + const std::vector& parameter_type_ids); + // Creates new OpTypeFunction instruction in the module. |type_ids| may not be // empty. It may not contain result ids of OpTypeFunction instructions. // |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|. @@ -302,6 +331,10 @@ uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width, // otherwise. uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width); +// Returns a result id of an OpTypeBool instruction if present. Returns 0 +// otherwise. +uint32_t MaybeGetBoolType(opt::IRContext* ir_context); + // Returns a result id of an OpTypeVector instruction if present. Returns 0 // otherwise. |component_type_id| must be a valid result id of an OpTypeInt, // OpTypeFloat or OpTypeBool instruction in the module. |element_count| must be @@ -323,39 +356,60 @@ uint32_t MaybeGetStructType(opt::IRContext* ir_context, // 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); +// The returned id either participates in IdIsIrrelevant fact or not, depending +// on the |is_irrelevant| parameter. +uint32_t MaybeGetZeroConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + uint32_t scalar_or_composite_type_id, bool is_irrelevant); // 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); +// instruction is present in the module. The returned id either participates in +// IdIsIrrelevant fact or not, depending on the |is_irrelevant| parameter. +uint32_t MaybeGetScalarConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t scalar_type_id, + bool is_irrelevant); // 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); +// module. The returned id either participates in IdIsIrrelevant fact or not, +// depending on the |is_irrelevant| parameter. +uint32_t MaybeGetCompositeConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& component_ids, uint32_t composite_type_id, + bool is_irrelevant); // 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); +// The returned id either participates in IdIsIrrelevant fact or not, depending +// on the |is_irrelevant| parameter. +uint32_t MaybeGetIntegerConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t width, bool is_signed, + bool is_irrelevant); // 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); +// The returned id either participates in IdIsIrrelevant fact or not, depending +// on the |is_irrelevant| parameter. +uint32_t MaybeGetFloatConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t width, bool is_irrelevant); // 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); +// module, or 0 otherwise. The returned id either participates in IdIsIrrelevant +// fact or not, depending on the |is_irrelevant| parameter. +uint32_t MaybeGetBoolConstant( + opt::IRContext* context, + const TransformationContext& transformation_context, bool value, + bool is_irrelevant); // Creates a new OpTypeInt instruction in the module. Updates module's id bound // to accommodate for |result_id|. diff --git a/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto index 53f571b84..968e08495 100644 --- a/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -170,6 +170,7 @@ message Fact { FactFunctionIsLivesafe function_is_livesafe_fact = 4; FactPointeeValueIsIrrelevant pointee_value_is_irrelevant_fact = 5; FactIdEquation id_equation_fact = 6; + FactIdIsIrrelevant id_is_irrelevant = 7; } } @@ -182,6 +183,7 @@ message FactBlockIsDead { // can be made to this block. uint32 block_id = 1; + } message FactConstantUniform { @@ -222,6 +224,7 @@ message FactFunctionIsLivesafe { // functions. uint32 function_id = 1; + } message FactIdEquation { @@ -249,6 +252,18 @@ message FactIdEquation { } +message FactIdIsIrrelevant { + + // Records a fact that |result_id| is irrelevant (i.e. it's usage doesn't + // change the semantics of the module). This implies that a use of this id + // can later be replaced with some other id of the same type, or the + // definition of |result_id| can be changed so that it yields a different value. + + // An irrelevant id. + uint32 result_id = 1; + +} + message FactPointeeValueIsIrrelevant { // Records the fact that value of the data pointed to by a pointer id does @@ -258,6 +273,7 @@ message FactPointeeValueIsIrrelevant { // A result id of pointer type uint32 pointer_id = 1; + } message AccessChainClampingInfo { @@ -386,6 +402,10 @@ message Transformation { TransformationReplaceParameterWithGlobal replace_parameter_with_global = 55; TransformationRecordSynonymousConstants record_synonymous_constants = 56; TransformationAddSynonym add_synonym = 57; + TransformationAddRelaxedDecoration add_relaxed_decoration = 58; + TransformationReplaceParamsWithStruct replace_params_with_struct = 59; + TransformationReplaceCopyObjectWithStoreLoad replace_copy_object_with_store_load = 60; + TransformationReplaceCopyMemoryWithLoadStore replace_copy_memory_with_load_store = 61; // Add additional option using the next available number. } } @@ -415,15 +435,21 @@ message TransformationAddConstantBoolean { // Supports adding the constants true and false to a module, which may be // necessary in order to enable other transformations if they are not present. + // Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true. uint32 fresh_id = 1; bool is_true = 2; + // If the constant should be marked as irrelevant. + bool is_irrelevant = 3; + } message TransformationAddConstantComposite { // Adds a constant of the given composite type to the module. + // Also, creates an IdIsIrrelevant fact about |fresh_id| if + // |is_irrelevant| is true. // Fresh id for the composite uint32 fresh_id = 1; @@ -434,6 +460,9 @@ message TransformationAddConstantComposite { // Constituent ids for the composite repeated uint32 constituent_id = 3; + // If the constant should be marked as irrelevant. + bool is_irrelevant = 4; + } message TransformationAddConstantNull { @@ -451,6 +480,8 @@ message TransformationAddConstantNull { message TransformationAddConstantScalar { // Adds a constant of the given scalar type. + // Also, creates an IdIsIrrelevant fact about + // |fresh_id| if |is_irrelevant| is true. // Id for the constant uint32 fresh_id = 1; @@ -461,6 +492,9 @@ message TransformationAddConstantScalar { // Value of the constant repeated uint32 word = 3; + // If the constant should be marked as irrelevant. + bool is_irrelevant = 4; + } message TransformationAddCopyMemory { @@ -687,6 +721,15 @@ message TransformationAddParameter { } +message TransformationAddRelaxedDecoration { + + // Applies OpDecorate RelaxedPrecision to the given result id + + // Result id to be decorated + uint32 result_id = 1; + +} + message TransformationAddSpecConstantOp { // Adds OpSpecConstantOp into the module. @@ -1233,6 +1276,39 @@ message TransformationReplaceConstantWithUniform { } +message TransformationReplaceCopyMemoryWithLoadStore { + + // A transformation that replaces instructions OpCopyMemory with loading + // the source variable to an intermediate value and storing this value into the + // target variable of the original OpCopyMemory instruction. + + // The intermediate value. + uint32 fresh_id = 1; + + // The instruction descriptor to OpCopyMemory. It is necessary, because + // OpCopyMemory doesn't have a result id. + InstructionDescriptor copy_memory_instruction_descriptor = 2; +} + +message TransformationReplaceCopyObjectWithStoreLoad { + + // A transformation that replaces instruction OpCopyObject with + // storing into a new variable and immediately loading from this + // variable to |result_id| of the original OpCopyObject instruction. + + // The result id of initial OpCopyObject instruction + uint32 copy_object_result_id = 1; + + // A fresh id for the variable to be stored to. + uint32 fresh_variable_id = 2; + + // The variable storage class (Function or Private). + uint32 variable_storage_class = 3; + + // Constant to initialize the variable with. + uint32 variable_initializer_id = 4; +} + message TransformationReplaceIdWithSynonym { // Replaces a use of an id with an id that is known to be synonymous, e.g. @@ -1273,6 +1349,31 @@ message TransformationReplaceLinearAlgebraInstruction { } +message TransformationReplaceParamsWithStruct { + + // Replaces parameters of the function with a struct containing + // values of those parameters. + + // Result ids of parameters to replace. + repeated uint32 parameter_id = 1; + + // Fresh id for a new function type. This might be unused if the required type + // already exists in the module or if we can change the old type. + uint32 fresh_function_type_id = 2; + + // Fresh id for a new struct function parameter to be used as a replacement. + uint32 fresh_parameter_id = 3; + + // Fresh ids for struct objects containing values of replaced parameters. + // This map contains a fresh id for at least every result id of a relevant + // OpFunctionCall instruction. + // + // While maps are not fully deterministic, the way this map is used does not + // exhibit nondeterminism. Change to repeated Uint32Pair if this changes. + map caller_id_to_fresh_composite_id = 4; + +} + message TransformationSetFunctionControl { // A transformation that sets the function control operand of an OpFunction diff --git a/3rdparty/spirv-tools/source/fuzz/transformation.cpp b/3rdparty/spirv-tools/source/fuzz/transformation.cpp index 4455107ec..a9fa61130 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation.cpp @@ -33,6 +33,7 @@ #include "source/fuzz/transformation_add_local_variable.h" #include "source/fuzz/transformation_add_no_contraction_decoration.h" #include "source/fuzz/transformation_add_parameter.h" +#include "source/fuzz/transformation_add_relaxed_decoration.h" #include "source/fuzz/transformation_add_spec_constant_op.h" #include "source/fuzz/transformation_add_synonym.h" #include "source/fuzz/transformation_add_type_array.h" @@ -61,9 +62,12 @@ #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_copy_memory_with_load_store.h" +#include "source/fuzz/transformation_replace_copy_object_with_store_load.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_replace_params_with_struct.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" @@ -128,6 +132,9 @@ std::unique_ptr Transformation::FromMessage( message.add_no_contraction_decoration()); case protobufs::Transformation::TransformationCase::kAddParameter: return MakeUnique(message.add_parameter()); + case protobufs::Transformation::TransformationCase::kAddRelaxedDecoration: + return MakeUnique( + message.add_relaxed_decoration()); case protobufs::Transformation::TransformationCase::kAddSpecConstantOp: return MakeUnique( message.add_spec_constant_op()); @@ -211,6 +218,14 @@ std::unique_ptr Transformation::FromMessage( kReplaceConstantWithUniform: return MakeUnique( message.replace_constant_with_uniform()); + case protobufs::Transformation::TransformationCase:: + kReplaceCopyMemoryWithLoadStore: + return MakeUnique( + message.replace_copy_memory_with_load_store()); + case protobufs::Transformation::TransformationCase:: + kReplaceCopyObjectWithStoreLoad: + return MakeUnique( + message.replace_copy_object_with_store_load()); case protobufs::Transformation::TransformationCase::kReplaceIdWithSynonym: return MakeUnique( message.replace_id_with_synonym()); @@ -218,6 +233,10 @@ std::unique_ptr Transformation::FromMessage( kReplaceLinearAlgebraInstruction: return MakeUnique( message.replace_linear_algebra_instruction()); + case protobufs::Transformation::TransformationCase:: + kReplaceParamsWithStruct: + return MakeUnique( + message.replace_params_with_struct()); case protobufs::Transformation::TransformationCase::kSetFunctionControl: return MakeUnique( message.set_function_control()); diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_boolean.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_boolean.cpp index 1930f7ef6..904ad6104 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_boolean.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_boolean.cpp @@ -25,34 +25,36 @@ TransformationAddConstantBoolean::TransformationAddConstantBoolean( : message_(message) {} TransformationAddConstantBoolean::TransformationAddConstantBoolean( - uint32_t fresh_id, bool is_true) { + uint32_t fresh_id, bool is_true, bool is_irrelevant) { message_.set_fresh_id(fresh_id); message_.set_is_true(is_true); + message_.set_is_irrelevant(is_irrelevant); } bool TransformationAddConstantBoolean::IsApplicable( opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { - opt::analysis::Bool bool_type; - if (!ir_context->get_type_mgr()->GetId(&bool_type)) { - // No OpTypeBool is present. - return false; - } - return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()); + return fuzzerutil::MaybeGetBoolType(ir_context) != 0 && + fuzzerutil::IsFreshId(ir_context, message_.fresh_id()); } void TransformationAddConstantBoolean::Apply( - opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - opt::analysis::Bool bool_type; + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { // Add the boolean constant to the module, ensuring the module's id bound is // high enough. fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); ir_context->module()->AddGlobalValue( message_.is_true() ? SpvOpConstantTrue : SpvOpConstantFalse, - message_.fresh_id(), ir_context->get_type_mgr()->GetId(&bool_type)); + message_.fresh_id(), fuzzerutil::MaybeGetBoolType(ir_context)); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. ir_context->InvalidateAnalysesExceptFor( opt::IRContext::Analysis::kAnalysisNone); + + if (message_.is_irrelevant()) { + transformation_context->GetFactManager()->AddFactIdIsIrrelevant( + message_.fresh_id()); + } } protobufs::Transformation TransformationAddConstantBoolean::ToMessage() const { diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_boolean.h b/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_boolean.h index 5d876cf6d..d2f9e9ad3 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_boolean.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_boolean.h @@ -28,7 +28,8 @@ class TransformationAddConstantBoolean : public Transformation { explicit TransformationAddConstantBoolean( const protobufs::TransformationAddConstantBoolean& message); - TransformationAddConstantBoolean(uint32_t fresh_id, bool is_true); + TransformationAddConstantBoolean(uint32_t fresh_id, bool is_true, + bool is_irrelevant); // - |message_.fresh_id| must not be used by the module. // - The module must already contain OpTypeBool. @@ -38,6 +39,8 @@ class TransformationAddConstantBoolean : public Transformation { // - Adds OpConstantTrue (OpConstantFalse) to the module with id // |message_.fresh_id| if |message_.is_true| holds (does not hold). + // - Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| + // is true. void Apply(opt::IRContext* ir_context, TransformationContext* transformation_context) const override; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_composite.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_composite.cpp index ae34b2627..99d88b40b 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_composite.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_composite.cpp @@ -28,9 +28,10 @@ TransformationAddConstantComposite::TransformationAddConstantComposite( TransformationAddConstantComposite::TransformationAddConstantComposite( uint32_t fresh_id, uint32_t type_id, - const std::vector& constituent_ids) { + const std::vector& constituent_ids, bool is_irrelevant) { message_.set_fresh_id(fresh_id); message_.set_type_id(type_id); + message_.set_is_irrelevant(is_irrelevant); for (auto constituent_id : constituent_ids) { message_.add_constituent_id(constituent_id); } @@ -104,7 +105,8 @@ bool TransformationAddConstantComposite::IsApplicable( } void TransformationAddConstantComposite::Apply( - opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { opt::Instruction::OperandList in_operands; for (auto constituent_id : message_.constituent_id()) { in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}}); @@ -117,6 +119,11 @@ void TransformationAddConstantComposite::Apply( // validity of existing analyses. ir_context->InvalidateAnalysesExceptFor( opt::IRContext::Analysis::kAnalysisNone); + + if (message_.is_irrelevant()) { + transformation_context->GetFactManager()->AddFactIdIsIrrelevant( + message_.fresh_id()); + } } protobufs::Transformation TransformationAddConstantComposite::ToMessage() diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_composite.h b/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_composite.h index 4fec561fc..2dddbfb5b 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_composite.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_composite.h @@ -32,7 +32,7 @@ class TransformationAddConstantComposite : public Transformation { TransformationAddConstantComposite( uint32_t fresh_id, uint32_t type_id, - const std::vector& constituent_ids); + const std::vector& constituent_ids, bool is_irrelevant); // - |message_.fresh_id| must be a fresh id // - |message_.type_id| must be the id of a composite type @@ -42,9 +42,11 @@ class TransformationAddConstantComposite : public Transformation { opt::IRContext* ir_context, const TransformationContext& transformation_context) const override; - // Adds an OpConstantComposite instruction defining a constant of type - // |message_.type_id|, using |message_.constituent_id| as constituents, with - // result id |message_.fresh_id|. + // - Adds an OpConstantComposite instruction defining a constant of type + // |message_.type_id|, using |message_.constituent_id| as constituents, with + // result id |message_.fresh_id|. + // - Creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is + // true. void Apply(opt::IRContext* ir_context, TransformationContext* transformation_context) const override; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_scalar.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_scalar.cpp index e13d08f7a..98bfbf046 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_scalar.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_scalar.cpp @@ -24,9 +24,11 @@ TransformationAddConstantScalar::TransformationAddConstantScalar( : message_(message) {} TransformationAddConstantScalar::TransformationAddConstantScalar( - uint32_t fresh_id, uint32_t type_id, std::vector words) { + uint32_t fresh_id, uint32_t type_id, const std::vector& words, + bool is_irrelevant) { message_.set_fresh_id(fresh_id); message_.set_type_id(type_id); + message_.set_is_irrelevant(is_irrelevant); for (auto word : words) { message_.add_word(word); } @@ -60,7 +62,8 @@ bool TransformationAddConstantScalar::IsApplicable( } void TransformationAddConstantScalar::Apply( - opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { opt::Instruction::OperandList operand_list; for (auto word : message_.word()) { operand_list.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {word}}); @@ -75,6 +78,11 @@ void TransformationAddConstantScalar::Apply( // validity of existing analyses. ir_context->InvalidateAnalysesExceptFor( opt::IRContext::Analysis::kAnalysisNone); + + if (message_.is_irrelevant()) { + transformation_context->GetFactManager()->AddFactIdIsIrrelevant( + message_.fresh_id()); + } } protobufs::Transformation TransformationAddConstantScalar::ToMessage() const { diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_scalar.h b/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_scalar.h index e0ed39fac..06a77fc7d 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_scalar.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_constant_scalar.h @@ -31,7 +31,8 @@ class TransformationAddConstantScalar : public Transformation { const protobufs::TransformationAddConstantScalar& message); TransformationAddConstantScalar(uint32_t fresh_id, uint32_t type_id, - std::vector words); + const std::vector& words, + bool is_irrelevant); // - |message_.fresh_id| must not be used by the module // - |message_.type_id| must be the id of a floating-point or integer type @@ -42,6 +43,7 @@ class TransformationAddConstantScalar : public Transformation { const TransformationContext& transformation_context) const override; // Adds a new OpConstant instruction with the given type and words. + // Creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true. void Apply(opt::IRContext* ir_context, TransformationContext* transformation_context) const override; 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 0bbda5a2b..5b06e9782 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_block.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_block.cpp @@ -32,7 +32,8 @@ TransformationAddDeadBlock::TransformationAddDeadBlock(uint32_t fresh_id, } bool TransformationAddDeadBlock::IsApplicable( - opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { // The new block's id must be fresh. if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; @@ -40,8 +41,8 @@ bool TransformationAddDeadBlock::IsApplicable( // First, we check that a constant with the same value as // |message_.condition_value| is present. - if (!fuzzerutil::MaybeGetBoolConstant(ir_context, - message_.condition_value())) { + if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + message_.condition_value(), false)) { // The required constant is not present, so the transformation cannot be // applied. return false; @@ -92,8 +93,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::MaybeGetBoolConstant(ir_context, message_.condition_value()); + auto bool_id = fuzzerutil::MaybeGetBoolConstant( + ir_context, *transformation_context, message_.condition_value(), false); // 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 695c23fe7..284174ad7 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,9 @@ 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::MaybeGetBoolConstant(ir_context, - message_.break_condition_value())) { + if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + message_.break_condition_value(), + false)) { // The required constant is not present, so the transformation cannot be // applied. return false; @@ -179,14 +180,15 @@ bool TransformationAddDeadBreak::IsApplicable( // the validator is complete with respect to checking structured control flow // rules. auto cloned_context = fuzzerutil::CloneIRContext(ir_context); - ApplyImpl(cloned_context.get()); + ApplyImpl(cloned_context.get(), transformation_context); return fuzzerutil::IsValid(cloned_context.get(), transformation_context.GetValidatorOptions()); } void TransformationAddDeadBreak::Apply( - opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - ApplyImpl(ir_context); + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + ApplyImpl(ir_context, *transformation_context); // Invalidate all analyses ir_context->InvalidateAnalysesExceptFor( opt::IRContext::Analysis::kAnalysisNone); @@ -199,11 +201,14 @@ protobufs::Transformation TransformationAddDeadBreak::ToMessage() const { } void TransformationAddDeadBreak::ApplyImpl( - spvtools::opt::IRContext* ir_context) const { + spvtools::opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis( ir_context, ir_context->cfg()->block(message_.from_block()), ir_context->cfg()->block(message_.to_block()), - message_.break_condition_value(), message_.phi_id()); + fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + message_.break_condition_value(), false), + message_.phi_id()); } } // namespace fuzz diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.h b/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.h index 0ea921041..f010b11ff 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.h @@ -75,7 +75,8 @@ class TransformationAddDeadBreak : public Transformation { // module. This is only invoked by 'IsApplicable' after certain basic // applicability checks have been made, ensuring that the invocation of this // method is legal. - void ApplyImpl(opt::IRContext* ir_context) const; + void ApplyImpl(opt::IRContext* ir_context, + const TransformationContext& transformation_context) const; protobufs::TransformationAddDeadBreak message_; }; 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 1328b1eaf..b5b7ae3fe 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,9 @@ 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::MaybeGetBoolConstant(ir_context, - message_.continue_condition_value())) { + if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + message_.continue_condition_value(), + false)) { // The required constant is not present, so the transformation cannot be // applied. return false; @@ -119,14 +120,15 @@ bool TransformationAddDeadContinue::IsApplicable( // the validator is complete with respect to checking structured control flow // rules. auto cloned_context = fuzzerutil::CloneIRContext(ir_context); - ApplyImpl(cloned_context.get()); + ApplyImpl(cloned_context.get(), transformation_context); return fuzzerutil::IsValid(cloned_context.get(), transformation_context.GetValidatorOptions()); } void TransformationAddDeadContinue::Apply( - opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - ApplyImpl(ir_context); + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + ApplyImpl(ir_context, *transformation_context); // Invalidate all analyses ir_context->InvalidateAnalysesExceptFor( opt::IRContext::Analysis::kAnalysisNone); @@ -139,7 +141,8 @@ protobufs::Transformation TransformationAddDeadContinue::ToMessage() const { } void TransformationAddDeadContinue::ApplyImpl( - spvtools::opt::IRContext* ir_context) const { + spvtools::opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { auto bb_from = ir_context->cfg()->block(message_.from_block()); auto continue_block = bb_from->IsLoopHeader() @@ -149,7 +152,10 @@ void TransformationAddDeadContinue::ApplyImpl( assert(continue_block && "message_.from_block must be in a loop."); fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis( ir_context, bb_from, ir_context->cfg()->block(continue_block), - message_.continue_condition_value(), message_.phi_id()); + fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + message_.continue_condition_value(), + false), + message_.phi_id()); } } // namespace fuzz diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_continue.h b/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_continue.h index 1053c1655..b977bb20f 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_continue.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_dead_continue.h @@ -72,7 +72,8 @@ class TransformationAddDeadContinue : public Transformation { // module. This is only invoked by 'IsApplicable' after certain basic // applicability checks have been made, ensuring that the invocation of this // method is legal. - void ApplyImpl(opt::IRContext* ir_context) const; + void ApplyImpl(opt::IRContext* ir_context, + const TransformationContext& transformation_context) const; protobufs::TransformationAddDeadContinue message_; }; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.cpp index b64c7e5cc..cc32362e2 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.cpp @@ -66,7 +66,8 @@ bool TransformationAddParameter::IsApplicable( } void TransformationAddParameter::Apply( - opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { // Find the function that will be transformed auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id()); assert(function && "Can't find the function"); @@ -85,6 +86,11 @@ void TransformationAddParameter::Apply( // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403): // Add an PointeeValueIsIrrelevant fact if the parameter is a pointer. + // Mark new parameter as irrelevant so that we can replace its use with some + // other id. + transformation_context->GetFactManager()->AddFactIdIsIrrelevant( + message_.parameter_fresh_id()); + // Fix all OpFunctionCall instructions. ir_context->get_def_use_mgr()->ForEachUser( &function->DefInst(), [this](opt::Instruction* call) { @@ -93,9 +99,6 @@ void TransformationAddParameter::Apply( return; } - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): - // it would be good to mark this usage of |id| as irrelevant, so that - // we can perform some interesting transformations on it later. call->AddOperand({SPV_OPERAND_TYPE_ID, {message_.initializer_id()}}); }); diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.cpp new file mode 100644 index 000000000..effa71db2 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.cpp @@ -0,0 +1,146 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_relaxed_decoration.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration( + const spvtools::fuzz::protobufs::TransformationAddRelaxedDecoration& + message) + : message_(message) {} + +TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration( + uint32_t result_id) { + message_.set_result_id(result_id); +} + +bool TransformationAddRelaxedDecoration::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // |message_.result_id| must be the id of an instruction. + auto instr = ir_context->get_def_use_mgr()->GetDef(message_.result_id()); + if (!instr) { + return false; + } + opt::BasicBlock* cur_block = ir_context->get_instr_block(instr); + // The instruction must have a block. + if (cur_block == nullptr) { + return false; + } + // |cur_block| must be a dead block. + if (!(transformation_context.GetFactManager()->BlockIsDead( + cur_block->id()))) { + return false; + } + // The instruction must be numeric. + return IsNumeric(instr->opcode()); +} + +void TransformationAddRelaxedDecoration::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Add a RelaxedPrecision decoration targeting |message_.result_id|. + ir_context->get_decoration_mgr()->AddDecoration( + message_.result_id(), SpvDecorationRelaxedPrecision); +} + +protobufs::Transformation TransformationAddRelaxedDecoration::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_add_relaxed_decoration() = message_; + return result; +} + +bool TransformationAddRelaxedDecoration::IsNumeric(uint32_t opcode) { + switch (opcode) { + case SpvOpConvertFToU: + case SpvOpConvertFToS: + case SpvOpConvertSToF: + case SpvOpConvertUToF: + case SpvOpUConvert: + case SpvOpSConvert: + case SpvOpFConvert: + case SpvOpConvertPtrToU: + case SpvOpSatConvertSToU: + case SpvOpSatConvertUToS: + case SpvOpVectorExtractDynamic: + case SpvOpVectorInsertDynamic: + case SpvOpVectorShuffle: + case SpvOpTranspose: + case SpvOpSNegate: + case SpvOpFNegate: + case SpvOpIAdd: + case SpvOpFAdd: + case SpvOpISub: + case SpvOpFSub: + case SpvOpIMul: + case SpvOpFMul: + case SpvOpUDiv: + case SpvOpSDiv: + case SpvOpFDiv: + case SpvOpUMod: + case SpvOpSRem: + case SpvOpSMod: + case SpvOpFRem: + case SpvOpFMod: + case SpvOpVectorTimesScalar: + case SpvOpMatrixTimesScalar: + case SpvOpVectorTimesMatrix: + case SpvOpMatrixTimesVector: + case SpvOpMatrixTimesMatrix: + case SpvOpOuterProduct: + case SpvOpDot: + case SpvOpIAddCarry: + case SpvOpISubBorrow: + case SpvOpUMulExtended: + case SpvOpSMulExtended: + case SpvOpShiftRightLogical: + case SpvOpShiftRightArithmetic: + case SpvOpShiftLeftLogical: + case SpvOpBitwiseOr: + case SpvOpBitwiseXor: + case SpvOpBitwiseAnd: + case SpvOpNot: + case SpvOpBitFieldInsert: + case SpvOpBitFieldSExtract: + case SpvOpBitFieldUExtract: + case SpvOpBitReverse: + case SpvOpBitCount: + case SpvOpAtomicLoad: + case SpvOpAtomicStore: + case SpvOpAtomicExchange: + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + return true; + default: + return false; + } +} + +} // namespace fuzz +} // namespace spvtools \ No newline at end of file diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.h b/3rdparty/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.h new file mode 100644 index 000000000..30c1abfa3 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.h @@ -0,0 +1,62 @@ +// 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 SPIRV_TOOLS_TRANSFORMATION_ADD_RELAXED_DECORATION_H +#define SPIRV_TOOLS_TRANSFORMATION_ADD_RELAXED_DECORATION_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 TransformationAddRelaxedDecoration : public Transformation { + public: + explicit TransformationAddRelaxedDecoration( + const protobufs::TransformationAddRelaxedDecoration& message); + + explicit TransformationAddRelaxedDecoration(uint32_t fresh_id); + + // - |message_.result_id| must be the result id of an instruction, which is + // located in a dead block and Relaxed decoration can be applied. + // - It does not matter whether this instruction is already annotated with the + // Relaxed decoration. + bool IsApplicable( + + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a decoration of the form: + // 'OpDecoration |message_.result_id| RelaxedPrecision' + // to the module. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if and only if |opcode| is the opcode of an instruction + // that operates on 32-bit integers and 32-bit floats + // as defined by the SPIR-V specification. + static bool IsNumeric(uint32_t opcode); + + private: + protobufs::TransformationAddRelaxedDecoration message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SPIRV_TOOLS_TRANSFORMATION_ADD_RELAXED_DECORATION_H diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.cpp index 749d7872d..6a93e61b9 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.cpp @@ -38,7 +38,8 @@ TransformationAddSynonym::TransformationAddSynonym( } bool TransformationAddSynonym::IsApplicable( - opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { assert(protobufs::TransformationAddSynonym::SynonymType_IsValid( message_.synonym_type()) && "Synonym type is invalid"); @@ -55,7 +56,8 @@ bool TransformationAddSynonym::IsApplicable( } // Check that we can apply |synonym_type| to |result_id|. - if (!IsInstructionValid(ir_context, synonym, message_.synonym_type())) { + if (!IsInstructionValid(ir_context, transformation_context, synonym, + message_.synonym_type())) { return false; } @@ -76,7 +78,7 @@ bool TransformationAddSynonym::IsApplicable( // A constant instruction must be present in the module if required. if (IsAdditionalConstantRequired(message_.synonym_type()) && - MaybeGetConstantId(ir_context) == 0) { + MaybeGetConstantId(ir_context, transformation_context) == 0) { return false; } @@ -90,7 +92,8 @@ void TransformationAddSynonym::Apply( TransformationContext* transformation_context) const { // Add a synonymous instruction. FindInstruction(message_.insert_before(), ir_context) - ->InsertBefore(MakeSynonymousInstruction(ir_context)); + ->InsertBefore( + MakeSynonymousInstruction(ir_context, *transformation_context)); fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id()); @@ -122,7 +125,8 @@ protobufs::Transformation TransformationAddSynonym::ToMessage() const { } bool TransformationAddSynonym::IsInstructionValid( - opt::IRContext* ir_context, opt::Instruction* inst, + opt::IRContext* ir_context, + const TransformationContext& transformation_context, opt::Instruction* inst, protobufs::TransformationAddSynonym::SynonymType synonym_type) { // Instruction must have a result id, type id. We skip OpUndef and // OpConstantNull. @@ -131,8 +135,10 @@ bool TransformationAddSynonym::IsInstructionValid( return false; } - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): - // We can't create a synonym of an irrelevant id. + if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, inst)) { + return false; + } + switch (synonym_type) { case protobufs::TransformationAddSynonym::ADD_ZERO: case protobufs::TransformationAddSynonym::SUB_ZERO: @@ -149,7 +155,9 @@ bool TransformationAddSynonym::IsInstructionValid( return type->AsInteger() || type->AsFloat(); } case protobufs::TransformationAddSynonym::COPY_OBJECT: - return fuzzerutil::CanMakeSynonymOf(ir_context, inst); + // All checks for OpCopyObject are handled by + // fuzzerutil::CanMakeSynonymOf. + return true; case protobufs::TransformationAddSynonym::LOGICAL_AND: case protobufs::TransformationAddSynonym::LOGICAL_OR: { // The instruction must be either a scalar or a vector of booleans. @@ -166,7 +174,8 @@ bool TransformationAddSynonym::IsInstructionValid( std::unique_ptr TransformationAddSynonym::MakeSynonymousInstruction( - opt::IRContext* ir_context) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { auto synonym_type_id = fuzzerutil::GetTypeId(ir_context, message_.result_id()); assert(synonym_type_id && "Synonym has invalid type id"); @@ -206,7 +215,8 @@ TransformationAddSynonym::MakeSynonymousInstruction( ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(), opt::Instruction::OperandList{ {SPV_OPERAND_TYPE_ID, {message_.result_id()}}, - {SPV_OPERAND_TYPE_ID, {MaybeGetConstantId(ir_context)}}}); + {SPV_OPERAND_TYPE_ID, + {MaybeGetConstantId(ir_context, transformation_context)}}}); } case protobufs::TransformationAddSynonym::COPY_OBJECT: return MakeUnique( @@ -224,7 +234,8 @@ TransformationAddSynonym::MakeSynonymousInstruction( ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(), opt::Instruction::OperandList{ {SPV_OPERAND_TYPE_ID, {message_.result_id()}}, - {SPV_OPERAND_TYPE_ID, {MaybeGetConstantId(ir_context)}}}); + {SPV_OPERAND_TYPE_ID, + {MaybeGetConstantId(ir_context, transformation_context)}}}); } default: assert(false && "Unhandled synonym type"); @@ -233,7 +244,8 @@ TransformationAddSynonym::MakeSynonymousInstruction( } uint32_t TransformationAddSynonym::MaybeGetConstantId( - opt::IRContext* ir_context) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { assert(IsAdditionalConstantRequired(message_.synonym_type()) && "Synonym type doesn't require an additional constant"); @@ -245,7 +257,8 @@ uint32_t TransformationAddSynonym::MaybeGetConstantId( case protobufs::TransformationAddSynonym::ADD_ZERO: case protobufs::TransformationAddSynonym::SUB_ZERO: case protobufs::TransformationAddSynonym::LOGICAL_OR: - return fuzzerutil::MaybeGetZeroConstant(ir_context, synonym_type_id); + return fuzzerutil::MaybeGetZeroConstant( + ir_context, transformation_context, synonym_type_id, false); case protobufs::TransformationAddSynonym::MUL_ONE: case protobufs::TransformationAddSynonym::LOGICAL_AND: { auto synonym_type = ir_context->get_type_mgr()->GetType(synonym_type_id); @@ -259,19 +272,20 @@ uint32_t TransformationAddSynonym::MaybeGetConstantId( auto one_word = vector->element_type()->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u; if (auto scalar_one_id = fuzzerutil::MaybeGetScalarConstant( - ir_context, {one_word}, element_type_id)) { + ir_context, transformation_context, {one_word}, element_type_id, + false)) { return fuzzerutil::MaybeGetCompositeConstant( - ir_context, + ir_context, transformation_context, std::vector(vector->element_count(), scalar_one_id), - synonym_type_id); + synonym_type_id, false); } return 0; } else { return fuzzerutil::MaybeGetScalarConstant( - ir_context, + ir_context, transformation_context, {synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u}, - synonym_type_id); + synonym_type_id, false); } } default: diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.h b/3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.h index 85465661f..7705415a7 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.h @@ -35,6 +35,7 @@ class TransformationAddSynonym : public Transformation { const protobufs::InstructionDescriptor& insert_before); // - |result_id| must be a valid result id of some instruction in the module. + // - |result_id| may not be an irrelevant id. // - |synonym_type| is a type of the synonymous instruction that will be // created. // - |synonym_fresh_id| is a fresh id. @@ -57,7 +58,9 @@ class TransformationAddSynonym : public Transformation { // Returns true if we can create a synonym of |inst| according to the // |synonym_type|. static bool IsInstructionValid( - opt::IRContext* ir_context, opt::Instruction* inst, + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + opt::Instruction* inst, protobufs::TransformationAddSynonym::SynonymType synonym_type); // Returns true if |synonym_type| requires an additional constant instruction @@ -68,14 +71,17 @@ class TransformationAddSynonym : public Transformation { private: // Returns a new instruction which is synonymous to |message_.result_id|. std::unique_ptr MakeSynonymousInstruction( - opt::IRContext* ir_context) const; + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const; // Returns a result id of a constant instruction that is required to be // present in some synonym types (e.g. returns a result id of a zero constant // for ADD_ZERO synonym type). Returns 0 if no such instruction is present in // the module. This method should only be called when // IsAdditionalConstantRequired returns true. - uint32_t MaybeGetConstantId(opt::IRContext* ir_context) const; + uint32_t MaybeGetConstantId( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const; protobufs::TransformationAddSynonym message_; }; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_composite_construct.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_composite_construct.cpp index cd4f22f14..15af53ed7 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_composite_construct.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_composite_construct.cpp @@ -40,7 +40,8 @@ TransformationCompositeConstruct::TransformationCompositeConstruct( } bool TransformationCompositeConstruct::IsApplicable( - opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { // We require the id for the composite constructor to be unused. return false; @@ -87,7 +88,20 @@ bool TransformationCompositeConstruct::IsApplicable( // Now check whether every component being used to initialize the composite is // available at the desired program point. - for (auto& component : message_.component()) { + for (auto component : message_.component()) { + auto* inst = ir_context->get_def_use_mgr()->GetDef(component); + if (!inst) { + return false; + } + + // We should be able to create a synonym of |component| if it's not + // irrelevant. + if (!transformation_context.GetFactManager()->IdIsIrrelevant(component) && + !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, + inst)) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, component)) { return false; @@ -144,17 +158,23 @@ void TransformationCompositeConstruct::Apply( for (uint32_t subvector_index = 0; subvector_index < component_type->AsVector()->element_count(); subvector_index++) { - transformation_context->GetFactManager()->AddFactDataSynonym( - MakeDataDescriptor(component, {subvector_index}), - MakeDataDescriptor(message_.fresh_id(), {index}), ir_context); + if (!transformation_context->GetFactManager()->IdIsIrrelevant( + component)) { + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(component, {subvector_index}), + MakeDataDescriptor(message_.fresh_id(), {index}), ir_context); + } index++; } } else { // The other cases are simple: the component is made directly synonymous // with the element of the composite being constructed. - transformation_context->GetFactManager()->AddFactDataSynonym( - MakeDataDescriptor(component, {}), - MakeDataDescriptor(message_.fresh_id(), {index}), ir_context); + if (!transformation_context->GetFactManager()->IdIsIrrelevant( + component)) { + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(component, {}), + MakeDataDescriptor(message_.fresh_id(), {index}), ir_context); + } index++; } } diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_composite_extract.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_composite_extract.cpp index 3dc3953d7..9f4d55462 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_composite_extract.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_composite_extract.cpp @@ -40,7 +40,8 @@ TransformationCompositeExtract::TransformationCompositeExtract( } bool TransformationCompositeExtract::IsApplicable( - opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } @@ -54,6 +55,14 @@ bool TransformationCompositeExtract::IsApplicable( if (!composite_instruction) { return false; } + if (!transformation_context.GetFactManager()->IdIsIrrelevant( + message_.composite_id()) && + !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, + composite_instruction)) { + // |composite_id| will participate in DataSynonym facts. Thus, it can't be + // an irrelevant id. + return false; + } if (auto block = ir_context->get_instr_block(composite_instruction)) { if (composite_instruction == instruction_to_insert_before || !ir_context->GetDominatorAnalysis(block->GetParent()) @@ -105,17 +114,20 @@ void TransformationCompositeExtract::Apply( // Add the fact that the id storing the extracted element is synonymous with // the index into the structure. - std::vector indices; - for (auto an_index : message_.index()) { - indices.push_back(an_index); + if (!transformation_context->GetFactManager()->IdIsIrrelevant( + message_.composite_id())) { + std::vector indices; + for (auto an_index : message_.index()) { + indices.push_back(an_index); + } + protobufs::DataDescriptor data_descriptor_for_extracted_element = + MakeDataDescriptor(message_.composite_id(), std::move(indices)); + protobufs::DataDescriptor data_descriptor_for_result_id = + MakeDataDescriptor(message_.fresh_id(), {}); + transformation_context->GetFactManager()->AddFactDataSynonym( + data_descriptor_for_extracted_element, data_descriptor_for_result_id, + ir_context); } - protobufs::DataDescriptor data_descriptor_for_extracted_element = - MakeDataDescriptor(message_.composite_id(), std::move(indices)); - protobufs::DataDescriptor data_descriptor_for_result_id = - MakeDataDescriptor(message_.fresh_id(), {}); - transformation_context->GetFactManager()->AddFactDataSynonym( - data_descriptor_for_extracted_element, data_descriptor_for_result_id, - ir_context); } protobufs::Transformation TransformationCompositeExtract::ToMessage() const { diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_composite_extract.h b/3rdparty/spirv-tools/source/fuzz/transformation_composite_extract.h index 8f52d2269..34df82348 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_composite_extract.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_composite_extract.h @@ -48,7 +48,8 @@ class TransformationCompositeExtract : public Transformation { // Adds an OpCompositeConstruct instruction before the instruction identified // by |message_.instruction_to_insert_before|, that extracts from // |message_.composite_id| via indices |message_.index| into - // |message_.fresh_id|. Generates a data synonym fact relating + // |message_.fresh_id|. If |composite_id| is not an irrelevant id, + // generates a data synonym fact relating // |message_.fresh_id| to the extracted element. void Apply(opt::IRContext* ir_context, TransformationContext* transformation_context) const override; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.cpp index 5f2c7f78a..e27cd2970 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.cpp @@ -37,7 +37,8 @@ TransformationEquationInstruction::TransformationEquationInstruction( } bool TransformationEquationInstruction::IsApplicable( - opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { // The result id must be fresh. if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; @@ -59,6 +60,9 @@ bool TransformationEquationInstruction::IsApplicable( if (inst->opcode() == SpvOpUndef) { return false; } + if (transformation_context.GetFactManager()->IdIsIrrelevant(id)) { + return false; + } if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, id)) { return false; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp index 5f0660535..c4de743d5 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp @@ -87,38 +87,6 @@ void TransformationPermuteFunctionParameters::Apply( auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id()); assert(function && "Can't find the function"); - auto* old_function_type_inst = - fuzzerutil::GetFunctionType(ir_context, function); - assert(old_function_type_inst && "Function must have a valid type"); - - // Change function's type - if (ir_context->get_def_use_mgr()->NumUsers(old_function_type_inst) == 1) { - // If only the current function uses |old_function_type_inst| - change it - // in-place. - opt::Instruction::OperandList permuted_operands = { - std::move(old_function_type_inst->GetInOperand(0))}; - for (auto index : message_.permutation()) { - // +1 since the first operand to OpTypeFunction is a return type. - permuted_operands.push_back( - std::move(old_function_type_inst->GetInOperand(index + 1))); - } - - old_function_type_inst->SetInOperands(std::move(permuted_operands)); - } else { - // Either use an existing type or create a new one. - std::vector type_ids = { - old_function_type_inst->GetSingleWordInOperand(0)}; - for (auto index : message_.permutation()) { - // +1 since the first operand to OpTypeFunction is a return type. - type_ids.push_back( - old_function_type_inst->GetSingleWordInOperand(index + 1)); - } - - function->DefInst().SetInOperand( - 1, {fuzzerutil::FindOrCreateFunctionType( - ir_context, message_.function_type_fresh_id(), type_ids)}); - } - // Adjust OpFunctionParameter instructions // Collect ids and types from OpFunctionParameter instructions @@ -146,24 +114,40 @@ void TransformationPermuteFunctionParameters::Apply( }); // Fix all OpFunctionCall instructions - ir_context->get_def_use_mgr()->ForEachUser( - &function->DefInst(), [this](opt::Instruction* call) { - if (call->opcode() != SpvOpFunctionCall || - call->GetSingleWordInOperand(0) != message_.function_id()) { - return; - } + for (auto* call : fuzzerutil::GetCallers(ir_context, function->result_id())) { + opt::Instruction::OperandList call_operands = { + call->GetInOperand(0) // Function id + }; - opt::Instruction::OperandList call_operands = { - call->GetInOperand(0) // Function id - }; + for (auto index : message_.permutation()) { + // Take function id into account + call_operands.push_back(call->GetInOperand(index + 1)); + } - for (auto index : message_.permutation()) { - // Take function id into account - call_operands.push_back(call->GetInOperand(index + 1)); - } + call->SetInOperands(std::move(call_operands)); + } - call->SetInOperands(std::move(call_operands)); - }); + // Update function type. + { + // We use a separate scope here since |old_function_type_inst| might become + // a dangling pointer after the call to the fuzzerutil::UpdateFunctionType. + + auto* old_function_type_inst = + fuzzerutil::GetFunctionType(ir_context, function); + assert(old_function_type_inst && "Function must have a valid type"); + + std::vector parameter_type_ids; + for (auto index : message_.permutation()) { + // +1 since the first operand to OpTypeFunction is a return type. + parameter_type_ids.push_back( + old_function_type_inst->GetSingleWordInOperand(index + 1)); + } + + // Change function's type. + fuzzerutil::UpdateFunctionType( + ir_context, function->result_id(), message_.function_type_fresh_id(), + old_function_type_inst->GetSingleWordInOperand(0), parameter_type_ids); + } // Make sure our changes are analyzed ir_context->InvalidateAnalysesExceptFor( 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 a0bf79740..647bfa065 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 @@ -38,7 +38,8 @@ TransformationPushIdThroughVariable::TransformationPushIdThroughVariable( } bool TransformationPushIdThroughVariable::IsApplicable( - opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { // |message_.value_synonym_id| and |message_.variable_id| must be fresh. if (!fuzzerutil::IsFreshId(ir_context, message_.value_synonym_id()) || !fuzzerutil::IsFreshId(ir_context, message_.variable_id())) { @@ -73,6 +74,14 @@ bool TransformationPushIdThroughVariable::IsApplicable( return false; } + // We should be able to create a synonym of |value_id| if it's not irrelevant. + if (!transformation_context.GetFactManager()->IdIsIrrelevant( + message_.value_id()) && + !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, + value_instruction)) { + return false; + } + // A pointer type instruction pointing to the value type must be defined. auto pointer_type_id = fuzzerutil::MaybeGetPointerType( ir_context, value_instruction->type_id(), @@ -144,11 +153,14 @@ void TransformationPushIdThroughVariable::Apply( ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); - // Adds the fact that |message_.value_synonym_id| - // and |message_.value_id| are synonymous. - transformation_context->GetFactManager()->AddFactDataSynonym( - MakeDataDescriptor(message_.value_synonym_id(), {}), - MakeDataDescriptor(message_.value_id(), {}), ir_context); + if (!transformation_context->GetFactManager()->IdIsIrrelevant( + message_.value_id())) { + // Adds the fact that |message_.value_synonym_id| + // and |message_.value_id| are synonymous. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.value_synonym_id(), {}), + MakeDataDescriptor(message_.value_id(), {}), ir_context); + } } protobufs::Transformation TransformationPushIdThroughVariable::ToMessage() diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.h b/3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.h index 24d3c2b26..f49db3170 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.h @@ -51,8 +51,8 @@ class TransformationPushIdThroughVariable : public Transformation { const TransformationContext& transformation_context) const override; // Stores |value_id| to |variable_id|, loads |variable_id| to - // |value_synonym_id| and adds the fact that |value_synonym_id| and |value_id| - // are synonymous. + // |value_synonym_id|. Adds the fact that |value_synonym_id| and |value_id| + // are synonymous if |value_id| is not irrelevant. void Apply(opt::IRContext* ir_context, TransformationContext* transformation_context) const override; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp index 422e57e89..b1568bffb 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp @@ -18,12 +18,6 @@ namespace spvtools { namespace fuzz { -namespace { -bool IsScalarZeroConstant(const opt::analysis::Constant* constant) { - return constant->AsScalarConstant() && constant->IsZero(); -} -} // namespace - TransformationRecordSynonymousConstants:: TransformationRecordSynonymousConstants( const protobufs::TransformationRecordSynonymousConstants& message) @@ -38,62 +32,30 @@ TransformationRecordSynonymousConstants:: bool TransformationRecordSynonymousConstants::IsApplicable( opt::IRContext* ir_context, - const TransformationContext& /* unused */) const { + const TransformationContext& transformation_context) 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) { + if (transformation_context.GetFactManager()->IdIsIrrelevant( + message_.constant1_id()) || + transformation_context.GetFactManager()->IdIsIrrelevant( + message_.constant2_id())) { 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()); + return AreEquivalentConstants(ir_context, message_.constant1_id(), + message_.constant2_id()); } 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); + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.constant1_id(), {}), + MakeDataDescriptor(message_.constant2_id(), {}), ir_context); } protobufs::Transformation TransformationRecordSynonymousConstants::ToMessage() @@ -103,5 +65,62 @@ protobufs::Transformation TransformationRecordSynonymousConstants::ToMessage() return result; } +bool TransformationRecordSynonymousConstants::AreEquivalentConstants( + opt::IRContext* ir_context, uint32_t constant_id1, uint32_t constant_id2) { + const auto* def_1 = ir_context->get_def_use_mgr()->GetDef(constant_id1); + const auto* def_2 = ir_context->get_def_use_mgr()->GetDef(constant_id2); + + // Check that the definitions exist + if (!def_1 || !def_2) { + // We don't use an assertion since otherwise the shrinker fails. + return false; + } + + // The type ids must be the same + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3536): Somehow + // relax this for integers (so that unsigned integer and signed integer are + // considered the same type) + if (def_1->type_id() != def_2->type_id()) { + return false; + } + + auto constant1 = ir_context->get_constant_mgr()->GetConstantFromInst(def_1); + auto constant2 = ir_context->get_constant_mgr()->GetConstantFromInst(def_2); + + assert(constant1 && constant2 && "The ids must refer to constants."); + + // If either constant is null, the other is equivalent iff it is zero-like + if (constant1->AsNullConstant()) { + return constant2->IsZero(); + } + + if (constant2->AsNullConstant()) { + return constant1->IsZero(); + } + + // If the constants are scalar, they are equal iff their words are the same + if (auto scalar1 = constant1->AsScalarConstant()) { + return scalar1->words() == constant2->AsScalarConstant()->words(); + } + + // The only remaining possibility is that the constants are composite + assert(constant1->AsCompositeConstant() && + "Equivalence of constants can only be checked with scalar, composite " + "or null constants."); + + // Since the types match, we already know that the number of components is + // the same. We check that the input operands of the definitions are all + // constants and that they are pairwise equivalent. + for (uint32_t i = 0; i < def_1->NumInOperands(); i++) { + if (!AreEquivalentConstants(ir_context, def_1->GetSingleWordInOperand(i), + def_2->GetSingleWordInOperand(i))) { + return false; + } + } + + // If we get here, all the components are equivalent + return true; +} + } // 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 index 6a1a607dd..b28eeb3b7 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h @@ -33,11 +33,15 @@ class TransformationRecordSynonymousConstants : public Transformation { // of constants // - |message_.constant_id| and |message_.synonym_id| refer to constants // that are equal or equivalent. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3536): Signed and + // unsigned integers are currently considered non-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). + // Constants are equivalent: + // - if both of them represent zero-like values of the same type + // - if they are composite constants with the same type and their + // components are pairwise equivalent. + // - |constant1_id| and |constant2_id| may not be irrelevant. bool IsApplicable( opt::IRContext* ir_context, const TransformationContext& transformation_context) const override; @@ -51,6 +55,13 @@ class TransformationRecordSynonymousConstants : public Transformation { private: protobufs::TransformationRecordSynonymousConstants message_; + + // Returns true if the two given constants are equivalent + // (the description of IsApplicable specifies the conditions they must satisfy + // to be considered equivalent) + static bool AreEquivalentConstants(opt::IRContext* ir_context, + uint32_t constant_id1, + uint32_t constant_id2); }; } // namespace fuzz diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp new file mode 100644 index 000000000..bf6996ad3 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceCopyMemoryWithLoadStore:: + TransformationReplaceCopyMemoryWithLoadStore( + const spvtools::fuzz::protobufs:: + TransformationReplaceCopyMemoryWithLoadStore& message) + : message_(message) {} + +TransformationReplaceCopyMemoryWithLoadStore:: + TransformationReplaceCopyMemoryWithLoadStore( + uint32_t fresh_id, const protobufs::InstructionDescriptor& + copy_memory_instruction_descriptor) { + message_.set_fresh_id(fresh_id); + *message_.mutable_copy_memory_instruction_descriptor() = + copy_memory_instruction_descriptor; +} + +bool TransformationReplaceCopyMemoryWithLoadStore::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.fresh_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // The instruction to be replaced must be defined and have opcode + // OpCopyMemory. + auto copy_memory_instruction = FindInstruction( + message_.copy_memory_instruction_descriptor(), ir_context); + if (!copy_memory_instruction || + copy_memory_instruction->opcode() != SpvOpCopyMemory) { + return false; + } + return true; +} + +void TransformationReplaceCopyMemoryWithLoadStore::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto copy_memory_instruction = FindInstruction( + message_.copy_memory_instruction_descriptor(), ir_context); + // |copy_memory_instruction| must be defined. + assert(copy_memory_instruction && + copy_memory_instruction->opcode() == SpvOpCopyMemory && + "The required OpCopyMemory instruction must be defined."); + + // Coherence check: Both operands must be pointers. + + // Get types of ids used as a source and target of |copy_memory_instruction|. + auto target = ir_context->get_def_use_mgr()->GetDef( + copy_memory_instruction->GetSingleWordInOperand(0)); + auto source = ir_context->get_def_use_mgr()->GetDef( + copy_memory_instruction->GetSingleWordInOperand(1)); + auto target_type_opcode = + ir_context->get_def_use_mgr()->GetDef(target->type_id())->opcode(); + auto source_type_opcode = + ir_context->get_def_use_mgr()->GetDef(source->type_id())->opcode(); + + // Keep release-mode compilers happy. (No unused variables.) + (void)target; + (void)source; + (void)target_type_opcode; + (void)source_type_opcode; + + assert(target_type_opcode == SpvOpTypePointer && + source_type_opcode == SpvOpTypePointer && + "Operands must be of type OpTypePointer"); + + // Coherence check: |source| and |target| must point to the same type. + uint32_t target_pointee_type = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, target->type_id()); + uint32_t source_pointee_type = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, source->type_id()); + + // Keep release-mode compilers happy. (No unused variables.) + (void)target_pointee_type; + (void)source_pointee_type; + + assert(target_pointee_type == source_pointee_type && + "Operands must have the same type to which they point to."); + + // First, insert the OpStore instruction before the OpCopyMemory instruction + // and then insert the OpLoad instruction before the OpStore instruction. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + FindInstruction(message_.copy_memory_instruction_descriptor(), ir_context) + ->InsertBefore(MakeUnique( + ir_context, SpvOpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {target->result_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}}))) + ->InsertBefore(MakeUnique( + ir_context, SpvOpLoad, target_pointee_type, message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {source->result_id()}}}))); + + // Remove the OpCopyMemory instruction. + ir_context->KillInst(copy_memory_instruction); + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation +TransformationReplaceCopyMemoryWithLoadStore::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_copy_memory_with_load_store() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h b/3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h new file mode 100644 index 000000000..70120f8df --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h @@ -0,0 +1,57 @@ +// 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 SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H +#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_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 TransformationReplaceCopyMemoryWithLoadStore : public Transformation { + public: + explicit TransformationReplaceCopyMemoryWithLoadStore( + const protobufs::TransformationReplaceCopyMemoryWithLoadStore& message); + + TransformationReplaceCopyMemoryWithLoadStore( + uint32_t fresh_id, const protobufs::InstructionDescriptor& + copy_memory_instruction_descriptor); + + // - |message_.fresh_id| must be fresh. + // - |message_.copy_memory_instruction_descriptor| must refer to an + // OpCopyMemory instruction. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces instruction OpCopyMemory with loading the source variable to an + // intermediate value and storing this value into the target variable of the + // original OpCopyMemory instruction. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationReplaceCopyMemoryWithLoadStore message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp new file mode 100644 index 000000000..05e8cdaec --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp @@ -0,0 +1,155 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_copy_object_with_store_load.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceCopyObjectWithStoreLoad:: + TransformationReplaceCopyObjectWithStoreLoad( + const spvtools::fuzz::protobufs:: + TransformationReplaceCopyObjectWithStoreLoad& message) + : message_(message) {} + +TransformationReplaceCopyObjectWithStoreLoad:: + TransformationReplaceCopyObjectWithStoreLoad( + uint32_t copy_object_result_id, uint32_t fresh_variable_id, + uint32_t variable_storage_class, uint32_t variable_initializer_id) { + message_.set_copy_object_result_id(copy_object_result_id); + message_.set_fresh_variable_id(fresh_variable_id); + message_.set_variable_storage_class(variable_storage_class); + message_.set_variable_initializer_id(variable_initializer_id); +} + +bool TransformationReplaceCopyObjectWithStoreLoad::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.fresh_variable_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_variable_id())) { + return false; + } + auto copy_object_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.copy_object_result_id()); + + // This must be a defined OpCopyObject instruction. + if (!copy_object_instruction || + copy_object_instruction->opcode() != SpvOpCopyObject) { + return false; + } + + // The opcode of the type_id instruction cannot be a OpTypePointer, + // because we cannot define a pointer to pointer. + if (ir_context->get_def_use_mgr() + ->GetDef(copy_object_instruction->type_id()) + ->opcode() == SpvOpTypePointer) { + return false; + } + + // It must be valid to insert the OpStore and OpLoad instruction before it. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, + copy_object_instruction) || + !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, + copy_object_instruction)) { + return false; + } + + // A pointer type instruction pointing to the value type must be defined. + auto pointer_type_id = fuzzerutil::MaybeGetPointerType( + ir_context, copy_object_instruction->type_id(), + static_cast(message_.variable_storage_class())); + if (!pointer_type_id) { + return false; + } + + // Check that initializer is valid. + const auto* constant_inst = + ir_context->get_def_use_mgr()->GetDef(message_.variable_initializer_id()); + if (!constant_inst || !spvOpcodeIsConstant(constant_inst->opcode()) || + copy_object_instruction->type_id() != constant_inst->type_id()) { + return false; + } + // |message_.variable_storage_class| must be Private or Function. + return message_.variable_storage_class() == SpvStorageClassPrivate || + message_.variable_storage_class() == SpvStorageClassFunction; +} + +void TransformationReplaceCopyObjectWithStoreLoad::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto copy_object_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.copy_object_result_id()); + // |copy_object_instruction| must be defined. + assert(copy_object_instruction && + copy_object_instruction->opcode() == SpvOpCopyObject && + "The required OpCopyObject instruction must be defined."); + // Get id used as a source by the OpCopyObject instruction. + uint32_t src_operand = copy_object_instruction->GetSingleWordOperand(2); + // A pointer type instruction pointing to the value type must be defined. + auto pointer_type_id = fuzzerutil::MaybeGetPointerType( + ir_context, copy_object_instruction->type_id(), + static_cast(message_.variable_storage_class())); + assert(pointer_type_id && "The required pointer type must be available."); + + // Adds a global or local variable (according to the storage class). + if (message_.variable_storage_class() == SpvStorageClassPrivate) { + fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_variable_id(), + pointer_type_id, SpvStorageClassPrivate, + message_.variable_initializer_id()); + } else { + auto function_id = ir_context->get_instr_block(copy_object_instruction) + ->GetParent() + ->result_id(); + fuzzerutil::AddLocalVariable(ir_context, message_.fresh_variable_id(), + pointer_type_id, function_id, + message_.variable_initializer_id()); + } + + // First, insert the OpLoad instruction before the OpCopyObject instruction + // and then insert the OpStore instruction before the OpLoad instruction. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_variable_id()); + copy_object_instruction + ->InsertBefore(MakeUnique( + ir_context, SpvOpLoad, copy_object_instruction->type_id(), + message_.copy_object_result_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}}))) + ->InsertBefore(MakeUnique( + ir_context, SpvOpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}, + {SPV_OPERAND_TYPE_ID, {src_operand}}}))); + // Remove the CopyObject instruction. + ir_context->KillInst(copy_object_instruction); + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // Adds the fact that |message_.copy_object_result_id| + // and src_operand (id used by OpCopyObject) are synonymous. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.copy_object_result_id(), {}), + MakeDataDescriptor(src_operand, {}), ir_context); +} + +protobufs::Transformation +TransformationReplaceCopyObjectWithStoreLoad::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_copy_object_with_store_load() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.h b/3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.h new file mode 100644 index 000000000..db9c74eea --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.h @@ -0,0 +1,63 @@ +// 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 SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H +#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_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 TransformationReplaceCopyObjectWithStoreLoad : public Transformation { + public: + explicit TransformationReplaceCopyObjectWithStoreLoad( + const protobufs::TransformationReplaceCopyObjectWithStoreLoad& message); + + TransformationReplaceCopyObjectWithStoreLoad( + uint32_t copy_object_result_id, uint32_t fresh_variable_id, + uint32_t variable_storage_class, uint32_t variable_initializer_id); + + // - |message_.copy_object_result_id| must be a result id of an OpCopyObject + // instruction. + // - |message_.fresh_variable_id| must be a fresh id given to variable used by + // OpStore. + // - |message_.variable_storage_class| must be either StorageClassPrivate or + // StorageClassFunction. + // - |message_.initializer_id| must be a result id of some constant in the + // module. Its type must be equal to the pointee type of the variable that + // will be created. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces instruction OpCopyObject with storing into a new variable and + // immediately loading from this variable to |result_id| of the original + // OpCopyObject instruction. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationReplaceCopyObjectWithStoreLoad message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp index 61c7f1011..f680b63bc 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp @@ -20,23 +20,6 @@ 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( @@ -53,7 +36,8 @@ TransformationReplaceParameterWithGlobal:: } bool TransformationReplaceParameterWithGlobal::IsApplicable( - opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { // Check that |parameter_id| is valid. const auto* param_inst = ir_context->get_def_use_mgr()->GetDef(message_.parameter_id()); @@ -62,8 +46,8 @@ bool TransformationReplaceParameterWithGlobal::IsApplicable( } // Check that function exists and is not an entry point. - const auto* function = - GetFunctionFromParameterId(ir_context, message_.parameter_id()); + const auto* function = fuzzerutil::GetFunctionFromParameterId( + ir_context, message_.parameter_id()); if (!function || fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) { return false; @@ -81,8 +65,8 @@ bool TransformationReplaceParameterWithGlobal::IsApplicable( } // Check that initializer for the global variable exists in the module. - if (fuzzerutil::MaybeGetZeroConstant(ir_context, param_inst->type_id()) == - 0) { + if (fuzzerutil::MaybeGetZeroConstant(ir_context, transformation_context, + param_inst->type_id(), false) == 0) { return false; } @@ -100,25 +84,31 @@ bool TransformationReplaceParameterWithGlobal::IsApplicable( } void TransformationReplaceParameterWithGlobal::Apply( - opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::IRContext* ir_context, + TransformationContext* transformation_context) 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())); + fuzzerutil::MaybeGetZeroConstant(ir_context, *transformation_context, + param_inst->type_id(), false)); - auto* function = - GetFunctionFromParameterId(ir_context, message_.parameter_id()); + // Mark the global variable's pointee as irrelevant if replaced parameter is + // irrelevant. + if (transformation_context->GetFactManager()->IdIsIrrelevant( + message_.parameter_id())) { + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.global_variable_fresh_id()); + } + + auto* function = fuzzerutil::GetFunctionFromParameterId( + ir_context, message_.parameter_id()); assert(function && "Function must exist"); // Insert an OpLoad instruction right after OpVariable instructions. @@ -152,57 +142,47 @@ void TransformationReplaceParameterWithGlobal::Apply( "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; - } + for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) { + assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, inst) && + "Can't insert OpStore right before the function call"); - 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)}}})); - // 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); - }); + // +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"); + { + // We use a separate scope here since |old_function_type| might become a + // dangling pointer after the call to the fuzzerutil::UpdateFunctionType. - // Preemptively add function's return type id. - std::vector type_ids = { - old_function_type->GetSingleWordInOperand(0)}; + auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function); + assert(old_function_type && "Function has invalid type"); - // +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)); + // +1 and -1 since the first operand is the return type id. + std::vector parameter_type_ids; + for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) { + if (i - 1 != parameter_index) { + parameter_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)}); + fuzzerutil::UpdateFunctionType( + ir_context, function->result_id(), message_.function_type_fresh_id(), + old_function_type->GetSingleWordInOperand(0), parameter_type_ids); } // Make sure our changes are analyzed diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp new file mode 100644 index 000000000..a2aaa3aa8 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp @@ -0,0 +1,306 @@ +// 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_params_with_struct.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct( + const protobufs::TransformationReplaceParamsWithStruct& message) + : message_(message) {} + +TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct( + const std::vector& parameter_id, uint32_t fresh_function_type_id, + uint32_t fresh_parameter_id, + const std::unordered_map& + caller_id_to_fresh_composite_id) { + message_.set_fresh_function_type_id(fresh_function_type_id); + message_.set_fresh_parameter_id(fresh_parameter_id); + + for (auto id : parameter_id) { + message_.add_parameter_id(id); + } + + message_.mutable_caller_id_to_fresh_composite_id()->insert( + caller_id_to_fresh_composite_id.begin(), + caller_id_to_fresh_composite_id.end()); +} + +bool TransformationReplaceParamsWithStruct::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + std::vector parameter_id(message_.parameter_id().begin(), + message_.parameter_id().end()); + + // Check that |parameter_id| is neither empty nor it has duplicates. + if (parameter_id.empty() || fuzzerutil::HasDuplicates(parameter_id)) { + return false; + } + + // All ids must correspond to valid parameters of the same function. + // The function can't be an entry-point function. + + // fuzzerutil::GetFunctionFromParameterId requires a valid id. + if (!ir_context->get_def_use_mgr()->GetDef(parameter_id[0])) { + return false; + } + + const auto* function = + fuzzerutil::GetFunctionFromParameterId(ir_context, parameter_id[0]); + if (!function || + fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) { + return false; + } + + // Compute all ids of the function's parameters. + std::unordered_set all_parameter_ids; + for (const auto* param : + fuzzerutil::GetParameters(ir_context, function->result_id())) { + all_parameter_ids.insert(param->result_id()); + } + + // Check that all elements in |parameter_id| are valid. + for (auto id : parameter_id) { + // fuzzerutil::GetFunctionFromParameterId requires a valid id. + if (!ir_context->get_def_use_mgr()->GetDef(id)) { + return false; + } + + // Check that |id| is a result id of one of the |function|'s parameters. + if (!all_parameter_ids.count(id)) { + return false; + } + + // Check that the parameter with result id |id| has supported type. + const auto* type = ir_context->get_type_mgr()->GetType( + fuzzerutil::GetTypeId(ir_context, id)); + assert(type && "Parameter has invalid type"); + if (!IsParameterTypeSupported(*type)) { + return false; + } + } + + // We already know that the function has at least |parameter_id.size()| + // parameters. + + // Check that a relevant OpTypeStruct exists in the module. + if (!MaybeGetRequiredStructType(ir_context)) { + return false; + } + + // Check that |callee_id_to_fresh_composite_id| is valid. + for (const auto* inst : + fuzzerutil::GetCallers(ir_context, function->result_id())) { + // Check that the callee is present in the map. It's ok if the map contains + // more ids that there are callees (those ids will not be used). + if (!message_.caller_id_to_fresh_composite_id().contains( + inst->result_id())) { + return false; + } + } + + // Check that all fresh ids are unique and fresh. + std::vector fresh_ids = {message_.fresh_function_type_id(), + message_.fresh_parameter_id()}; + + for (const auto& entry : message_.caller_id_to_fresh_composite_id()) { + fresh_ids.push_back(entry.second); + } + + return !fuzzerutil::HasDuplicates(fresh_ids) && + std::all_of(fresh_ids.begin(), fresh_ids.end(), + [ir_context](uint32_t id) { + return fuzzerutil::IsFreshId(ir_context, id); + }); +} + +void TransformationReplaceParamsWithStruct::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto* function = fuzzerutil::GetFunctionFromParameterId( + ir_context, message_.parameter_id(0)); + assert(function && + "All parameters' ids should've been checked in the IsApplicable"); + + // Get a type id of the OpTypeStruct used as a type id of the new parameter. + auto struct_type_id = MaybeGetRequiredStructType(ir_context); + assert(struct_type_id && + "IsApplicable should've guaranteed that this value isn't equal to 0"); + + // Add new parameter to the function. + function->AddParameter(MakeUnique( + ir_context, SpvOpFunctionParameter, struct_type_id, + message_.fresh_parameter_id(), opt::Instruction::OperandList())); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_parameter_id()); + + // Compute indices of replaced parameters. This will be used to adjust + // OpFunctionCall instructions and create OpCompositeConstruct instructions at + // every call site. + std::vector indices_of_replaced_params; + { + // We want to destroy |params| after the loop because it will contain + // dangling pointers when we remove parameters from the function. + auto params = fuzzerutil::GetParameters(ir_context, function->result_id()); + for (auto id : message_.parameter_id()) { + auto it = std::find_if(params.begin(), params.end(), + [id](const opt::Instruction* param) { + return param->result_id() == id; + }); + assert(it != params.end() && "Parameter's id is invalid"); + indices_of_replaced_params.push_back( + static_cast(it - params.begin())); + } + } + + // Update all function calls. + for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) { + // Create a list of operands for the OpCompositeConstruct instruction. + opt::Instruction::OperandList composite_components; + for (auto index : indices_of_replaced_params) { + // +1 since the first in operand to OpFunctionCall is the result id of + // the function. + composite_components.emplace_back( + std::move(inst->GetInOperand(index + 1))); + } + + // Remove arguments from the function call. We do it in a separate loop + // and in reverse order to make sure we have removed correct operands. + for (auto it = indices_of_replaced_params.rbegin(); + it != indices_of_replaced_params.rend(); ++it) { + // +1 since the first in operand to OpFunctionCall is the result id of + // the function. + inst->RemoveInOperand(*it + 1); + } + + // Insert OpCompositeConstruct before the function call. + auto fresh_composite_id = + message_.caller_id_to_fresh_composite_id().at(inst->result_id()); + inst->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeConstruct, struct_type_id, fresh_composite_id, + std::move(composite_components))); + + // Add a new operand to the OpFunctionCall instruction. + inst->AddOperand({SPV_OPERAND_TYPE_ID, {fresh_composite_id}}); + fuzzerutil::UpdateModuleIdBound(ir_context, fresh_composite_id); + } + + // Insert OpCompositeExtract instructions into the entry point block of the + // function and remove replaced parameters. + for (int i = 0; i < message_.parameter_id_size(); ++i) { + const auto* param_inst = + ir_context->get_def_use_mgr()->GetDef(message_.parameter_id(i)); + assert(param_inst && "Parameter id is invalid"); + + // Skip all OpVariable instructions. + auto iter = function->begin()->begin(); + while (iter != function->begin()->end() && + !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract, + iter)) { + ++iter; + } + + assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract, + iter) && + "Can't extract parameter's value from the structure"); + + // Insert OpCompositeExtract instructions to unpack parameters' values from + // the struct type. + iter.InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, param_inst->type_id(), + param_inst->result_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.fresh_parameter_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {static_cast(i)}}})); + + function->RemoveParameter(param_inst->result_id()); + } + + // Update function's type. + { + // We use a separate scope here since |old_function_type| might become a + // dangling pointer after the call to the fuzzerutil::UpdateFunctionType. + + auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function); + assert(old_function_type && "Function has invalid type"); + + // +1 since the first in operand to OpTypeFunction is the result type id + // of the function. + std::vector parameter_type_ids; + for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) { + if (std::find(indices_of_replaced_params.begin(), + indices_of_replaced_params.end(), + i - 1) == indices_of_replaced_params.end()) { + parameter_type_ids.push_back( + old_function_type->GetSingleWordInOperand(i)); + } + } + + parameter_type_ids.push_back(struct_type_id); + + fuzzerutil::UpdateFunctionType( + ir_context, function->result_id(), message_.fresh_function_type_id(), + old_function_type->GetSingleWordInOperand(0), parameter_type_ids); + } + + // Make sure our changes are analyzed + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationReplaceParamsWithStruct::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_replace_params_with_struct() = message_; + return result; +} + +bool TransformationReplaceParamsWithStruct::IsParameterTypeSupported( + const opt::analysis::Type& param_type) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403): + // Consider adding support for more types of parameters. + switch (param_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::kVector: + case opt::analysis::Type::kMatrix: + return true; + case opt::analysis::Type::kStruct: + return std::all_of(param_type.AsStruct()->element_types().begin(), + param_type.AsStruct()->element_types().end(), + [](const opt::analysis::Type* type) { + return IsParameterTypeSupported(*type); + }); + default: + return false; + } +} + +uint32_t TransformationReplaceParamsWithStruct::MaybeGetRequiredStructType( + opt::IRContext* ir_context) const { + std::vector component_type_ids; + for (auto id : message_.parameter_id()) { + component_type_ids.push_back(fuzzerutil::GetTypeId(ir_context, id)); + } + + return fuzzerutil::MaybeGetStructType(ir_context, component_type_ids); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h b/3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h new file mode 100644 index 000000000..0ff73405e --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h @@ -0,0 +1,83 @@ +// 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_PARAMS_WITH_STRUCT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_ + +#include + +#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 TransformationReplaceParamsWithStruct : public Transformation { + public: + explicit TransformationReplaceParamsWithStruct( + const protobufs::TransformationReplaceParamsWithStruct& message); + + TransformationReplaceParamsWithStruct( + const std::vector& parameter_id, + uint32_t fresh_function_type_id, uint32_t fresh_parameter_id, + const std::unordered_map& + caller_id_to_fresh_composite_id); + + // - Each element of |parameter_id| is a valid result id of some + // OpFunctionParameter instruction. All parameter ids must correspond to + // parameters of the same function. That function may not be an entry-point + // function. + // - Types of all parameters must be supported by this transformation (see + // IsParameterTypeSupported method). + // - |parameter_id| may not be empty or contain duplicates. + // - There must exist an OpTypeStruct instruction containing types of all + // replaced parameters. Type of the i'th component of the struct is equal + // to the type of the instruction with result id |parameter_id[i]|. + // - |caller_id_to_fresh_composite_id| should contain a key for at least every + // result id of an OpFunctionCall instruction that calls the function. + // - |fresh_function_type_id|, |fresh_parameter_id|, + // |caller_id_to_fresh_composite_id| are all fresh and unique ids. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Creates a new function parameter with result id |fresh_parameter_id|. + // Parameter's type is OpTypeStruct with each components type equal to the + // type of the replaced parameter. + // - OpCompositeConstruct with result id from |fresh_composite_id| is inserted + // before each OpFunctionCall instruction. + // - OpCompositeExtract with result id equal to the result id of the replaced + // parameter is created in the function. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if parameter's type is supported by this transformation. + static bool IsParameterTypeSupported(const opt::analysis::Type& param_type); + + private: + // Returns a result id of the OpTypeStruct instruction required by this + // transformation (see docs on the IsApplicable method to learn more). + uint32_t MaybeGetRequiredStructType(opt::IRContext* ir_context) const; + + protobufs::TransformationReplaceParamsWithStruct message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_vector_shuffle.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_vector_shuffle.cpp index ee6429266..b3bd59353 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_vector_shuffle.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_vector_shuffle.cpp @@ -39,7 +39,8 @@ TransformationVectorShuffle::TransformationVectorShuffle( } bool TransformationVectorShuffle::IsApplicable( - opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { // The fresh id must not already be in use. if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; @@ -56,12 +57,26 @@ bool TransformationVectorShuffle::IsApplicable( if (!vector1_instruction || !vector1_instruction->type_id()) { return false; } + // We should be able to create a synonym of |vector1| if it's not irrelevant. + if (!transformation_context.GetFactManager()->IdIsIrrelevant( + message_.vector1()) && + !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, + vector1_instruction)) { + return false; + } // The second vector must be an instruction with a type id auto vector2_instruction = ir_context->get_def_use_mgr()->GetDef(message_.vector2()); if (!vector2_instruction || !vector2_instruction->type_id()) { return false; } + // We should be able to create a synonym of |vector2| if it's not irrelevant. + if (!transformation_context.GetFactManager()->IdIsIrrelevant( + message_.vector2()) && + !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, + vector2_instruction)) { + return false; + } auto vector1_type = ir_context->get_type_mgr()->GetType(vector1_instruction->type_id()); // The first vector instruction's type must actually be a vector type. @@ -161,9 +176,21 @@ void TransformationVectorShuffle::Apply( // |component| refers. if (component < GetVectorType(ir_context, message_.vector1())->element_count()) { + // Irrelevant id cannot participate in DataSynonym facts. + if (transformation_context->GetFactManager()->IdIsIrrelevant( + message_.vector1())) { + continue; + } + descriptor_for_source_component = MakeDataDescriptor(message_.vector1(), {component}); } else { + // Irrelevant id cannot participate in DataSynonym facts. + if (transformation_context->GetFactManager()->IdIsIrrelevant( + message_.vector2())) { + continue; + } + auto index_into_vector_2 = component - GetVectorType(ir_context, message_.vector1())->element_count(); diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_vector_shuffle.h b/3rdparty/spirv-tools/source/fuzz/transformation_vector_shuffle.h index f73fc3112..f91197615 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_vector_shuffle.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_vector_shuffle.h @@ -19,7 +19,6 @@ #include "source/fuzz/transformation.h" #include "source/fuzz/transformation_context.h" #include "source/opt/ir_context.h" - #include "source/opt/types.h" namespace spvtools { @@ -59,7 +58,9 @@ class TransformationVectorShuffle : public Transformation { // from which it came (with undefined components being ignored). If the // result vector is a contiguous sub-range of one of the input vectors, a // fact is added to record that |message_.fresh_id| is synonymous with this - // sub-range. + // sub-range. DataSynonym facts are added only for non-irrelevant vectors + // (e.g. if |vector1| is irrelevant but |vector2| is not, synonyms will be + // created for |vector1| but not |vector2|). void Apply(opt::IRContext* ir_context, TransformationContext* transformation_context) const override; diff --git a/3rdparty/spirv-tools/source/opcode.cpp b/3rdparty/spirv-tools/source/opcode.cpp index 0a7a95dcc..f93cfd371 100644 --- a/3rdparty/spirv-tools/source/opcode.cpp +++ b/3rdparty/spirv-tools/source/opcode.cpp @@ -413,6 +413,7 @@ bool spvOpcodeIsAtomicWithLoad(const SpvOp opcode) { case SpvOpAtomicIIncrement: case SpvOpAtomicIDecrement: case SpvOpAtomicIAdd: + case SpvOpAtomicFAddEXT: case SpvOpAtomicISub: case SpvOpAtomicSMin: case SpvOpAtomicUMin: @@ -445,7 +446,7 @@ bool spvOpcodeIsReturn(SpvOp opcode) { bool spvOpcodeIsReturnOrAbort(SpvOp opcode) { return spvOpcodeIsReturn(opcode) || opcode == SpvOpKill || - opcode == SpvOpUnreachable; + opcode == SpvOpUnreachable || opcode == SpvOpTerminateInvocation; } bool spvOpcodeIsBlockTerminator(SpvOp opcode) { @@ -700,6 +701,7 @@ std::vector spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) { case SpvOpAtomicIIncrement: case SpvOpAtomicIDecrement: case SpvOpAtomicIAdd: + case SpvOpAtomicFAddEXT: case SpvOpAtomicISub: case SpvOpAtomicSMin: case SpvOpAtomicUMin: diff --git a/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp b/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp index 2e90a2162..b75578744 100644 --- a/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp @@ -38,6 +38,8 @@ const uint32_t kLoopMergeMergeBlockIdInIdx = 0; const uint32_t kLoopMergeContinueBlockIdInIdx = 1; const uint32_t kCopyMemoryTargetAddrInIdx = 0; const uint32_t kCopyMemorySourceAddrInIdx = 1; +const uint32_t kDebugDeclareOperandVariableIndex = 5; +const uint32_t kGlobalVariableVariableIndex = 12; // Sorting functor to present annotation instructions in an easy-to-process // order. The functor orders by opcode first and falls back on unique id @@ -470,6 +472,19 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) { if (varId != 0) { ProcessLoad(func, varId); } + // If DebugDeclare, process as load of variable + } else if (liveInst->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugDeclare) { + uint32_t varId = + liveInst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); + ProcessLoad(func, varId); + // If DebugValue with Deref, process as load of variable + } else if (liveInst->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugValue) { + uint32_t varId = context() + ->get_debug_info_mgr() + ->GetVariableIdOfDebugValueUsedForDeclare(liveInst); + if (varId != 0) ProcessLoad(func, varId); // If merge, add other branches that are part of its control structure } else if (liveInst->opcode() == SpvOpLoopMerge || liveInst->opcode() == SpvOpSelectionMerge) { @@ -621,6 +636,18 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { } } } + + // For each DebugInfo GlobalVariable keep all operands except the Variable. + // Later, if the variable is dead, we will set the operand to DebugInfoNone. + for (auto& dbg : get_module()->ext_inst_debuginfo()) { + if (dbg.GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugGlobalVariable) + continue; + dbg.ForEachInId([this](const uint32_t* iid) { + Instruction* inInst = get_def_use_mgr()->GetDef(*iid); + if (inInst->opcode() == SpvOpVariable) return; + AddToWorklist(inInst); + }); + } } Pass::Status AggressiveDCEPass::ProcessImpl() { @@ -840,6 +867,26 @@ bool AggressiveDCEPass::ProcessGlobalValues() { } } + for (auto& dbg : get_module()->ext_inst_debuginfo()) { + if (!IsDead(&dbg)) continue; + // Save GlobalVariable if its variable is live, otherwise null out variable + // index + if (dbg.GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugGlobalVariable) { + auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex); + Instruction* var_inst = get_def_use_mgr()->GetDef(var_id); + if (!IsDead(var_inst)) continue; + context()->ForgetUses(&dbg); + dbg.SetOperand( + kGlobalVariableVariableIndex, + {context()->get_debug_info_mgr()->GetDebugInfoNone()->result_id()}); + context()->AnalyzeUses(&dbg); + continue; + } + to_kill_.push_back(&dbg); + modified = true; + } + // Since ADCE is disabled for non-shaders, we don't check for export linkage // attributes here. for (auto& val : get_module()->types_values()) { @@ -939,6 +986,7 @@ void AggressiveDCEPass::InitExtensions() { "SPV_KHR_ray_tracing", "SPV_EXT_fragment_invocation_density", "SPV_EXT_physical_storage_buffer", + "SPV_KHR_terminate_invocation", }); } diff --git a/3rdparty/spirv-tools/source/opt/code_sink.cpp b/3rdparty/spirv-tools/source/opt/code_sink.cpp index 4c88cd432..e49029fe1 100644 --- a/3rdparty/spirv-tools/source/opt/code_sink.cpp +++ b/3rdparty/spirv-tools/source/opt/code_sink.cpp @@ -214,6 +214,7 @@ bool CodeSinkingPass::HasUniformMemorySync() { case SpvOpAtomicIIncrement: case SpvOpAtomicIDecrement: case SpvOpAtomicIAdd: + case SpvOpAtomicFAddEXT: case SpvOpAtomicISub: case SpvOpAtomicSMin: case SpvOpAtomicUMin: diff --git a/3rdparty/spirv-tools/source/opt/debug_info_manager.h b/3rdparty/spirv-tools/source/opt/debug_info_manager.h index d7c270037..ff91b5c54 100644 --- a/3rdparty/spirv-tools/source/opt/debug_info_manager.h +++ b/3rdparty/spirv-tools/source/opt/debug_info_manager.h @@ -148,6 +148,11 @@ class DebugInfoManager { // Erases |instr| from data structures of this class. void ClearDebugInfo(Instruction* instr); + // Returns the id of Value operand if |inst| is DebugValue who has Deref + // operation and its Value operand is a result id of OpVariable with + // Function storage class. Otherwise, returns 0. + uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst); + private: IRContext* context() { return context_; } @@ -178,11 +183,6 @@ class DebugInfoManager { // Returns a DebugExpression instruction without Operation operands. Instruction* GetEmptyDebugExpression(); - // Returns the id of Value operand if |inst| is DebugValue who has Deref - // operation and its Value operand is a result id of OpVariable with - // Function storage class. Otherwise, returns 0. - uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst); - // Returns true if a scope |ancestor| is |scope| or an ancestor scope // of |scope|. bool IsAncestorOfScope(uint32_t scope, uint32_t ancestor); diff --git a/3rdparty/spirv-tools/source/opt/dominator_tree.cpp b/3rdparty/spirv-tools/source/opt/dominator_tree.cpp index da5073a5e..7e61506b9 100644 --- a/3rdparty/spirv-tools/source/opt/dominator_tree.cpp +++ b/3rdparty/spirv-tools/source/opt/dominator_tree.cpp @@ -176,7 +176,8 @@ void BasicBlockSuccessorHelper::CreateSuccessorMap( // The tree construction requires 1 entry point, so we add a dummy node // that is connected to all function exiting basic blocks. // An exiting basic block is a block with an OpKill, OpUnreachable, - // OpReturn or OpReturnValue as terminator instruction. + // OpReturn, OpReturnValue, or OpTerminateInvocation as terminator + // instruction. for (BasicBlock& bb : f) { if (bb.hasSuccessor()) { BasicBlockListTy& pred_list = predecessors_[&bb]; diff --git a/3rdparty/spirv-tools/source/opt/inline_pass.cpp b/3rdparty/spirv-tools/source/opt/inline_pass.cpp index cb5a1265e..ef94d0d6c 100644 --- a/3rdparty/spirv-tools/source/opt/inline_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/inline_pass.cpp @@ -384,7 +384,8 @@ std::unique_ptr InlinePass::InlineReturn( for (auto callee_block_itr = calleeFn->begin(); callee_block_itr != calleeFn->end(); ++callee_block_itr) { if (callee_block_itr->tail()->opcode() == SpvOpUnreachable || - callee_block_itr->tail()->opcode() == SpvOpKill) { + callee_block_itr->tail()->opcode() == SpvOpKill || + callee_block_itr->tail()->opcode() == SpvOpTerminateInvocation) { returnLabelId = context()->TakeNextId(); break; } @@ -738,16 +739,18 @@ bool InlinePass::IsInlinableFunction(Function* func) { bool func_is_called_from_continue = funcs_called_from_continue_.count(func->result_id()) != 0; - if (func_is_called_from_continue && ContainsKill(func)) { + if (func_is_called_from_continue && ContainsKillOrTerminateInvocation(func)) { return false; } return true; } -bool InlinePass::ContainsKill(Function* func) const { - return !func->WhileEachInst( - [](Instruction* inst) { return inst->opcode() != SpvOpKill; }); +bool InlinePass::ContainsKillOrTerminateInvocation(Function* func) const { + return !func->WhileEachInst([](Instruction* inst) { + const auto opcode = inst->opcode(); + return (opcode != SpvOpKill) && (opcode != SpvOpTerminateInvocation); + }); } void InlinePass::InitializeInline() { diff --git a/3rdparty/spirv-tools/source/opt/inline_pass.h b/3rdparty/spirv-tools/source/opt/inline_pass.h index 202bc97fd..abe773af8 100644 --- a/3rdparty/spirv-tools/source/opt/inline_pass.h +++ b/3rdparty/spirv-tools/source/opt/inline_pass.h @@ -139,8 +139,9 @@ class InlinePass : public Pass { // Return true if |func| is a function that can be inlined. bool IsInlinableFunction(Function* func); - // Returns true if |func| contains an OpKill instruction. - bool ContainsKill(Function* func) const; + // Returns true if |func| contains an OpKill or OpTerminateInvocation + // instruction. + bool ContainsKillOrTerminateInvocation(Function* func) const; // Update phis in succeeding blocks to point to new last block void UpdateSucceedingPhis( diff --git a/3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.cpp b/3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.cpp index 05704c148..9b8c112e1 100644 --- a/3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.cpp @@ -382,6 +382,7 @@ void LocalAccessChainConvertPass::InitExtensions() { "SPV_KHR_ray_tracing", "SPV_KHR_ray_query", "SPV_EXT_fragment_invocation_density", + "SPV_KHR_terminate_invocation", }); } 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 57572825d..bd5d75101 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 @@ -267,6 +267,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() { "SPV_KHR_ray_query", "SPV_EXT_fragment_invocation_density", "SPV_EXT_physical_storage_buffer", + "SPV_KHR_terminate_invocation", }); } 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 6626d87f3..238410755 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 @@ -121,6 +121,7 @@ void LocalSingleStoreElimPass::InitExtensionAllowList() { "SPV_KHR_ray_query", "SPV_EXT_fragment_invocation_density", "SPV_EXT_physical_storage_buffer", + "SPV_KHR_terminate_invocation", }); } bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) { diff --git a/3rdparty/spirv-tools/source/opt/loop_unroller.cpp b/3rdparty/spirv-tools/source/opt/loop_unroller.cpp index 10fac0433..40cf6bc2a 100644 --- a/3rdparty/spirv-tools/source/opt/loop_unroller.cpp +++ b/3rdparty/spirv-tools/source/opt/loop_unroller.cpp @@ -997,7 +997,8 @@ bool LoopUtils::CanPerformUnroll() { const BasicBlock* block = context_->cfg()->block(label_id); if (block->ctail()->opcode() == SpvOp::SpvOpKill || block->ctail()->opcode() == SpvOp::SpvOpReturn || - block->ctail()->opcode() == SpvOp::SpvOpReturnValue) { + block->ctail()->opcode() == SpvOp::SpvOpReturnValue || + block->ctail()->opcode() == SpvOp::SpvOpTerminateInvocation) { return false; } } diff --git a/3rdparty/spirv-tools/source/opt/merge_return_pass.cpp b/3rdparty/spirv-tools/source/opt/merge_return_pass.cpp index 8cb429945..2421c2cad 100644 --- a/3rdparty/spirv-tools/source/opt/merge_return_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/merge_return_pass.cpp @@ -299,9 +299,6 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block, // There is at least one values that needs to be replaced. // First create the OpPhi instruction. - InstructionBuilder builder( - context(), &*merge_block->begin(), - IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); uint32_t undef_id = Type2Undef(inst.type_id()); std::vector phi_operands; const std::set& new_edges = new_edges_[merge_block]; @@ -318,7 +315,50 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block, phi_operands.push_back(pred_id); } - Instruction* new_phi = builder.AddPhi(inst.type_id(), phi_operands); + Instruction* new_phi = nullptr; + // If the instruction is a pointer and variable pointers are not an option, + // then we have to regenerate the instruction instead of creating an OpPhi + // instruction. If not, the Spir-V will be invalid. + Instruction* inst_type = get_def_use_mgr()->GetDef(inst.type_id()); + bool regenerateInstruction = false; + if (inst_type->opcode() == SpvOpTypePointer) { + if (!context()->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointers)) { + regenerateInstruction = true; + } + + uint32_t storage_class = inst_type->GetSingleWordInOperand(0); + if (storage_class != SpvStorageClassWorkgroup && + storage_class != SpvStorageClassStorageBuffer) { + regenerateInstruction = true; + } + } + + if (regenerateInstruction) { + std::unique_ptr regen_inst(inst.Clone(context())); + uint32_t new_id = TakeNextId(); + regen_inst->SetResultId(new_id); + Instruction* insert_pos = &*merge_block->begin(); + while (insert_pos->opcode() == SpvOpPhi) { + insert_pos = insert_pos->NextNode(); + } + new_phi = insert_pos->InsertBefore(std::move(regen_inst)); + get_def_use_mgr()->AnalyzeInstDefUse(new_phi); + context()->set_instr_block(new_phi, merge_block); + + new_phi->ForEachInId([dom_tree, merge_block, this](uint32_t* use_id) { + Instruction* use = get_def_use_mgr()->GetDef(*use_id); + BasicBlock* use_bb = context()->get_instr_block(use); + if (use_bb != nullptr && !dom_tree->Dominates(use_bb, merge_block)) { + CreatePhiNodesForInst(merge_block, *use); + } + }); + } else { + InstructionBuilder builder( + context(), &*merge_block->begin(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + new_phi = builder.AddPhi(inst.type_id(), phi_operands); + } uint32_t result_of_phi = new_phi->result_id(); // Update all of the users to use the result of the new OpPhi. diff --git a/3rdparty/spirv-tools/source/opt/reflect.h b/3rdparty/spirv-tools/source/opt/reflect.h index 51d23a740..2e253add3 100644 --- a/3rdparty/spirv-tools/source/opt/reflect.h +++ b/3rdparty/spirv-tools/source/opt/reflect.h @@ -60,7 +60,8 @@ inline bool IsSpecConstantInst(SpvOp opcode) { return opcode >= SpvOpSpecConstantTrue && opcode <= SpvOpSpecConstantOp; } inline bool IsTerminatorInst(SpvOp opcode) { - return opcode >= SpvOpBranch && opcode <= SpvOpUnreachable; + return (opcode >= SpvOpBranch && opcode <= SpvOpUnreachable) || + (opcode == SpvOpTerminateInvocation); } } // namespace opt diff --git a/3rdparty/spirv-tools/source/opt/replace_invalid_opc.cpp b/3rdparty/spirv-tools/source/opt/replace_invalid_opc.cpp index 4e0f24f46..38b7539bf 100644 --- a/3rdparty/spirv-tools/source/opt/replace_invalid_opc.cpp +++ b/3rdparty/spirv-tools/source/opt/replace_invalid_opc.cpp @@ -141,6 +141,7 @@ bool ReplaceInvalidOpcodePass::IsFragmentShaderOnlyInstruction( // TODO: Teach |ReplaceInstruction| to handle block terminators. Then // uncomment the OpKill case. // case SpvOpKill: + // case SpvOpTerminateInstruction: return true; default: return false; diff --git a/3rdparty/spirv-tools/source/opt/wrap_opkill.cpp b/3rdparty/spirv-tools/source/opt/wrap_opkill.cpp index 3c8bae6d7..4d708405c 100644 --- a/3rdparty/spirv-tools/source/opt/wrap_opkill.cpp +++ b/3rdparty/spirv-tools/source/opt/wrap_opkill.cpp @@ -27,7 +27,8 @@ Pass::Status WrapOpKill::Process() { for (uint32_t func_id : func_to_process) { Function* func = context()->GetFunction(func_id); bool successful = func->WhileEachInst([this, &modified](Instruction* inst) { - if (inst->opcode() == SpvOpKill) { + const auto opcode = inst->opcode(); + if ((opcode == SpvOpKill) || (opcode == SpvOpTerminateInvocation)) { modified = true; if (!ReplaceWithFunctionCall(inst)) { return false; @@ -46,16 +47,22 @@ Pass::Status WrapOpKill::Process() { "The function should only be generated if something was modified."); context()->AddFunction(std::move(opkill_function_)); } + if (opterminateinvocation_function_ != nullptr) { + assert(modified && + "The function should only be generated if something was modified."); + context()->AddFunction(std::move(opterminateinvocation_function_)); + } return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); } bool WrapOpKill::ReplaceWithFunctionCall(Instruction* inst) { - assert(inst->opcode() == SpvOpKill && - "|inst| must be an OpKill instruction."); + assert((inst->opcode() == SpvOpKill || + inst->opcode() == SpvOpTerminateInvocation) && + "|inst| must be an OpKill or OpTerminateInvocation instruction."); InstructionBuilder ir_builder( context(), inst, IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); - uint32_t func_id = GetOpKillFuncId(); + uint32_t func_id = GetKillingFuncId(inst->opcode()); if (func_id == 0) { return false; } @@ -108,13 +115,20 @@ uint32_t WrapOpKill::GetVoidFunctionTypeId() { return type_mgr->GetTypeInstruction(&func_type); } -uint32_t WrapOpKill::GetOpKillFuncId() { - if (opkill_function_ != nullptr) { - return opkill_function_->result_id(); +uint32_t WrapOpKill::GetKillingFuncId(SpvOp opcode) { + // Parameterize by opcode + assert(opcode == SpvOpKill || opcode == SpvOpTerminateInvocation); + + std::unique_ptr* const killing_func = + (opcode == SpvOpKill) ? &opkill_function_ + : &opterminateinvocation_function_; + + if (*killing_func != nullptr) { + return (*killing_func)->result_id(); } - uint32_t opkill_func_id = TakeNextId(); - if (opkill_func_id == 0) { + uint32_t killing_func_id = TakeNextId(); + if (killing_func_id == 0) { return 0; } @@ -125,15 +139,15 @@ uint32_t WrapOpKill::GetOpKillFuncId() { // Generate the function start instruction std::unique_ptr func_start(new Instruction( - context(), SpvOpFunction, void_type_id, opkill_func_id, {})); + context(), SpvOpFunction, void_type_id, killing_func_id, {})); func_start->AddOperand({SPV_OPERAND_TYPE_FUNCTION_CONTROL, {0}}); func_start->AddOperand({SPV_OPERAND_TYPE_ID, {GetVoidFunctionTypeId()}}); - opkill_function_.reset(new Function(std::move(func_start))); + (*killing_func).reset(new Function(std::move(func_start))); // Generate the function end instruction std::unique_ptr func_end( new Instruction(context(), SpvOpFunctionEnd, 0, 0, {})); - opkill_function_->SetFunctionEnd(std::move(func_end)); + (*killing_func)->SetFunctionEnd(std::move(func_end)); // Create the one basic block for the function. uint32_t lab_id = TakeNextId(); @@ -146,21 +160,22 @@ uint32_t WrapOpKill::GetOpKillFuncId() { // Add the OpKill to the basic block std::unique_ptr kill_inst( - new Instruction(context(), SpvOpKill, 0, 0, {})); + new Instruction(context(), opcode, 0, 0, {})); bb->AddInstruction(std::move(kill_inst)); // Add the bb to the function - bb->SetParent(opkill_function_.get()); - opkill_function_->AddBasicBlock(std::move(bb)); + bb->SetParent((*killing_func).get()); + (*killing_func)->AddBasicBlock(std::move(bb)); // Add the function to the module. if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) { - opkill_function_->ForEachInst( - [this](Instruction* inst) { context()->AnalyzeDefUse(inst); }); + (*killing_func)->ForEachInst([this](Instruction* inst) { + context()->AnalyzeDefUse(inst); + }); } if (context()->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) { - for (BasicBlock& basic_block : *opkill_function_) { + for (BasicBlock& basic_block : *(*killing_func)) { context()->set_instr_block(basic_block.GetLabelInst(), &basic_block); for (Instruction& inst : basic_block) { context()->set_instr_block(&inst, &basic_block); @@ -168,7 +183,7 @@ uint32_t WrapOpKill::GetOpKillFuncId() { } } - return opkill_function_->result_id(); + return (*killing_func)->result_id(); } uint32_t WrapOpKill::GetOwningFunctionsReturnType(Instruction* inst) { diff --git a/3rdparty/spirv-tools/source/opt/wrap_opkill.h b/3rdparty/spirv-tools/source/opt/wrap_opkill.h index 09f2dfafd..7e43ca6cd 100644 --- a/3rdparty/spirv-tools/source/opt/wrap_opkill.h +++ b/3rdparty/spirv-tools/source/opt/wrap_opkill.h @@ -38,10 +38,10 @@ class WrapOpKill : public Pass { } private: - // Replaces the OpKill instruction |inst| with a function call to a function - // that contains a single instruction, which is OpKill. An OpUnreachable - // instruction will be placed after the function call. Return true if - // successful. + // Replaces the OpKill or OpTerminateInvocation instruction |inst| with a + // function call to a function that contains a single instruction, a clone of + // |inst|. An OpUnreachable instruction will be placed after the function + // call. Return true if successful. bool ReplaceWithFunctionCall(Instruction* inst); // Returns the id of the void type. @@ -51,9 +51,9 @@ class WrapOpKill : public Pass { uint32_t GetVoidFunctionTypeId(); // Return the id of a function that has return type void, has no parameters, - // and contains a single instruction, which is an OpKill. Returns 0 if the - // function could not be generated. - uint32_t GetOpKillFuncId(); + // and contains a single instruction, which is |opcode|, either OpKill or + // OpTerminateInvocation. Returns 0 if the function could not be generated. + uint32_t GetKillingFuncId(SpvOp opcode); // Returns the id of the return type for the function that contains |inst|. // Returns 0 if |inst| is not in a function. @@ -67,6 +67,11 @@ class WrapOpKill : public Pass { // function has a void return type and takes no parameters. If the function is // |nullptr|, then the function has not been generated. std::unique_ptr opkill_function_; + // The function that is a single instruction, which is an + // OpTerminateInvocation. The function has a void return type and takes no + // parameters. If the function is |nullptr|, then the function has not been + // generated. + std::unique_ptr opterminateinvocation_function_; }; } // namespace opt diff --git a/3rdparty/spirv-tools/source/val/validate_atomics.cpp b/3rdparty/spirv-tools/source/val/validate_atomics.cpp index b8867ddcc..df7973f87 100644 --- a/3rdparty/spirv-tools/source/val/validate_atomics.cpp +++ b/3rdparty/spirv-tools/source/val/validate_atomics.cpp @@ -54,11 +54,16 @@ namespace val { spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { const SpvOp opcode = inst->opcode(); const uint32_t result_type = inst->type_id(); - + bool is_atomic_float_opcode = false; + if (opcode == SpvOpAtomicLoad || opcode == SpvOpAtomicStore || + opcode == SpvOpAtomicFAddEXT || opcode == SpvOpAtomicExchange) { + is_atomic_float_opcode = true; + } switch (opcode) { case SpvOpAtomicLoad: case SpvOpAtomicStore: case SpvOpAtomicExchange: + case SpvOpAtomicFAddEXT: case SpvOpAtomicCompareExchange: case SpvOpAtomicCompareExchangeWeak: case SpvOpAtomicIIncrement: @@ -92,11 +97,59 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { } else if (opcode == SpvOpAtomicFlagClear || opcode == SpvOpAtomicStore) { assert(result_type == 0); } else { - if (!_.IsIntScalarType(result_type)) { + if (_.IsFloatScalarType(result_type)) { + if (is_atomic_float_opcode) { + if (opcode == SpvOpAtomicFAddEXT) { + if ((_.GetBitWidth(result_type) == 32) && + (!_.HasCapability(SpvCapabilityAtomicFloat32AddEXT))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float add atomics require the AtomicFloat32AddEXT " + "capability"; + } + if ((_.GetBitWidth(result_type) == 64) && + (!_.HasCapability(SpvCapabilityAtomicFloat64AddEXT))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float add atomics require the AtomicFloat64AddEXT " + "capability"; + } + } + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be int scalar type"; + } + } else if (_.IsIntScalarType(result_type) && + opcode == SpvOpAtomicFAddEXT) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) - << ": expected Result Type to be int scalar type"; + << ": expected Result Type to be float scalar type"; + } else if (!_.IsFloatScalarType(result_type) && + !_.IsIntScalarType(result_type)) { + switch (opcode) { + case SpvOpAtomicFAddEXT: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be float scalar type"; + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMin: + case SpvOpAtomicUMax: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be integer scalar type"; + default: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be int or float scalar type"; + } } + if (spvIsVulkanEnv(_.context()->target_env) && _.GetBitWidth(result_type) != 32) { switch (opcode) { @@ -108,11 +161,17 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { case SpvOpAtomicOr: case SpvOpAtomicXor: case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicFAddEXT: case SpvOpAtomicLoad: case SpvOpAtomicStore: case SpvOpAtomicExchange: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicCompareExchangeWeak: case SpvOpAtomicCompareExchange: { if (_.GetBitWidth(result_type) == 64 && + _.IsIntScalarType(result_type) && !_.HasCapability(SpvCapabilityInt64Atomics)) return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) diff --git a/3rdparty/spirv-tools/source/val/validate_cfg.cpp b/3rdparty/spirv-tools/source/val/validate_cfg.cpp index a2fe88279..8eb3a968f 100644 --- a/3rdparty/spirv-tools/source/val/validate_cfg.cpp +++ b/3rdparty/spirv-tools/source/val/validate_cfg.cpp @@ -1096,12 +1096,18 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) { case SpvOpKill: case SpvOpReturnValue: case SpvOpUnreachable: + case SpvOpTerminateInvocation: _.current_function().RegisterBlockEnd(std::vector()); if (opcode == SpvOpKill) { _.current_function().RegisterExecutionModelLimitation( SpvExecutionModelFragment, "OpKill requires Fragment execution model"); } + if (opcode == SpvOpTerminateInvocation) { + _.current_function().RegisterExecutionModelLimitation( + SpvExecutionModelFragment, + "OpTerminateInvocation requires Fragment execution model"); + } break; default: break; diff --git a/3rdparty/spirv-tools/source/val/validate_instruction.cpp b/3rdparty/spirv-tools/source/val/validate_instruction.cpp index 6478b3cb6..9d395fb46 100644 --- a/3rdparty/spirv-tools/source/val/validate_instruction.cpp +++ b/3rdparty/spirv-tools/source/val/validate_instruction.cpp @@ -296,7 +296,12 @@ spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) { << SPV_SPIRV_VERSION_MINOR_PART(last_version) << " or earlier"; } - if (inst_desc->numCapabilities > 0u) { + // OpTerminateInvocation is special because it is enabled by Shader + // capability, but also requries a extension and/or version check. + const bool capability_check_is_sufficient = + inst->opcode() != SpvOpTerminateInvocation; + + if (capability_check_is_sufficient && (inst_desc->numCapabilities > 0u)) { // We already checked that the direct capability dependency has been // satisfied. We don't need to check any further. return SPV_SUCCESS; diff --git a/3rdparty/spirv-tools/utils/roll_deps.sh b/3rdparty/spirv-tools/utils/roll_deps.sh index d5c547fe0..7ecfdd3b3 100755 --- a/3rdparty/spirv-tools/utils/roll_deps.sh +++ b/3rdparty/spirv-tools/utils/roll_deps.sh @@ -17,13 +17,13 @@ # # Depends on roll-dep from depot_path being in PATH. -effcee_dir="third_party/effcee/" +effcee_dir="external/effcee/" effcee_trunk="origin/main" -googletest_dir="third_party/googletest/" +googletest_dir="external/googletest/" googletest_trunk="origin/master" -re2_dir="third_party/re2/" +re2_dir="external/re2/" re2_trunk="origin/master" -spirv_headers_dir="third_party/spirv-headers/" +spirv_headers_dir="external/spirv-headers/" spirv_headers_trunk="origin/master" # This script assumes it's parent directory is the repo root.