diff --git a/3rdparty/spirv-tools/include/generated/build-version.inc b/3rdparty/spirv-tools/include/generated/build-version.inc index d4c98e877..41b6291dc 100644 --- a/3rdparty/spirv-tools/include/generated/build-version.inc +++ b/3rdparty/spirv-tools/include/generated/build-version.inc @@ -1 +1 @@ -"v2022.3-dev", "SPIRV-Tools v2022.3-dev b8091498a3d8f2f7a46df2009d7c340eef1939b6" +"v2022.3-dev", "SPIRV-Tools v2022.3-dev 05862b9695f45c8a8810d69c52ced4440446c1b7" diff --git a/3rdparty/spirv-tools/include/generated/enum_string_mapping.inc b/3rdparty/spirv-tools/include/generated/enum_string_mapping.inc index a0b618ff3..a09c64af4 100644 --- a/3rdparty/spirv-tools/include/generated/enum_string_mapping.inc +++ b/3rdparty/spirv-tools/include/generated/enum_string_mapping.inc @@ -10,6 +10,8 @@ const char* ExtensionToString(Extension extension) { return "SPV_AMD_gpu_shader_int16"; case Extension::kSPV_AMD_shader_ballot: return "SPV_AMD_shader_ballot"; + case Extension::kSPV_AMD_shader_early_and_late_fragment_tests: + return "SPV_AMD_shader_early_and_late_fragment_tests"; case Extension::kSPV_AMD_shader_explicit_vertex_parameter: return "SPV_AMD_shader_explicit_vertex_parameter"; case Extension::kSPV_AMD_shader_fragment_mask: @@ -213,8 +215,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_atomic_float16_add", "SPV_EXT_shader_atomic_float_add", "SPV_EXT_shader_atomic_float_min_max", "SPV_EXT_shader_image_int64", "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_arbitrary_precision_fixed_point", "SPV_INTEL_arbitrary_precision_floating_point", "SPV_INTEL_arbitrary_precision_integers", "SPV_INTEL_blocking_pipes", "SPV_INTEL_debug_module", "SPV_INTEL_device_side_avc_motion_estimation", "SPV_INTEL_float_controls2", "SPV_INTEL_fp_fast_math_mode", "SPV_INTEL_fpga_buffer_location", "SPV_INTEL_fpga_cluster_attributes", "SPV_INTEL_fpga_loop_controls", "SPV_INTEL_fpga_memory_accesses", "SPV_INTEL_fpga_memory_attributes", "SPV_INTEL_fpga_reg", "SPV_INTEL_function_pointers", "SPV_INTEL_inline_assembly", "SPV_INTEL_io_pipes", "SPV_INTEL_kernel_attributes", "SPV_INTEL_long_constant_composite", "SPV_INTEL_loop_fuse", "SPV_INTEL_media_block_io", "SPV_INTEL_memory_access_aliasing", "SPV_INTEL_optnone", "SPV_INTEL_shader_integer_functions2", "SPV_INTEL_split_barrier", "SPV_INTEL_subgroups", "SPV_INTEL_unstructured_loop_controls", "SPV_INTEL_usm_storage_classes", "SPV_INTEL_variable_length_array", "SPV_INTEL_vector_compute", "SPV_KHR_16bit_storage", "SPV_KHR_8bit_storage", "SPV_KHR_bit_instructions", "SPV_KHR_device_group", "SPV_KHR_expect_assume", "SPV_KHR_float_controls", "SPV_KHR_fragment_shader_barycentric", "SPV_KHR_fragment_shading_rate", "SPV_KHR_integer_dot_product", "SPV_KHR_linkonce_odr", "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_cull_mask", "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_rotate", "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_subgroup_vote", "SPV_KHR_terminate_invocation", "SPV_KHR_uniform_group_instructions", "SPV_KHR_variable_pointers", "SPV_KHR_vulkan_memory_model", "SPV_KHR_workgroup_memory_explicit_layout", "SPV_NVX_multiview_per_view_attributes", "SPV_NV_bindless_texture", "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_ray_tracing_motion_blur", "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_float16_add, Extension::kSPV_EXT_shader_atomic_float_add, Extension::kSPV_EXT_shader_atomic_float_min_max, Extension::kSPV_EXT_shader_image_int64, 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_arbitrary_precision_fixed_point, Extension::kSPV_INTEL_arbitrary_precision_floating_point, Extension::kSPV_INTEL_arbitrary_precision_integers, Extension::kSPV_INTEL_blocking_pipes, Extension::kSPV_INTEL_debug_module, Extension::kSPV_INTEL_device_side_avc_motion_estimation, Extension::kSPV_INTEL_float_controls2, Extension::kSPV_INTEL_fp_fast_math_mode, Extension::kSPV_INTEL_fpga_buffer_location, Extension::kSPV_INTEL_fpga_cluster_attributes, Extension::kSPV_INTEL_fpga_loop_controls, Extension::kSPV_INTEL_fpga_memory_accesses, Extension::kSPV_INTEL_fpga_memory_attributes, Extension::kSPV_INTEL_fpga_reg, Extension::kSPV_INTEL_function_pointers, Extension::kSPV_INTEL_inline_assembly, Extension::kSPV_INTEL_io_pipes, Extension::kSPV_INTEL_kernel_attributes, Extension::kSPV_INTEL_long_constant_composite, Extension::kSPV_INTEL_loop_fuse, Extension::kSPV_INTEL_media_block_io, Extension::kSPV_INTEL_memory_access_aliasing, Extension::kSPV_INTEL_optnone, Extension::kSPV_INTEL_shader_integer_functions2, Extension::kSPV_INTEL_split_barrier, Extension::kSPV_INTEL_subgroups, Extension::kSPV_INTEL_unstructured_loop_controls, Extension::kSPV_INTEL_usm_storage_classes, Extension::kSPV_INTEL_variable_length_array, Extension::kSPV_INTEL_vector_compute, Extension::kSPV_KHR_16bit_storage, Extension::kSPV_KHR_8bit_storage, Extension::kSPV_KHR_bit_instructions, Extension::kSPV_KHR_device_group, Extension::kSPV_KHR_expect_assume, Extension::kSPV_KHR_float_controls, Extension::kSPV_KHR_fragment_shader_barycentric, Extension::kSPV_KHR_fragment_shading_rate, Extension::kSPV_KHR_integer_dot_product, Extension::kSPV_KHR_linkonce_odr, 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_cull_mask, 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_rotate, Extension::kSPV_KHR_subgroup_uniform_control_flow, Extension::kSPV_KHR_subgroup_vote, Extension::kSPV_KHR_terminate_invocation, Extension::kSPV_KHR_uniform_group_instructions, Extension::kSPV_KHR_variable_pointers, Extension::kSPV_KHR_vulkan_memory_model, Extension::kSPV_KHR_workgroup_memory_explicit_layout, Extension::kSPV_NVX_multiview_per_view_attributes, Extension::kSPV_NV_bindless_texture, 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_ray_tracing_motion_blur, 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_early_and_late_fragment_tests", "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_float16_add", "SPV_EXT_shader_atomic_float_add", "SPV_EXT_shader_atomic_float_min_max", "SPV_EXT_shader_image_int64", "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_arbitrary_precision_fixed_point", "SPV_INTEL_arbitrary_precision_floating_point", "SPV_INTEL_arbitrary_precision_integers", "SPV_INTEL_blocking_pipes", "SPV_INTEL_debug_module", "SPV_INTEL_device_side_avc_motion_estimation", "SPV_INTEL_float_controls2", "SPV_INTEL_fp_fast_math_mode", "SPV_INTEL_fpga_buffer_location", "SPV_INTEL_fpga_cluster_attributes", "SPV_INTEL_fpga_loop_controls", "SPV_INTEL_fpga_memory_accesses", "SPV_INTEL_fpga_memory_attributes", "SPV_INTEL_fpga_reg", "SPV_INTEL_function_pointers", "SPV_INTEL_inline_assembly", "SPV_INTEL_io_pipes", "SPV_INTEL_kernel_attributes", "SPV_INTEL_long_constant_composite", "SPV_INTEL_loop_fuse", "SPV_INTEL_media_block_io", "SPV_INTEL_memory_access_aliasing", "SPV_INTEL_optnone", "SPV_INTEL_shader_integer_functions2", "SPV_INTEL_split_barrier", "SPV_INTEL_subgroups", "SPV_INTEL_unstructured_loop_controls", "SPV_INTEL_usm_storage_classes", "SPV_INTEL_variable_length_array", "SPV_INTEL_vector_compute", "SPV_KHR_16bit_storage", "SPV_KHR_8bit_storage", "SPV_KHR_bit_instructions", "SPV_KHR_device_group", "SPV_KHR_expect_assume", "SPV_KHR_float_controls", "SPV_KHR_fragment_shader_barycentric", "SPV_KHR_fragment_shading_rate", "SPV_KHR_integer_dot_product", "SPV_KHR_linkonce_odr", "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_cull_mask", "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_rotate", "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_subgroup_vote", "SPV_KHR_terminate_invocation", "SPV_KHR_uniform_group_instructions", "SPV_KHR_variable_pointers", "SPV_KHR_vulkan_memory_model", "SPV_KHR_workgroup_memory_explicit_layout", "SPV_NVX_multiview_per_view_attributes", "SPV_NV_bindless_texture", "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_ray_tracing_motion_blur", "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_early_and_late_fragment_tests, 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_float16_add, Extension::kSPV_EXT_shader_atomic_float_add, Extension::kSPV_EXT_shader_atomic_float_min_max, Extension::kSPV_EXT_shader_image_int64, 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_arbitrary_precision_fixed_point, Extension::kSPV_INTEL_arbitrary_precision_floating_point, Extension::kSPV_INTEL_arbitrary_precision_integers, Extension::kSPV_INTEL_blocking_pipes, Extension::kSPV_INTEL_debug_module, Extension::kSPV_INTEL_device_side_avc_motion_estimation, Extension::kSPV_INTEL_float_controls2, Extension::kSPV_INTEL_fp_fast_math_mode, Extension::kSPV_INTEL_fpga_buffer_location, Extension::kSPV_INTEL_fpga_cluster_attributes, Extension::kSPV_INTEL_fpga_loop_controls, Extension::kSPV_INTEL_fpga_memory_accesses, Extension::kSPV_INTEL_fpga_memory_attributes, Extension::kSPV_INTEL_fpga_reg, Extension::kSPV_INTEL_function_pointers, Extension::kSPV_INTEL_inline_assembly, Extension::kSPV_INTEL_io_pipes, Extension::kSPV_INTEL_kernel_attributes, Extension::kSPV_INTEL_long_constant_composite, Extension::kSPV_INTEL_loop_fuse, Extension::kSPV_INTEL_media_block_io, Extension::kSPV_INTEL_memory_access_aliasing, Extension::kSPV_INTEL_optnone, Extension::kSPV_INTEL_shader_integer_functions2, Extension::kSPV_INTEL_split_barrier, Extension::kSPV_INTEL_subgroups, Extension::kSPV_INTEL_unstructured_loop_controls, Extension::kSPV_INTEL_usm_storage_classes, Extension::kSPV_INTEL_variable_length_array, Extension::kSPV_INTEL_vector_compute, Extension::kSPV_KHR_16bit_storage, Extension::kSPV_KHR_8bit_storage, Extension::kSPV_KHR_bit_instructions, Extension::kSPV_KHR_device_group, Extension::kSPV_KHR_expect_assume, Extension::kSPV_KHR_float_controls, Extension::kSPV_KHR_fragment_shader_barycentric, Extension::kSPV_KHR_fragment_shading_rate, Extension::kSPV_KHR_integer_dot_product, Extension::kSPV_KHR_linkonce_odr, 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_cull_mask, 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_rotate, Extension::kSPV_KHR_subgroup_uniform_control_flow, Extension::kSPV_KHR_subgroup_vote, Extension::kSPV_KHR_terminate_invocation, Extension::kSPV_KHR_uniform_group_instructions, Extension::kSPV_KHR_variable_pointers, Extension::kSPV_KHR_vulkan_memory_model, Extension::kSPV_KHR_workgroup_memory_explicit_layout, Extension::kSPV_NVX_multiview_per_view_attributes, Extension::kSPV_NV_bindless_texture, 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_ray_tracing_motion_blur, 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( diff --git a/3rdparty/spirv-tools/include/generated/extension_enum.inc b/3rdparty/spirv-tools/include/generated/extension_enum.inc index 649d0c02d..41c7927cb 100644 --- a/3rdparty/spirv-tools/include/generated/extension_enum.inc +++ b/3rdparty/spirv-tools/include/generated/extension_enum.inc @@ -3,6 +3,7 @@ kSPV_AMD_gpu_shader_half_float, kSPV_AMD_gpu_shader_half_float_fetch, kSPV_AMD_gpu_shader_int16, kSPV_AMD_shader_ballot, +kSPV_AMD_shader_early_and_late_fragment_tests, kSPV_AMD_shader_explicit_vertex_parameter, kSPV_AMD_shader_fragment_mask, kSPV_AMD_shader_image_load_store_lod, diff --git a/3rdparty/spirv-tools/include/generated/nonsemantic.clspvreflection.insts.inc b/3rdparty/spirv-tools/include/generated/nonsemantic.clspvreflection.insts.inc index 2cf7cafe3..01cbf2546 100644 --- a/3rdparty/spirv-tools/include/generated/nonsemantic.clspvreflection.insts.inc +++ b/3rdparty/spirv-tools/include/generated/nonsemantic.clspvreflection.insts.inc @@ -25,5 +25,13 @@ static const spv_ext_inst_desc_t nonsemantic_clspvreflection_entries[] = { {"ConstantDataUniform", 22, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}}, {"LiteralSampler", 23, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}}, {"PropertyRequiredWorkgroupSize", 24, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}}, - {"SpecConstantSubgroupMaxSize", 25, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}} + {"SpecConstantSubgroupMaxSize", 25, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}}, + {"ArgumentPointerPushConstant", 26, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}}, + {"ArgumentPointerUniform", 27, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}}, + {"ProgramScopeVariablesStorageBuffer", 28, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}}, + {"ProgramScopeVariablePointerRelocation", 29, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}}, + {"ImageArgumentInfoChannelOrderPushConstant", 30, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}}, + {"ImageArgumentInfoChannelDataTypePushConstant", 31, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}}, + {"ImageArgumentInfoChannelOrderUniform", 32, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}}, + {"ImageArgumentInfoChannelDataTypeUniform", 33, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}} }; \ No newline at end of file diff --git a/3rdparty/spirv-tools/include/generated/operand.kinds-unified1.inc b/3rdparty/spirv-tools/include/generated/operand.kinds-unified1.inc index 610783c50..1a422d506 100644 --- a/3rdparty/spirv-tools/include/generated/operand.kinds-unified1.inc +++ b/3rdparty/spirv-tools/include/generated/operand.kinds-unified1.inc @@ -123,6 +123,8 @@ static const SpvCapability pygen_variable_caps_WorkgroupMemoryExplicitLayoutKHR[ static const spvtools::Extension pygen_variable_exts_SPV_AMD_gpu_shader_half_float_fetch[] = {spvtools::Extension::kSPV_AMD_gpu_shader_half_float_fetch}; static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_ballot[] = {spvtools::Extension::kSPV_AMD_shader_ballot}; +static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_tests[] = {spvtools::Extension::kSPV_AMD_shader_early_and_late_fragment_tests}; +static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_testsSPV_EXT_shader_stencil_export[] = {spvtools::Extension::kSPV_AMD_shader_early_and_late_fragment_tests, spvtools::Extension::kSPV_EXT_shader_stencil_export}; static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_explicit_vertex_parameter[] = {spvtools::Extension::kSPV_AMD_shader_explicit_vertex_parameter}; 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_AMD_shader_image_load_store_lod[] = {spvtools::Extension::kSPV_AMD_shader_image_load_store_lod}; @@ -450,7 +452,14 @@ static const spv_operand_desc_t pygen_variable_ExecutionModeEntries[] = { {"SignedZeroInfNanPreserve", 4461, 1, pygen_variable_caps_SignedZeroInfNanPreserve, 1, pygen_variable_exts_SPV_KHR_float_controls, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu}, {"RoundingModeRTE", 4462, 1, pygen_variable_caps_RoundingModeRTE, 1, pygen_variable_exts_SPV_KHR_float_controls, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu}, {"RoundingModeRTZ", 4463, 1, pygen_variable_caps_RoundingModeRTZ, 1, pygen_variable_exts_SPV_KHR_float_controls, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu}, + {"EarlyAndLateFragmentTestsAMD", 5017, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_tests, {}, 0xffffffffu, 0xffffffffu}, {"StencilRefReplacingEXT", 5027, 1, pygen_variable_caps_StencilExportEXT, 1, pygen_variable_exts_SPV_EXT_shader_stencil_export, {}, 0xffffffffu, 0xffffffffu}, + {"StencilRefUnchangedFrontAMD", 5079, 1, pygen_variable_caps_StencilExportEXT, 2, pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_testsSPV_EXT_shader_stencil_export, {}, 0xffffffffu, 0xffffffffu}, + {"StencilRefGreaterFrontAMD", 5080, 1, pygen_variable_caps_StencilExportEXT, 2, pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_testsSPV_EXT_shader_stencil_export, {}, 0xffffffffu, 0xffffffffu}, + {"StencilRefLessFrontAMD", 5081, 1, pygen_variable_caps_StencilExportEXT, 2, pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_testsSPV_EXT_shader_stencil_export, {}, 0xffffffffu, 0xffffffffu}, + {"StencilRefUnchangedBackAMD", 5082, 1, pygen_variable_caps_StencilExportEXT, 2, pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_testsSPV_EXT_shader_stencil_export, {}, 0xffffffffu, 0xffffffffu}, + {"StencilRefGreaterBackAMD", 5083, 1, pygen_variable_caps_StencilExportEXT, 2, pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_testsSPV_EXT_shader_stencil_export, {}, 0xffffffffu, 0xffffffffu}, + {"StencilRefLessBackAMD", 5084, 1, pygen_variable_caps_StencilExportEXT, 2, pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_testsSPV_EXT_shader_stencil_export, {}, 0xffffffffu, 0xffffffffu}, {"OutputLinesNV", 5269, 1, pygen_variable_caps_MeshShadingNV, 1, pygen_variable_exts_SPV_NV_mesh_shader, {}, 0xffffffffu, 0xffffffffu}, {"OutputPrimitivesNV", 5270, 1, pygen_variable_caps_MeshShadingNV, 1, pygen_variable_exts_SPV_NV_mesh_shader, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, 0xffffffffu, 0xffffffffu}, {"DerivativeGroupQuadsNV", 5289, 1, pygen_variable_caps_ComputeDerivativeGroupQuadsNV, 1, pygen_variable_exts_SPV_NV_compute_shader_derivatives, {}, 0xffffffffu, 0xffffffffu}, diff --git a/3rdparty/spirv-tools/source/cfa.h b/3rdparty/spirv-tools/source/cfa.h index 7cadf55f0..f55a7bde6 100644 --- a/3rdparty/spirv-tools/source/cfa.h +++ b/3rdparty/spirv-tools/source/cfa.h @@ -68,6 +68,8 @@ class CFA { /// CFG following postorder traversal semantics /// @param[in] backedge A function that will be called when a backedge is /// encountered during a traversal + /// @param[in] terminal A function that will be called to determine if the + /// search should stop at the given node. /// NOTE: The @p successor_func and predecessor_func each return a pointer to /// a /// collection such that iterators to that collection remain valid for the @@ -76,7 +78,8 @@ class CFA { const BB* entry, get_blocks_func successor_func, std::function preorder, std::function postorder, - std::function backedge); + std::function backedge, + std::function terminal); /// @brief Calculates dominator edges for a set of blocks /// @@ -138,7 +141,8 @@ void CFA::DepthFirstTraversal( const BB* entry, get_blocks_func successor_func, std::function preorder, std::function postorder, - std::function backedge) { + std::function backedge, + std::function terminal) { std::unordered_set processed; /// NOTE: work_list is the sequence of nodes from the root node to the node @@ -152,7 +156,7 @@ void CFA::DepthFirstTraversal( while (!work_list.empty()) { block_info& top = work_list.back(); - if (top.iter == end(*successor_func(top.block))) { + if (terminal(top.block) || top.iter == end(*successor_func(top.block))) { postorder(top.block); work_list.pop_back(); } else { @@ -266,11 +270,13 @@ std::vector CFA::TraversalRoots(const std::vector& blocks, auto mark_visited = [&visited](const BB* b) { visited.insert(b); }; auto ignore_block = [](const BB*) {}; auto ignore_blocks = [](const BB*, const BB*) {}; + auto no_terminal_blocks = [](const BB*) { return false; }; auto traverse_from_root = [&mark_visited, &succ_func, &ignore_block, - &ignore_blocks](const BB* entry) { + &ignore_blocks, + &no_terminal_blocks](const BB* entry) { DepthFirstTraversal(entry, succ_func, mark_visited, ignore_block, - ignore_blocks); + ignore_blocks, no_terminal_blocks); }; std::vector result; diff --git a/3rdparty/spirv-tools/source/disassemble.h b/3rdparty/spirv-tools/source/disassemble.h index 8eacb1006..b520a1ea9 100644 --- a/3rdparty/spirv-tools/source/disassemble.h +++ b/3rdparty/spirv-tools/source/disassemble.h @@ -25,9 +25,10 @@ namespace spvtools { // Decodes the given SPIR-V instruction binary representation to its assembly // text. The context is inferred from the provided module binary. The options -// parameter is a bit field of spv_binary_to_text_options_t. Decoded text will -// be stored into *text. Any error will be written into *diagnostic if -// diagnostic is non-null. +// parameter is a bit field of spv_binary_to_text_options_t (note: the option +// SPV_BINARY_TO_TEXT_OPTION_PRINT will be ignored). Decoded text will be +// stored into *text. Any error will be written into *diagnostic if diagnostic +// is non-null. std::string spvInstructionBinaryToText(const spv_target_env env, const uint32_t* inst_binary, const size_t inst_word_count, diff --git a/3rdparty/spirv-tools/source/libspirv.cpp b/3rdparty/spirv-tools/source/libspirv.cpp index 0bc093508..be76caaa8 100644 --- a/3rdparty/spirv-tools/source/libspirv.cpp +++ b/3rdparty/spirv-tools/source/libspirv.cpp @@ -99,7 +99,9 @@ bool SpirvTools::Disassemble(const uint32_t* binary, const size_t binary_size, spv_text spvtext = nullptr; spv_result_t status = spvBinaryToText(impl_->context, binary, binary_size, options, &spvtext, nullptr); - if (status == SPV_SUCCESS) { + if (status == SPV_SUCCESS && + (options & SPV_BINARY_TO_TEXT_OPTION_PRINT) == 0) { + assert(spvtext); text->assign(spvtext->str, spvtext->str + spvtext->length); } spvTextDestroy(spvtext); 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 04737521a..ffb499f67 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 @@ -659,9 +659,14 @@ Pass::Status AggressiveDCEPass::ProcessImpl() { InitializeModuleScopeLiveInstructions(); - // Process all entry point functions. - ProcessFunction pfn = [this](Function* fp) { return AggressiveDCE(fp); }; - modified |= context()->ProcessReachableCallTree(pfn); + // Run |AggressiveDCE| on the remaining functions. The order does not matter, + // since |AggressiveDCE| is intra-procedural. This can mean that function + // will become dead if all function call to them are removed. These dead + // function will still be in the module after this pass. We expect this to be + // rare. + for (Function& fp : *context()->module()) { + modified |= AggressiveDCE(&fp); + } // If the decoration manager is kept live then the context will try to keep it // up to date. ADCE deals with group decorations by changing the operands in @@ -687,8 +692,9 @@ Pass::Status AggressiveDCEPass::ProcessImpl() { } // Cleanup all CFG including all unreachable blocks. - ProcessFunction cleanup = [this](Function* f) { return CFGCleanup(f); }; - modified |= context()->ProcessReachableCallTree(cleanup); + for (Function& fp : *context()->module()) { + modified |= CFGCleanup(&fp); + } return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } @@ -968,6 +974,7 @@ void AggressiveDCEPass::InitExtensions() { "SPV_EXT_shader_image_int64", "SPV_KHR_non_semantic_info", "SPV_KHR_uniform_group_instructions", + "SPV_KHR_fragment_shader_barycentric", }); } diff --git a/3rdparty/spirv-tools/source/opt/cfg.cpp b/3rdparty/spirv-tools/source/opt/cfg.cpp index ac0fcc368..66b1aeda8 100644 --- a/3rdparty/spirv-tools/source/opt/cfg.cpp +++ b/3rdparty/spirv-tools/source/opt/cfg.cpp @@ -74,6 +74,12 @@ void CFG::RemoveNonExistingEdges(uint32_t blk_id) { void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root, std::list* order) { + ComputeStructuredOrder(func, root, nullptr, order); +} + +void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root, + BasicBlock* end, + std::list* order) { assert(module_->context()->get_feature_mgr()->HasCapability( SpvCapabilityShader) && "This only works on structured control flow"); @@ -82,6 +88,8 @@ void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root, ComputeStructuredSuccessors(func); auto ignore_block = [](cbb_ptr) {}; auto ignore_edge = [](cbb_ptr, cbb_ptr) {}; + auto terminal = [end](cbb_ptr bb) { return bb == end; }; + auto get_structured_successors = [this](const BasicBlock* b) { return &(block2structured_succs_[b]); }; @@ -92,7 +100,8 @@ void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root, order->push_front(const_cast(b)); }; CFA::DepthFirstTraversal(root, get_structured_successors, - ignore_block, post_order, ignore_edge); + ignore_block, post_order, ignore_edge, + terminal); } void CFG::ForEachBlockInPostOrder(BasicBlock* bb, @@ -205,7 +214,7 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) { // Find the back edge BasicBlock* latch_block = nullptr; Function::iterator latch_block_iter = header_it; - while (++latch_block_iter != fn->end()) { + for (; latch_block_iter != fn->end(); ++latch_block_iter) { // If blocks are in the proper order, then the only branch that appears // after the header is the latch. if (std::find(pred.begin(), pred.end(), latch_block_iter->id()) != @@ -237,6 +246,15 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) { context->set_instr_block(inst, new_header); }); + // If |bb| was the latch block, the branch back to the header is not in + // |new_header|. + if (latch_block == bb) { + if (new_header->ContinueBlockId() == bb->id()) { + new_header->GetLoopMergeInst()->SetInOperand(1, {new_header_id}); + } + latch_block = new_header; + } + // Adjust the OpPhi instructions as needed. bb->ForEachPhiInst([latch_block, bb, new_header, context](Instruction* phi) { std::vector preheader_phi_ops; diff --git a/3rdparty/spirv-tools/source/opt/cfg.h b/3rdparty/spirv-tools/source/opt/cfg.h index 33412f180..fa4fef2d5 100644 --- a/3rdparty/spirv-tools/source/opt/cfg.h +++ b/3rdparty/spirv-tools/source/opt/cfg.h @@ -66,6 +66,14 @@ class CFG { void ComputeStructuredOrder(Function* func, BasicBlock* root, std::list* order); + // Compute structured block order into |order| for |func| starting at |root| + // and ending at |end|. This order has the property that dominators come + // before all blocks they dominate, merge blocks come after all blocks that + // are in the control constructs of their header, and continue blocks come + // after all the blocks in the body of their loop. + void ComputeStructuredOrder(Function* func, BasicBlock* root, BasicBlock* end, + std::list* order); + // Applies |f| to all blocks that can be reach from |bb| in post order. void ForEachBlockInPostOrder(BasicBlock* bb, const std::function& f); diff --git a/3rdparty/spirv-tools/source/opt/const_folding_rules.cpp b/3rdparty/spirv-tools/source/opt/const_folding_rules.cpp index 249e11e56..cb3608747 100644 --- a/3rdparty/spirv-tools/source/opt/const_folding_rules.cpp +++ b/3rdparty/spirv-tools/source/opt/const_folding_rules.cpp @@ -251,6 +251,193 @@ ConstantFoldingRule FoldVectorTimesScalar() { }; } +ConstantFoldingRule FoldVectorTimesMatrix() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + assert(inst->opcode() == SpvOpVectorTimesMatrix); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + + if (!inst->IsFloatingPointFoldingAllowed()) { + if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) { + return nullptr; + } + } + + const analysis::Constant* c1 = constants[0]; + const analysis::Constant* c2 = constants[1]; + + if (c1 == nullptr || c2 == nullptr) { + return nullptr; + } + + // Check result type. + const analysis::Type* result_type = type_mgr->GetType(inst->type_id()); + const analysis::Vector* vector_type = result_type->AsVector(); + assert(vector_type != nullptr); + const analysis::Type* element_type = vector_type->element_type(); + assert(element_type != nullptr); + const analysis::Float* float_type = element_type->AsFloat(); + assert(float_type != nullptr); + + // Check types of c1 and c2. + assert(c1->type()->AsVector() == vector_type); + assert(c1->type()->AsVector()->element_type() == element_type && + c2->type()->AsMatrix()->element_type() == vector_type); + + // Get a float vector that is the result of vector-times-matrix. + std::vector c1_components = + c1->GetVectorComponents(const_mgr); + std::vector c2_components = + c2->AsMatrixConstant()->GetComponents(); + uint32_t resultVectorSize = result_type->AsVector()->element_count(); + + std::vector ids; + + if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) { + std::vector words(float_type->width() / 32, 0); + for (uint32_t i = 0; i < resultVectorSize; ++i) { + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } + + if (float_type->width() == 32) { + for (uint32_t i = 0; i < resultVectorSize; ++i) { + float result_scalar = 0.0f; + const analysis::VectorConstant* c2_vec = + c2_components[i]->AsVectorConstant(); + for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) { + float c1_scalar = c1_components[j]->GetFloat(); + float c2_scalar = c2_vec->GetComponents()[j]->GetFloat(); + result_scalar += c1_scalar * c2_scalar; + } + utils::FloatProxy result(result_scalar); + std::vector words = result.GetWords(); + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } else if (float_type->width() == 64) { + for (uint32_t i = 0; i < c2_components.size(); ++i) { + double result_scalar = 0.0; + const analysis::VectorConstant* c2_vec = + c2_components[i]->AsVectorConstant(); + for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) { + double c1_scalar = c1_components[j]->GetDouble(); + double c2_scalar = c2_vec->GetComponents()[j]->GetDouble(); + result_scalar += c1_scalar * c2_scalar; + } + utils::FloatProxy result(result_scalar); + std::vector words = result.GetWords(); + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } + return nullptr; + }; +} + +ConstantFoldingRule FoldMatrixTimesVector() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + assert(inst->opcode() == SpvOpMatrixTimesVector); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + + if (!inst->IsFloatingPointFoldingAllowed()) { + if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) { + return nullptr; + } + } + + const analysis::Constant* c1 = constants[0]; + const analysis::Constant* c2 = constants[1]; + + if (c1 == nullptr || c2 == nullptr) { + return nullptr; + } + + // Check result type. + const analysis::Type* result_type = type_mgr->GetType(inst->type_id()); + const analysis::Vector* vector_type = result_type->AsVector(); + assert(vector_type != nullptr); + const analysis::Type* element_type = vector_type->element_type(); + assert(element_type != nullptr); + const analysis::Float* float_type = element_type->AsFloat(); + assert(float_type != nullptr); + + // Check types of c1 and c2. + assert(c1->type()->AsMatrix()->element_type() == vector_type); + assert(c2->type()->AsVector()->element_type() == element_type); + + // Get a float vector that is the result of matrix-times-vector. + std::vector c1_components = + c1->AsMatrixConstant()->GetComponents(); + std::vector c2_components = + c2->GetVectorComponents(const_mgr); + uint32_t resultVectorSize = result_type->AsVector()->element_count(); + + std::vector ids; + + if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) { + std::vector words(float_type->width() / 32, 0); + for (uint32_t i = 0; i < resultVectorSize; ++i) { + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } + + if (float_type->width() == 32) { + for (uint32_t i = 0; i < resultVectorSize; ++i) { + float result_scalar = 0.0f; + for (uint32_t j = 0; j < c1_components.size(); ++j) { + float c1_scalar = c1_components[j] + ->AsVectorConstant() + ->GetComponents()[i] + ->GetFloat(); + float c2_scalar = c2_components[j]->GetFloat(); + result_scalar += c1_scalar * c2_scalar; + } + utils::FloatProxy result(result_scalar); + std::vector words = result.GetWords(); + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } else if (float_type->width() == 64) { + for (uint32_t i = 0; i < resultVectorSize; ++i) { + double result_scalar = 0.0; + for (uint32_t j = 0; j < c1_components.size(); ++j) { + double c1_scalar = c1_components[j] + ->AsVectorConstant() + ->GetComponents()[i] + ->GetDouble(); + double c2_scalar = c2_components[j]->GetDouble(); + result_scalar += c1_scalar * c2_scalar; + } + utils::FloatProxy result(result_scalar); + std::vector words = result.GetWords(); + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } + return nullptr; + }; +} + ConstantFoldingRule FoldCompositeWithConstants() { // Folds an OpCompositeConstruct where all of the inputs are constants to a // constant. A new constant is created if necessary. @@ -1288,6 +1475,8 @@ void ConstantFoldingRules::AddFoldingRules() { rules_[SpvOpVectorShuffle].push_back(FoldVectorShuffleWithConstants()); rules_[SpvOpVectorTimesScalar].push_back(FoldVectorTimesScalar()); + rules_[SpvOpVectorTimesMatrix].push_back(FoldVectorTimesMatrix()); + rules_[SpvOpMatrixTimesVector].push_back(FoldMatrixTimesVector()); rules_[SpvOpFNegate].push_back(FoldFNegate()); rules_[SpvOpQuantizeToF16].push_back(FoldQuantizeToF16()); diff --git a/3rdparty/spirv-tools/source/opt/dominator_tree.cpp b/3rdparty/spirv-tools/source/opt/dominator_tree.cpp index d86de151e..d6017bb19 100644 --- a/3rdparty/spirv-tools/source/opt/dominator_tree.cpp +++ b/3rdparty/spirv-tools/source/opt/dominator_tree.cpp @@ -59,7 +59,9 @@ static void DepthFirstSearch(const BBType* bb, SuccessorLambda successors, PreLambda pre, PostLambda post) { // Ignore backedge operation. auto nop_backedge = [](const BBType*, const BBType*) {}; - CFA::DepthFirstTraversal(bb, successors, pre, post, nop_backedge); + auto no_terminal_blocks = [](const BBType*) { return false; }; + CFA::DepthFirstTraversal(bb, successors, pre, post, nop_backedge, + no_terminal_blocks); } // Wrapper around CFA::DepthFirstTraversal to provide an interface to perform diff --git a/3rdparty/spirv-tools/source/opt/folding_rules.cpp b/3rdparty/spirv-tools/source/opt/folding_rules.cpp index d15ad0437..2d778b94a 100644 --- a/3rdparty/spirv-tools/source/opt/folding_rules.cpp +++ b/3rdparty/spirv-tools/source/opt/folding_rules.cpp @@ -277,6 +277,11 @@ uint32_t Reciprocal(analysis::ConstantManager* const_mgr, uint32_t width = c->type()->AsFloat()->width(); assert(width == 32 || width == 64); std::vector words; + + if (c->IsZero()) { + return 0; + } + if (width == 64) { spvtools::utils::FloatProxy result(1.0 / c->GetDouble()); if (!IsValidResult(result.getAsFloat())) return 0; @@ -1488,6 +1493,74 @@ bool MergeMulAddArithmetic(IRContext* context, Instruction* inst, return false; } +// Replaces |sub| inplace with an FMA instruction |(x*y)+a| where |a| first gets +// negated if |negate_addition| is true, otherwise |x| gets negated. +void ReplaceWithFmaAndNegate(Instruction* sub, uint32_t x, uint32_t y, + uint32_t a, bool negate_addition) { + uint32_t ext = + sub->context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + + if (ext == 0) { + sub->context()->AddExtInstImport("GLSL.std.450"); + ext = sub->context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + assert(ext != 0 && + "Could not add the GLSL.std.450 extended instruction set"); + } + + InstructionBuilder ir_builder( + sub->context(), sub, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + Instruction* neg = ir_builder.AddUnaryOp(sub->type_id(), SpvOpFNegate, + negate_addition ? a : x); + uint32_t neg_op = neg->result_id(); // -a : -x + + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {ext}}); + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {GLSLstd450Fma}}); + operands.push_back({SPV_OPERAND_TYPE_ID, {negate_addition ? x : neg_op}}); + operands.push_back({SPV_OPERAND_TYPE_ID, {y}}); + operands.push_back({SPV_OPERAND_TYPE_ID, {negate_addition ? neg_op : a}}); + + sub->SetOpcode(SpvOpExtInst); + sub->SetInOperands(std::move(operands)); +} + +// Folds a multiply and subtract into an Fma and negation. +// +// Cases: +// (x * y) - a = Fma x y -a +// a - (x * y) = Fma -x y a +bool MergeMulSubArithmetic(IRContext* context, Instruction* sub, + const std::vector&) { + assert(sub->opcode() == SpvOpFSub); + + if (!sub->IsFloatingPointFoldingAllowed()) { + return false; + } + + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + for (int i = 0; i < 2; i++) { + uint32_t op_id = sub->GetSingleWordInOperand(i); + Instruction* mul = def_use_mgr->GetDef(op_id); + + if (mul->opcode() != SpvOpFMul) { + continue; + } + + if (!mul->IsFloatingPointFoldingAllowed()) { + continue; + } + + uint32_t x = mul->GetSingleWordInOperand(0); + uint32_t y = mul->GetSingleWordInOperand(1); + uint32_t a = sub->GetSingleWordInOperand((i + 1) % 2); + ReplaceWithFmaAndNegate(sub, x, y, a, i == 0); + return true; + } + return false; +} + FoldingRule IntMultipleBy1() { return [](IRContext*, Instruction* inst, const std::vector& constants) { @@ -1631,6 +1704,57 @@ bool CompositeConstructFeedingExtract( return true; } +// Walks the indexes chain from |start| to |end| of an OpCompositeInsert or +// OpCompositeExtract instruction, and returns the type of the final element +// being accessed. +const analysis::Type* GetElementType(uint32_t type_id, + Instruction::iterator start, + Instruction::iterator end, + const analysis::TypeManager* type_mgr) { + const analysis::Type* type = type_mgr->GetType(type_id); + for (auto index : make_range(std::move(start), std::move(end))) { + assert(index.type == SPV_OPERAND_TYPE_LITERAL_INTEGER && + index.words.size() == 1); + if (auto* array_type = type->AsArray()) { + type = array_type->element_type(); + } else if (auto* matrix_type = type->AsMatrix()) { + type = matrix_type->element_type(); + } else if (auto* struct_type = type->AsStruct()) { + type = struct_type->element_types()[index.words[0]]; + } else { + type = nullptr; + } + } + return type; +} + +// Returns true of |inst_1| and |inst_2| have the same indexes that will be used +// to index into a composite object, excluding the last index. The two +// instructions must have the same opcode, and be either OpCompositeExtract or +// OpCompositeInsert instructions. +bool HaveSameIndexesExceptForLast(Instruction* inst_1, Instruction* inst_2) { + assert(inst_1->opcode() == inst_2->opcode() && + "Expecting the opcodes to be the same."); + assert((inst_1->opcode() == SpvOpCompositeInsert || + inst_1->opcode() == SpvOpCompositeExtract) && + "Instructions must be OpCompositeInsert or OpCompositeExtract."); + + if (inst_1->NumInOperands() != inst_2->NumInOperands()) { + return false; + } + + uint32_t first_index_position = + (inst_1->opcode() == SpvOpCompositeInsert ? 2 : 1); + for (uint32_t i = first_index_position; i < inst_1->NumInOperands() - 1; + i++) { + if (inst_1->GetSingleWordInOperand(i) != + inst_2->GetSingleWordInOperand(i)) { + return false; + } + } + return true; +} + // If the OpCompositeConstruct is simply putting back together elements that // where extracted from the same source, we can simply reuse the source. // @@ -1653,19 +1777,24 @@ bool CompositeExtractFeedingConstruct( // - extractions // - extracting the same position they are inserting // - all extract from the same id. + Instruction* first_element_inst = nullptr; for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { const uint32_t element_id = inst->GetSingleWordInOperand(i); Instruction* element_inst = def_use_mgr->GetDef(element_id); + if (first_element_inst == nullptr) { + first_element_inst = element_inst; + } if (element_inst->opcode() != SpvOpCompositeExtract) { return false; } - if (element_inst->NumInOperands() != 2) { + if (!HaveSameIndexesExceptForLast(element_inst, first_element_inst)) { return false; } - if (element_inst->GetSingleWordInOperand(1) != i) { + if (element_inst->GetSingleWordInOperand(element_inst->NumInOperands() - + 1) != i) { return false; } @@ -1681,13 +1810,31 @@ bool CompositeExtractFeedingConstruct( // The last check it to see that the object being extracted from is the // correct type. Instruction* original_inst = def_use_mgr->GetDef(original_id); - if (original_inst->type_id() != inst->type_id()) { + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* original_type = + GetElementType(original_inst->type_id(), first_element_inst->begin() + 3, + first_element_inst->end() - 1, type_mgr); + + if (original_type == nullptr) { return false; } - // Simplify by using the original object. - inst->SetOpcode(SpvOpCopyObject); - inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}}); + if (inst->type_id() != type_mgr->GetId(original_type)) { + return false; + } + + if (first_element_inst->NumInOperands() == 2) { + // Simplify by using the original object. + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}}); + return true; + } + + // Copies the original id and all indexes except for the last to the new + // extract instruction. + inst->SetOpcode(SpvOpCompositeExtract); + inst->SetInOperands(std::vector(first_element_inst->begin() + 2, + first_element_inst->end() - 1)); return true; } @@ -1891,6 +2038,139 @@ FoldingRule FMixFeedingExtract() { }; } +// Returns the number of elements in the composite type |type|. Returns 0 if +// |type| is a scalar value. +uint32_t GetNumberOfElements(const analysis::Type* type) { + if (auto* vector_type = type->AsVector()) { + return vector_type->element_count(); + } + if (auto* matrix_type = type->AsMatrix()) { + return matrix_type->element_count(); + } + if (auto* struct_type = type->AsStruct()) { + return static_cast(struct_type->element_types().size()); + } + if (auto* array_type = type->AsArray()) { + return array_type->length_info().words[0]; + } + return 0; +} + +// Returns a map with the set of values that were inserted into an object by +// the chain of OpCompositeInsertInstruction starting with |inst|. +// The map will map the index to the value inserted at that index. +std::map GetInsertedValues(Instruction* inst) { + analysis::DefUseManager* def_use_mgr = inst->context()->get_def_use_mgr(); + std::map values_inserted; + Instruction* current_inst = inst; + while (current_inst->opcode() == SpvOpCompositeInsert) { + if (current_inst->NumInOperands() > inst->NumInOperands()) { + // This is the catch the case + // %2 = OpCompositeInsert %m2x2int %v2int_1_0 %m2x2int_undef 0 + // %3 = OpCompositeInsert %m2x2int %int_4 %2 0 0 + // %4 = OpCompositeInsert %m2x2int %v2int_2_3 %3 1 + // In this case we cannot do a single construct to get the matrix. + uint32_t partially_inserted_element_index = + current_inst->GetSingleWordInOperand(inst->NumInOperands() - 1); + if (values_inserted.count(partially_inserted_element_index) == 0) + return {}; + } + if (HaveSameIndexesExceptForLast(inst, current_inst)) { + values_inserted.insert( + {current_inst->GetSingleWordInOperand(current_inst->NumInOperands() - + 1), + current_inst->GetSingleWordInOperand(kInsertObjectIdInIdx)}); + } + current_inst = def_use_mgr->GetDef( + current_inst->GetSingleWordInOperand(kInsertCompositeIdInIdx)); + } + return values_inserted; +} + +// Returns true of there is an entry in |values_inserted| for every element of +// |Type|. +bool DoInsertedValuesCoverEntireObject( + const analysis::Type* type, std::map& values_inserted) { + uint32_t container_size = GetNumberOfElements(type); + if (container_size != values_inserted.size()) { + return false; + } + + if (values_inserted.rbegin()->first >= container_size) { + return false; + } + return true; +} + +// Returns the type of the element that immediately contains the element being +// inserted by the OpCompositeInsert instruction |inst|. +const analysis::Type* GetContainerType(Instruction* inst) { + assert(inst->opcode() == SpvOpCompositeInsert); + analysis::TypeManager* type_mgr = inst->context()->get_type_mgr(); + return GetElementType(inst->type_id(), inst->begin() + 4, inst->end() - 1, + type_mgr); +} + +// Returns an OpCompositeConstruct instruction that build an object with +// |type_id| out of the values in |values_inserted|. Each value will be +// placed at the index corresponding to the value. The new instruction will +// be placed before |insert_before|. +Instruction* BuildCompositeConstruct( + uint32_t type_id, const std::map& values_inserted, + Instruction* insert_before) { + InstructionBuilder ir_builder( + insert_before->context(), insert_before, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + std::vector ids_in_order; + for (auto it : values_inserted) { + ids_in_order.push_back(it.second); + } + Instruction* construct = + ir_builder.AddCompositeConstruct(type_id, ids_in_order); + return construct; +} + +// Replaces the OpCompositeInsert |inst| that inserts |construct| into the same +// object as |inst| with final index removed. If the resulting +// OpCompositeInsert instruction would have no remaining indexes, the +// instruction is replaced with an OpCopyObject instead. +void InsertConstructedObject(Instruction* inst, const Instruction* construct) { + if (inst->NumInOperands() == 3) { + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {construct->result_id()}}}); + } else { + inst->SetInOperand(kInsertObjectIdInIdx, {construct->result_id()}); + inst->RemoveOperand(inst->NumOperands() - 1); + } +} + +// Replaces a series of |OpCompositeInsert| instruction that cover the entire +// object with an |OpCompositeConstruct|. +bool CompositeInsertToCompositeConstruct( + IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == SpvOpCompositeInsert && + "Wrong opcode. Should be OpCompositeInsert."); + if (inst->NumInOperands() < 3) return false; + + std::map values_inserted = GetInsertedValues(inst); + const analysis::Type* container_type = GetContainerType(inst); + if (container_type == nullptr) { + return false; + } + + if (!DoInsertedValuesCoverEntireObject(container_type, values_inserted)) { + return false; + } + + analysis::TypeManager* type_mgr = context->get_type_mgr(); + Instruction* construct = BuildCompositeConstruct( + type_mgr->GetId(container_type), values_inserted, inst); + InsertConstructedObject(inst, construct); + return true; +} + FoldingRule RedundantPhi() { // An OpPhi instruction where all values are the same or the result of the phi // itself, can be replaced by the value itself. @@ -2591,6 +2871,8 @@ void FoldingRules::AddFoldingRules() { rules_[SpvOpCompositeExtract].push_back(VectorShuffleFeedingExtract()); rules_[SpvOpCompositeExtract].push_back(FMixFeedingExtract()); + rules_[SpvOpCompositeInsert].push_back(CompositeInsertToCompositeConstruct); + rules_[SpvOpDot].push_back(DotProductDoingExtract()); rules_[SpvOpEntryPoint].push_back(RemoveRedundantOperands()); @@ -2622,6 +2904,7 @@ void FoldingRules::AddFoldingRules() { rules_[SpvOpFSub].push_back(MergeSubNegateArithmetic()); rules_[SpvOpFSub].push_back(MergeSubAddArithmetic()); rules_[SpvOpFSub].push_back(MergeSubSubArithmetic()); + rules_[SpvOpFSub].push_back(MergeMulSubArithmetic); rules_[SpvOpIAdd].push_back(RedundantIAdd()); rules_[SpvOpIAdd].push_back(MergeAddNegateArithmetic()); diff --git a/3rdparty/spirv-tools/source/opt/if_conversion.cpp b/3rdparty/spirv-tools/source/opt/if_conversion.cpp index d1debd055..1232796e5 100644 --- a/3rdparty/spirv-tools/source/opt/if_conversion.cpp +++ b/3rdparty/spirv-tools/source/opt/if_conversion.cpp @@ -160,6 +160,11 @@ bool IfConversion::CheckBlock(BasicBlock* block, DominatorAnalysis* dominators, BasicBlock* inc1 = context()->get_instr_block(preds[1]); if (dominators->Dominates(block, inc1)) return false; + if (inc0 == inc1) { + // If the predecessor blocks are the same, then there is only 1 value for + // the OpPhi. Other transformation should be able to simplify that. + return false; + } // All phis will have the same common dominator, so cache the result // for this block. If there is no common dominator, then we cannot transform // any phi in this basic block. diff --git a/3rdparty/spirv-tools/source/opt/inline_pass.cpp b/3rdparty/spirv-tools/source/opt/inline_pass.cpp index 2cc31258e..6e73f1cb9 100644 --- a/3rdparty/spirv-tools/source/opt/inline_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/inline_pass.cpp @@ -508,6 +508,37 @@ void InlinePass::MoveLoopMergeInstToFirstBlock( delete &*loop_merge_itr; } +void InlinePass::UpdateSingleBlockLoopContinueTarget( + uint32_t new_id, std::vector>* new_blocks) { + auto& header = new_blocks->front(); + auto* merge_inst = header->GetLoopMergeInst(); + + // The back-edge block is split at the branch to create a new back-edge + // block. The old block is modified to branch to the new block. The loop + // merge instruction is updated to declare the new block as the continue + // target. This has the effect of changing the loop from being a large + // continue construct and an empty loop construct to being a loop with a loop + // construct and a trivial continue construct. This change is made to satisfy + // structural dominance. + + // Add the new basic block. + std::unique_ptr new_block = + MakeUnique(NewLabel(new_id)); + auto& old_backedge = new_blocks->back(); + auto old_branch = old_backedge->tail(); + + // Move the old back edge into the new block. + std::unique_ptr br(&*old_branch); + new_block->AddInstruction(std::move(br)); + + // Add a branch to the new block from the old back-edge block. + AddBranch(new_id, &old_backedge); + new_blocks->push_back(std::move(new_block)); + + // Update the loop's continue target to the new block. + merge_inst->SetInOperand(1u, {new_id}); +} + bool InlinePass::GenInlineCode( std::vector>* new_blocks, std::vector>* new_vars, @@ -639,9 +670,19 @@ bool InlinePass::GenInlineCode( // Finalize inline code. new_blocks->push_back(std::move(new_blk_ptr)); - if (caller_is_loop_header && (new_blocks->size() > 1)) + if (caller_is_loop_header && (new_blocks->size() > 1)) { MoveLoopMergeInstToFirstBlock(new_blocks); + // If the loop was a single basic block previously, update it's structure. + auto& header = new_blocks->front(); + auto* merge_inst = header->GetLoopMergeInst(); + if (merge_inst->GetSingleWordInOperand(1u) == header->id()) { + auto new_id = context()->TakeNextId(); + if (new_id == 0) return false; + UpdateSingleBlockLoopContinueTarget(new_id, new_blocks); + } + } + // Update block map given replacement blocks. for (auto& blk : *new_blocks) { id2block_[blk->id()] = &*blk; diff --git a/3rdparty/spirv-tools/source/opt/inline_pass.h b/3rdparty/spirv-tools/source/opt/inline_pass.h index 9a5429bac..f20439542 100644 --- a/3rdparty/spirv-tools/source/opt/inline_pass.h +++ b/3rdparty/spirv-tools/source/opt/inline_pass.h @@ -235,6 +235,12 @@ class InlinePass : public Pass { // Move the OpLoopMerge from the last block back to the first. void MoveLoopMergeInstToFirstBlock( std::vector>* new_blocks); + + // Update the structure of single block loops so that the inlined code ends + // up in the loop construct and a new continue target is added to satisfy + // structural dominance. + void UpdateSingleBlockLoopContinueTarget( + uint32_t new_id, std::vector>* new_blocks); }; } // namespace opt diff --git a/3rdparty/spirv-tools/source/opt/instruction.cpp b/3rdparty/spirv-tools/source/opt/instruction.cpp index 418f1213a..6a8daea31 100644 --- a/3rdparty/spirv-tools/source/opt/instruction.cpp +++ b/3rdparty/spirv-tools/source/opt/instruction.cpp @@ -693,8 +693,12 @@ NonSemanticShaderDebugInfo100Instructions Instruction::GetShader100DebugOpcode() return NonSemanticShaderDebugInfo100InstructionsMax; } - return NonSemanticShaderDebugInfo100Instructions( - GetSingleWordInOperand(kExtInstInstructionInIdx)); + uint32_t opcode = GetSingleWordInOperand(kExtInstInstructionInIdx); + if (opcode >= NonSemanticShaderDebugInfo100InstructionsMax) { + return NonSemanticShaderDebugInfo100InstructionsMax; + } + + return NonSemanticShaderDebugInfo100Instructions(opcode); } CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const { diff --git a/3rdparty/spirv-tools/source/opt/interface_var_sroa.cpp b/3rdparty/spirv-tools/source/opt/interface_var_sroa.cpp index 58ed897c1..1b2cb3636 100644 --- a/3rdparty/spirv-tools/source/opt/interface_var_sroa.cpp +++ b/3rdparty/spirv-tools/source/opt/interface_var_sroa.cpp @@ -212,8 +212,12 @@ void InterfaceVariableScalarReplacement::KillInstructionAndUsers( context()->KillInst(inst); return; } + std::vector users; context()->get_def_use_mgr()->ForEachUser( - inst, [this](Instruction* user) { KillInstructionAndUsers(user); }); + inst, [&users](Instruction* user) { users.push_back(user); }); + for (auto user : users) { + context()->KillInst(user); + } context()->KillInst(inst); } 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 559856ec6..9491798e4 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 @@ -237,7 +237,8 @@ void LocalAccessChainConvertPass::FindTargetVars(Function* func) { } // Rule out variables with nested access chains // TODO(): Convert nested access chains - if (IsNonPtrAccessChain(op) && ptrInst->GetSingleWordInOperand( + bool is_non_ptr_access_chain = IsNonPtrAccessChain(op); + if (is_non_ptr_access_chain && ptrInst->GetSingleWordInOperand( kAccessChainPtrIdInIdx) != varId) { seen_non_target_vars_.insert(varId); seen_target_vars_.erase(varId); @@ -249,6 +250,12 @@ void LocalAccessChainConvertPass::FindTargetVars(Function* func) { seen_target_vars_.erase(varId); break; } + + if (is_non_ptr_access_chain && AnyIndexIsOutOfBounds(ptrInst)) { + seen_non_target_vars_.insert(varId); + seen_target_vars_.erase(varId); + break; + } } break; default: break; @@ -442,8 +449,46 @@ void LocalAccessChainConvertPass::InitExtensions() { "SPV_EXT_shader_image_int64", "SPV_KHR_non_semantic_info", "SPV_KHR_uniform_group_instructions", + "SPV_KHR_fragment_shader_barycentric", }); } +bool LocalAccessChainConvertPass::AnyIndexIsOutOfBounds( + const Instruction* access_chain_inst) { + assert(IsNonPtrAccessChain(access_chain_inst->opcode())); + + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + auto constants = const_mgr->GetOperandConstants(access_chain_inst); + uint32_t base_pointer_id = access_chain_inst->GetSingleWordInOperand(0); + Instruction* base_pointer = get_def_use_mgr()->GetDef(base_pointer_id); + const analysis::Pointer* base_pointer_type = + type_mgr->GetType(base_pointer->type_id())->AsPointer(); + assert(base_pointer_type != nullptr && + "The base of the access chain is not a pointer."); + const analysis::Type* current_type = base_pointer_type->pointee_type(); + for (uint32_t i = 1; i < access_chain_inst->NumInOperands(); ++i) { + if (IsIndexOutOfBounds(constants[i], current_type)) { + return true; + } + + uint32_t index = + (constants[i] + ? static_cast(constants[i]->GetZeroExtendedValue()) + : 0); + current_type = type_mgr->GetMemberType(current_type, {index}); + } + + return false; +} + +bool LocalAccessChainConvertPass::IsIndexOutOfBounds( + const analysis::Constant* index, const analysis::Type* type) const { + if (index == nullptr) { + return false; + } + return index->GetZeroExtendedValue() >= type->NumberOfComponents(); +} + } // namespace opt } // namespace spvtools diff --git a/3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.h b/3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.h index 8548e164a..eabf8645b 100644 --- a/3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.h +++ b/3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.h @@ -111,6 +111,17 @@ class LocalAccessChainConvertPass : public MemPass { // Returns a status to indicate success or failure, and change or no change. Status ConvertLocalAccessChains(Function* func); + // Returns true one of the indexes in the |access_chain_inst| is definitly out + // of bounds. If the size of the type or the value of the index is unknown, + // then it will be considered in-bounds. + bool AnyIndexIsOutOfBounds(const Instruction* access_chain_inst); + + // Returns true if getting element |index| from |type| would be out-of-bounds. + // If |index| is nullptr or the size of the type are unknown, then it will be + // considered in-bounds. + bool IsIndexOutOfBounds(const analysis::Constant* index, + const analysis::Type* type) const; + // Initialize extensions allowlist void InitExtensions(); 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 33c8bdf82..a58e8e4cc 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 @@ -287,6 +287,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() { "SPV_EXT_shader_image_int64", "SPV_KHR_non_semantic_info", "SPV_KHR_uniform_group_instructions", + "SPV_KHR_fragment_shader_barycentric", }); } 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 f22b19118..8cdd0abdc 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 @@ -140,6 +140,7 @@ void LocalSingleStoreElimPass::InitExtensionAllowList() { "SPV_EXT_shader_image_int64", "SPV_KHR_non_semantic_info", "SPV_KHR_uniform_group_instructions", + "SPV_KHR_fragment_shader_barycentric", }); } bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) { diff --git a/3rdparty/spirv-tools/source/opt/loop_descriptor.cpp b/3rdparty/spirv-tools/source/opt/loop_descriptor.cpp index 4feb64e86..13982d181 100644 --- a/3rdparty/spirv-tools/source/opt/loop_descriptor.cpp +++ b/3rdparty/spirv-tools/source/opt/loop_descriptor.cpp @@ -497,7 +497,8 @@ void Loop::ComputeLoopStructuredOrder( // continue blocks that must be copied to retain the structured order. // The structured order will include these. std::list order; - cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_, &order); + cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_, + loop_merge_, &order); for (BasicBlock* bb : order) { if (bb == GetMergeBlock()) { break; diff --git a/3rdparty/spirv-tools/source/opt/loop_unroller.cpp b/3rdparty/spirv-tools/source/opt/loop_unroller.cpp index 28ff07299..6f4e6f413 100644 --- a/3rdparty/spirv-tools/source/opt/loop_unroller.cpp +++ b/3rdparty/spirv-tools/source/opt/loop_unroller.cpp @@ -384,6 +384,7 @@ void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop, std::unique_ptr new_label{new Instruction( context_, SpvOp::SpvOpLabel, 0, context_->TakeNextId(), {})}; std::unique_ptr new_exit_bb{new BasicBlock(std::move(new_label))}; + new_exit_bb->SetParent(&function_); // Save the id of the block before we move it. uint32_t new_merge_id = new_exit_bb->id(); @@ -996,6 +997,20 @@ bool LoopUtils::CanPerformUnroll() { if (!loop_->FindNumberOfIterations(induction, &*condition->ctail(), nullptr)) return false; +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // ClusterFuzz/OSS-Fuzz is likely to yield examples with very high loop + // iteration counts. This can cause timeouts and memouts during fuzzing that + // are not classed as bugs. To avoid this noise, loop unrolling is not applied + // to loops with large iteration counts when fuzzing. + const size_t kFuzzerIterationLimit = 100; + size_t num_iterations; + loop_->FindNumberOfIterations(induction, &*condition->ctail(), + &num_iterations); + if (num_iterations > kFuzzerIterationLimit) { + return false; + } +#endif + // Make sure the latch block is a unconditional branch to the header // block. const Instruction& branch = *loop_->GetLatchBlock()->ctail(); diff --git a/3rdparty/spirv-tools/source/opt/optimizer.cpp b/3rdparty/spirv-tools/source/opt/optimizer.cpp index 29761518f..381589b53 100644 --- a/3rdparty/spirv-tools/source/opt/optimizer.cpp +++ b/3rdparty/spirv-tools/source/opt/optimizer.cpp @@ -623,10 +623,16 @@ bool Optimizer::Run(const uint32_t* original_binary, assert(optimized_binary_with_nop.size() == original_binary_size && "Binary size unexpectedly changed despite the optimizer saying " "there was no change"); - assert(memcmp(optimized_binary_with_nop.data(), original_binary, - original_binary_size) == 0 && - "Binary content unexpectedly changed despite the optimizer saying " - "there was no change"); + + // Compare the magic number to make sure the binaries were encoded in the + // endianness. If not, the contents of the binaries will be different, so + // do not check the contents. + if (optimized_binary_with_nop[0] == original_binary[0]) { + assert(memcmp(optimized_binary_with_nop.data(), original_binary, + original_binary_size) == 0 && + "Binary content unexpectedly changed despite the optimizer saying " + "there was no change"); + } } #endif // !NDEBUG diff --git a/3rdparty/spirv-tools/source/opt/reduce_load_size.cpp b/3rdparty/spirv-tools/source/opt/reduce_load_size.cpp index e9b808748..56491b2f2 100644 --- a/3rdparty/spirv-tools/source/opt/reduce_load_size.cpp +++ b/3rdparty/spirv-tools/source/opt/reduce_load_size.cpp @@ -161,8 +161,15 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) { case analysis::Type::kArray: { const analysis::Constant* size_const = const_mgr->FindDeclaredConstant(load_type->AsArray()->LengthId()); - assert(size_const->AsIntConstant()); - total_size = size_const->GetU32(); + + if (size_const) { + assert(size_const->AsIntConstant()); + total_size = size_const->GetU32(); + } else { + // The size is spec constant, so it is unknown at this time. Assume + // it is very large. + total_size = UINT32_MAX; + } } break; case analysis::Type::kStruct: total_size = static_cast( diff --git a/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.h b/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.h index 76afc2674..6a66dfb80 100644 --- a/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.h +++ b/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.h @@ -15,6 +15,7 @@ #ifndef SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_ #define SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_ +#include #include #include #include @@ -37,9 +38,20 @@ class ScalarReplacementPass : public MemPass { public: ScalarReplacementPass(uint32_t limit = kDefaultLimit) : max_num_elements_(limit) { - name_[0] = '\0'; - strcat(name_, "scalar-replacement="); - sprintf(&name_[strlen(name_)], "%d", max_num_elements_); + const auto num_to_write = snprintf( + name_, sizeof(name_), "scalar-replacement=%u", max_num_elements_); + assert(size_t(num_to_write) < sizeof(name_)); + (void)num_to_write; // Mark as unused + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // ClusterFuzz/OSS-Fuzz is likely to yield examples with very large arrays. + // This can cause timeouts and memouts during fuzzing that + // are not classed as bugs. To avoid this noise, we set the + // max_num_elements_ to a smaller value for fuzzing. + max_num_elements_ = + (max_num_elements_ > 0 && max_num_elements_ < 100 ? max_num_elements_ + : 100); +#endif } const char* name() const override { return name_; } @@ -253,7 +265,10 @@ class ScalarReplacementPass : public MemPass { // Limit on the number of members in an object that will be replaced. // 0 means there is no limit. uint32_t max_num_elements_; - char name_[55]; + // This has to be big enough to fit "scalar-replacement=" followed by a + // uint32_t number written in decimal (so 10 digits), and then a + // terminating nul. + char name_[30]; }; } // namespace opt diff --git a/3rdparty/spirv-tools/source/opt/types.cpp b/3rdparty/spirv-tools/source/opt/types.cpp index ebbdc367b..056acebb6 100644 --- a/3rdparty/spirv-tools/source/opt/types.cpp +++ b/3rdparty/spirv-tools/source/opt/types.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -246,6 +247,35 @@ size_t Type::HashValue() const { return ComputeHashValue(0, &seen); } +uint64_t Type::NumberOfComponents() const { + switch (kind()) { + case kVector: + return AsVector()->element_count(); + case kMatrix: + return AsMatrix()->element_count(); + case kArray: { + Array::LengthInfo length_info = AsArray()->length_info(); + if (length_info.words[0] != Array::LengthInfo::kConstant) { + return UINT64_MAX; + } + assert(length_info.words.size() <= 3 && + "The size of the array could not fit size_t."); + uint64_t length = 0; + length |= length_info.words[1]; + if (length_info.words.size() > 2) { + length |= static_cast(length_info.words[2]) << 32; + } + return length; + } + case kRuntimeArray: + return UINT64_MAX; + case kStruct: + return AsStruct()->element_types().size(); + default: + return 0; + } +} + bool Integer::IsSameImpl(const Type* that, IsSameCache*) const { const Integer* it = that->AsInteger(); return it && width_ == it->width_ && signed_ == it->signed_ && diff --git a/3rdparty/spirv-tools/source/opt/types.h b/3rdparty/spirv-tools/source/opt/types.h index f5a4a6be4..a92669e9f 100644 --- a/3rdparty/spirv-tools/source/opt/types.h +++ b/3rdparty/spirv-tools/source/opt/types.h @@ -160,6 +160,10 @@ class Type { size_t ComputeHashValue(size_t hash, SeenTypes* seen) const; + // Returns the number of components in a composite type. Returns 0 for a + // non-composite type. + uint64_t NumberOfComponents() const; + // A bunch of methods for casting this type to a given type. Returns this if the // cast can be done, nullptr otherwise. // clang-format off diff --git a/3rdparty/spirv-tools/source/val/basic_block.cpp b/3rdparty/spirv-tools/source/val/basic_block.cpp index b2a8793d2..da05db3a8 100644 --- a/3rdparty/spirv-tools/source/val/basic_block.cpp +++ b/3rdparty/spirv-tools/source/val/basic_block.cpp @@ -24,11 +24,13 @@ namespace val { BasicBlock::BasicBlock(uint32_t label_id) : id_(label_id), immediate_dominator_(nullptr), - immediate_post_dominator_(nullptr), + immediate_structural_dominator_(nullptr), + immediate_structural_post_dominator_(nullptr), predecessors_(), successors_(), type_(0), reachable_(false), + structurally_reachable_(false), label_(nullptr), terminator_(nullptr) {} @@ -36,21 +38,32 @@ void BasicBlock::SetImmediateDominator(BasicBlock* dom_block) { immediate_dominator_ = dom_block; } -void BasicBlock::SetImmediatePostDominator(BasicBlock* pdom_block) { - immediate_post_dominator_ = pdom_block; +void BasicBlock::SetImmediateStructuralDominator(BasicBlock* dom_block) { + immediate_structural_dominator_ = dom_block; +} + +void BasicBlock::SetImmediateStructuralPostDominator(BasicBlock* pdom_block) { + immediate_structural_post_dominator_ = pdom_block; } const BasicBlock* BasicBlock::immediate_dominator() const { return immediate_dominator_; } -const BasicBlock* BasicBlock::immediate_post_dominator() const { - return immediate_post_dominator_; +const BasicBlock* BasicBlock::immediate_structural_dominator() const { + return immediate_structural_dominator_; +} + +const BasicBlock* BasicBlock::immediate_structural_post_dominator() const { + return immediate_structural_post_dominator_; } BasicBlock* BasicBlock::immediate_dominator() { return immediate_dominator_; } -BasicBlock* BasicBlock::immediate_post_dominator() { - return immediate_post_dominator_; +BasicBlock* BasicBlock::immediate_structural_dominator() { + return immediate_structural_dominator_; +} +BasicBlock* BasicBlock::immediate_structural_post_dominator() { + return immediate_structural_post_dominator_; } void BasicBlock::RegisterSuccessors( @@ -58,6 +71,10 @@ void BasicBlock::RegisterSuccessors( for (auto& block : next_blocks) { block->predecessors_.push_back(this); successors_.push_back(block); + + // Register structural successors/predecessors too. + block->structural_predecessors_.push_back(this); + structural_successors_.push_back(block); } } @@ -67,10 +84,16 @@ bool BasicBlock::dominates(const BasicBlock& other) const { std::find(other.dom_begin(), other.dom_end(), this)); } -bool BasicBlock::postdominates(const BasicBlock& other) const { - return (this == &other) || - !(other.pdom_end() == - std::find(other.pdom_begin(), other.pdom_end(), this)); +bool BasicBlock::structurally_dominates(const BasicBlock& other) const { + return (this == &other) || !(other.structural_dom_end() == + std::find(other.structural_dom_begin(), + other.structural_dom_end(), this)); +} + +bool BasicBlock::structurally_postdominates(const BasicBlock& other) const { + return (this == &other) || !(other.structural_pdom_end() == + std::find(other.structural_pdom_begin(), + other.structural_pdom_end(), this)); } BasicBlock::DominatorIterator::DominatorIterator() : current_(nullptr) {} @@ -107,21 +130,43 @@ BasicBlock::DominatorIterator BasicBlock::dom_end() { return DominatorIterator(); } -const BasicBlock::DominatorIterator BasicBlock::pdom_begin() const { - return DominatorIterator( - this, [](const BasicBlock* b) { return b->immediate_post_dominator(); }); +const BasicBlock::DominatorIterator BasicBlock::structural_dom_begin() const { + return DominatorIterator(this, [](const BasicBlock* b) { + return b->immediate_structural_dominator(); + }); } -BasicBlock::DominatorIterator BasicBlock::pdom_begin() { - return DominatorIterator( - this, [](const BasicBlock* b) { return b->immediate_post_dominator(); }); +BasicBlock::DominatorIterator BasicBlock::structural_dom_begin() { + return DominatorIterator(this, [](const BasicBlock* b) { + return b->immediate_structural_dominator(); + }); } -const BasicBlock::DominatorIterator BasicBlock::pdom_end() const { +const BasicBlock::DominatorIterator BasicBlock::structural_dom_end() const { return DominatorIterator(); } -BasicBlock::DominatorIterator BasicBlock::pdom_end() { +BasicBlock::DominatorIterator BasicBlock::structural_dom_end() { + return DominatorIterator(); +} + +const BasicBlock::DominatorIterator BasicBlock::structural_pdom_begin() const { + return DominatorIterator(this, [](const BasicBlock* b) { + return b->immediate_structural_post_dominator(); + }); +} + +BasicBlock::DominatorIterator BasicBlock::structural_pdom_begin() { + return DominatorIterator(this, [](const BasicBlock* b) { + return b->immediate_structural_post_dominator(); + }); +} + +const BasicBlock::DominatorIterator BasicBlock::structural_pdom_end() const { + return DominatorIterator(); +} + +BasicBlock::DominatorIterator BasicBlock::structural_pdom_end() { return DominatorIterator(); } diff --git a/3rdparty/spirv-tools/source/val/basic_block.h b/3rdparty/spirv-tools/source/val/basic_block.h index 47cd06d07..be5657ea5 100644 --- a/3rdparty/spirv-tools/source/val/basic_block.h +++ b/3rdparty/spirv-tools/source/val/basic_block.h @@ -64,9 +64,32 @@ class BasicBlock { /// Returns the successors of the BasicBlock std::vector* successors() { return &successors_; } - /// Returns true if the block is reachable in the CFG + /// Returns the structural successors of the BasicBlock + std::vector* structural_predecessors() { + return &structural_predecessors_; + } + + /// Returns the structural predecessors of the BasicBlock + const std::vector* structural_predecessors() const { + return &structural_predecessors_; + } + + /// Returns the structural successors of the BasicBlock + std::vector* structural_successors() { + return &structural_successors_; + } + + /// Returns the structural predecessors of the BasicBlock + const std::vector* structural_successors() const { + return &structural_successors_; + } + + /// Returns true if the block is reachable in the CFG. bool reachable() const { return reachable_; } + /// Returns true if the block is structurally reachable in the CFG. + bool structurally_reachable() const { return structurally_reachable_; } + /// Returns true if BasicBlock is of the given type bool is_type(BlockType type) const { if (type == kBlockTypeUndefined) return type_.none(); @@ -76,6 +99,11 @@ class BasicBlock { /// Sets the reachability of the basic block in the CFG void set_reachable(bool reachability) { reachable_ = reachability; } + /// Sets the structural reachability of the basic block in the CFG + void set_structurally_reachable(bool reachability) { + structurally_reachable_ = reachability; + } + /// Sets the type of the BasicBlock void set_type(BlockType type) { if (type == kBlockTypeUndefined) @@ -89,10 +117,15 @@ class BasicBlock { /// @param[in] dom_block The dominator block void SetImmediateDominator(BasicBlock* dom_block); + /// Sets the immediate dominator of this basic block + /// + /// @param[in] dom_block The dominator block + void SetImmediateStructuralDominator(BasicBlock* dom_block); + /// Sets the immediate post dominator of this basic block /// /// @param[in] pdom_block The post dominator block - void SetImmediatePostDominator(BasicBlock* pdom_block); + void SetImmediateStructuralPostDominator(BasicBlock* pdom_block); /// Returns the immediate dominator of this basic block BasicBlock* immediate_dominator(); @@ -100,11 +133,17 @@ class BasicBlock { /// Returns the immediate dominator of this basic block const BasicBlock* immediate_dominator() const; - /// Returns the immediate post dominator of this basic block - BasicBlock* immediate_post_dominator(); + /// Returns the immediate dominator of this basic block + BasicBlock* immediate_structural_dominator(); + + /// Returns the immediate dominator of this basic block + const BasicBlock* immediate_structural_dominator() const; /// Returns the immediate post dominator of this basic block - const BasicBlock* immediate_post_dominator() const; + BasicBlock* immediate_structural_post_dominator(); + + /// Returns the immediate post dominator of this basic block + const BasicBlock* immediate_structural_post_dominator() const; /// Returns the label instruction for the block, or nullptr if not set. const Instruction* label() const { return label_; } @@ -132,9 +171,18 @@ class BasicBlock { /// Assumes dominators have been computed. bool dominates(const BasicBlock& other) const; - /// Returns true if this block postdominates the other block. - /// Assumes dominators have been computed. - bool postdominates(const BasicBlock& other) const; + /// Returns true if this block structurally dominates the other block. + /// Assumes structural dominators have been computed. + bool structurally_dominates(const BasicBlock& other) const; + + /// Returns true if this block structurally postdominates the other block. + /// Assumes structural dominators have been computed. + bool structurally_postdominates(const BasicBlock& other) const; + + void RegisterStructuralSuccessor(BasicBlock* block) { + block->structural_predecessors_.push_back(this); + structural_successors_.push_back(block); + } /// @brief A BasicBlock dominator iterator class /// @@ -191,18 +239,32 @@ class BasicBlock { /// block DominatorIterator dom_end(); + /// Returns a dominator iterator which points to the current block + const DominatorIterator structural_dom_begin() const; + + /// Returns a dominator iterator which points to the current block + DominatorIterator structural_dom_begin(); + + /// Returns a dominator iterator which points to one element past the first + /// block + const DominatorIterator structural_dom_end() const; + + /// Returns a dominator iterator which points to one element past the first + /// block + DominatorIterator structural_dom_end(); + /// Returns a post dominator iterator which points to the current block - const DominatorIterator pdom_begin() const; + const DominatorIterator structural_pdom_begin() const; /// Returns a post dominator iterator which points to the current block - DominatorIterator pdom_begin(); + DominatorIterator structural_pdom_begin(); /// Returns a post dominator iterator which points to one element past the /// last block - const DominatorIterator pdom_end() const; + const DominatorIterator structural_pdom_end() const; /// Returns a post dominator iterator which points to one element past the /// last block - DominatorIterator pdom_end(); + DominatorIterator structural_pdom_end(); private: /// Id of the BasicBlock @@ -211,8 +273,11 @@ class BasicBlock { /// Pointer to the immediate dominator of the BasicBlock BasicBlock* immediate_dominator_; - /// Pointer to the immediate dominator of the BasicBlock - BasicBlock* immediate_post_dominator_; + /// Pointer to the immediate structural dominator of the BasicBlock + BasicBlock* immediate_structural_dominator_; + + /// Pointer to the immediate structural post dominator of the BasicBlock + BasicBlock* immediate_structural_post_dominator_; /// The set of predecessors of the BasicBlock std::vector predecessors_; @@ -226,11 +291,17 @@ class BasicBlock { /// True if the block is reachable in the CFG bool reachable_; + /// True if the block is structurally reachable in the CFG + bool structurally_reachable_; + /// label of this block, if any. const Instruction* label_; /// Terminator of this block. const Instruction* terminator_; + + std::vector structural_predecessors_; + std::vector structural_successors_; }; /// @brief Returns true if the iterators point to the same element or if both diff --git a/3rdparty/spirv-tools/source/val/construct.cpp b/3rdparty/spirv-tools/source/val/construct.cpp index 251e2bbae..52e61d555 100644 --- a/3rdparty/spirv-tools/source/val/construct.cpp +++ b/3rdparty/spirv-tools/source/val/construct.cpp @@ -70,60 +70,45 @@ BasicBlock* Construct::exit_block() { return exit_block_; } void Construct::set_exit(BasicBlock* block) { exit_block_ = block; } -Construct::ConstructBlockSet Construct::blocks(Function* function) const { - auto header = entry_block(); - auto merge = exit_block(); - assert(header); - int header_depth = function->GetBlockDepth(const_cast(header)); - ConstructBlockSet construct_blocks; - std::unordered_set corresponding_headers; - for (auto& other : corresponding_constructs()) { - // The corresponding header can be the same block as this construct's - // header for loops with no loop construct. In those cases, don't add the - // loop header as it prevents finding any blocks in the construct. - if (type() != ConstructType::kContinue || other->entry_block() != header) { - corresponding_headers.insert(other->entry_block()); - } +Construct::ConstructBlockSet Construct::blocks(Function* /*function*/) const { + const auto header = entry_block(); + const auto exit = exit_block(); + const bool is_continue = type() == ConstructType::kContinue; + const bool is_loop = type() == ConstructType::kLoop; + const BasicBlock* continue_header = nullptr; + if (is_loop) { + // The only corresponding construct for a loop is the continue. + continue_header = (*corresponding_constructs().begin())->entry_block(); } std::vector stack; stack.push_back(const_cast(header)); + ConstructBlockSet construct_blocks; while (!stack.empty()) { - BasicBlock* block = stack.back(); + auto* block = stack.back(); stack.pop_back(); - if (merge == block && ExitBlockIsMergeBlock()) { - // Merge block is not part of the construct. - continue; - } + if (header->structurally_dominates(*block)) { + bool include = false; + if (is_continue && exit->structurally_postdominates(*block)) { + // Continue construct include blocks dominated by the continue target + // and post-dominated by the back-edge block. + include = true; + } else if (!exit->structurally_dominates(*block)) { + // Selection and loop constructs include blocks dominated by the header + // and not dominated by the merge. + include = true; + if (is_loop && continue_header->structurally_dominates(*block)) { + // Loop constructs have an additional constraint that they do not + // include blocks dominated by the continue construct. Since all + // blocks in the continue construct are dominated by the continue + // target, we just test for dominance by continue target. + include = false; + } + } + if (include) { + if (!construct_blocks.insert(block).second) continue; - if (corresponding_headers.count(block)) { - // Entered a corresponding construct. - continue; - } - - int block_depth = function->GetBlockDepth(block); - if (block_depth < header_depth) { - // Broke to outer construct. - continue; - } - - // In a loop, the continue target is at a depth of the loop construct + 1. - // A selection construct nested directly within the loop construct is also - // at the same depth. It is valid, however, to branch directly to the - // continue target from within the selection construct. - if (block != header && block_depth == header_depth && - type() == ConstructType::kSelection && - block->is_type(kBlockTypeContinue)) { - // Continued to outer construct. - continue; - } - - if (!construct_blocks.insert(block).second) continue; - - if (merge != block) { - for (auto succ : *block->successors()) { - // All blocks in the construct must be dominated by the header. - if (header->dominates(*succ)) { + for (auto succ : *block->structural_successors()) { stack.push_back(succ); } } @@ -181,11 +166,12 @@ bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const { for (auto& use : block->label()->uses()) { if ((use.first->opcode() == SpvOpLoopMerge || use.first->opcode() == SpvOpSelectionMerge) && - use.second == 1 && use.first->block()->dominates(*block)) { + use.second == 1 && + use.first->block()->structurally_dominates(*block)) { return use.first->block(); } } - return block->immediate_dominator(); + return block->immediate_structural_dominator(); }; bool seen_switch = false; @@ -201,7 +187,7 @@ bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const { terminator->opcode() == SpvOpSwitch)) { auto merge_target = merge_inst->GetOperandAs(0u); auto merge_block = merge_inst->function()->GetBlock(merge_target).first; - if (merge_block->dominates(*header)) { + if (merge_block->structurally_dominates(*header)) { block = NextBlock(block); continue; } diff --git a/3rdparty/spirv-tools/source/val/decoration.h b/3rdparty/spirv-tools/source/val/decoration.h index ed3320f87..4f53f2075 100644 --- a/3rdparty/spirv-tools/source/val/decoration.h +++ b/3rdparty/spirv-tools/source/val/decoration.h @@ -69,6 +69,15 @@ class Decoration { std::vector& params() { return params_; } const std::vector& params() const { return params_; } + inline bool operator<(const Decoration& rhs) const { + // Note: Sort by struct_member_index_ first, then type, so look up can be + // efficient using lower_bound() and upper_bound(). + if (struct_member_index_ < rhs.struct_member_index_) return true; + if (rhs.struct_member_index_ < struct_member_index_) return false; + if (dec_type_ < rhs.dec_type_) return true; + if (rhs.dec_type_ < dec_type_) return false; + return params_ < rhs.params_; + } inline bool operator==(const Decoration& rhs) const { return (dec_type_ == rhs.dec_type_ && params_ == rhs.params_ && struct_member_index_ == rhs.struct_member_index_); diff --git a/3rdparty/spirv-tools/source/val/function.cpp b/3rdparty/spirv-tools/source/val/function.cpp index f3292b0e7..fc7ccd062 100644 --- a/3rdparty/spirv-tools/source/val/function.cpp +++ b/3rdparty/spirv-tools/source/val/function.cpp @@ -73,6 +73,8 @@ spv_result_t Function::RegisterLoopMerge(uint32_t merge_id, BasicBlock& continue_target_block = blocks_.at(continue_id); assert(current_block_ && "RegisterLoopMerge must be called when called within a block"); + current_block_->RegisterStructuralSuccessor(&merge_block); + current_block_->RegisterStructuralSuccessor(&continue_target_block); current_block_->set_type(kBlockTypeLoop); merge_block.set_type(kBlockTypeMerge); @@ -101,6 +103,7 @@ spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) { current_block_->set_type(kBlockTypeSelection); merge_block.set_type(kBlockTypeMerge); merge_block_header_[&merge_block] = current_block_; + current_block_->RegisterStructuralSuccessor(&merge_block); AddConstruct({ConstructType::kSelection, current_block(), &merge_block}); @@ -251,16 +254,6 @@ Function::GetBlocksFunction Function::AugmentedCFGSuccessorsFunction() const { }; } -Function::GetBlocksFunction -Function::AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const { - return [this](const BasicBlock* block) { - auto where = loop_header_successors_plus_continue_target_map_.find(block); - return where == loop_header_successors_plus_continue_target_map_.end() - ? AugmentedCFGSuccessorsFunction()(block) - : &(*where).second; - }; -} - Function::GetBlocksFunction Function::AugmentedCFGPredecessorsFunction() const { return [this](const BasicBlock* block) { auto where = augmented_predecessors_map_.find(block); @@ -269,11 +262,35 @@ Function::GetBlocksFunction Function::AugmentedCFGPredecessorsFunction() const { }; } +Function::GetBlocksFunction Function::AugmentedStructuralCFGSuccessorsFunction() + const { + return [this](const BasicBlock* block) { + auto where = augmented_successors_map_.find(block); + return where == augmented_successors_map_.end() + ? block->structural_successors() + : &(*where).second; + }; +} + +Function::GetBlocksFunction +Function::AugmentedStructuralCFGPredecessorsFunction() const { + return [this](const BasicBlock* block) { + auto where = augmented_predecessors_map_.find(block); + return where == augmented_predecessors_map_.end() + ? block->structural_predecessors() + : &(*where).second; + }; +} + void Function::ComputeAugmentedCFG() { // Compute the successors of the pseudo-entry block, and // the predecessors of the pseudo exit block. - auto succ_func = [](const BasicBlock* b) { return b->successors(); }; - auto pred_func = [](const BasicBlock* b) { return b->predecessors(); }; + auto succ_func = [](const BasicBlock* b) { + return b->structural_successors(); + }; + auto pred_func = [](const BasicBlock* b) { + return b->structural_predecessors(); + }; CFA::ComputeAugmentedCFG( ordered_blocks_, &pseudo_entry_block_, &pseudo_exit_block_, &augmented_successors_map_, &augmented_predecessors_map_, succ_func, diff --git a/3rdparty/spirv-tools/source/val/function.h b/3rdparty/spirv-tools/source/val/function.h index 2fe30bdc8..126b1dc77 100644 --- a/3rdparty/spirv-tools/source/val/function.h +++ b/3rdparty/spirv-tools/source/val/function.h @@ -184,12 +184,12 @@ class Function { std::function*(const BasicBlock*)>; /// Returns the block successors function for the augmented CFG. GetBlocksFunction AugmentedCFGSuccessorsFunction() const; - /// Like AugmentedCFGSuccessorsFunction, but also includes a forward edge from - /// a loop header block to its continue target, if they are different blocks. - GetBlocksFunction - AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const; /// Returns the block predecessors function for the augmented CFG. GetBlocksFunction AugmentedCFGPredecessorsFunction() const; + /// Returns the block structural successors function for the augmented CFG. + GetBlocksFunction AugmentedStructuralCFGSuccessorsFunction() const; + /// Returns the block structural predecessors function for the augmented CFG. + GetBlocksFunction AugmentedStructuralCFGPredecessorsFunction() const; /// Returns the control flow nesting depth of the given basic block. /// This function only works when you have structured control flow. diff --git a/3rdparty/spirv-tools/source/val/validate_annotation.cpp b/3rdparty/spirv-tools/source/val/validate_annotation.cpp index bef7ef9c5..c27c79943 100644 --- a/3rdparty/spirv-tools/source/val/validate_annotation.cpp +++ b/3rdparty/spirv-tools/source/val/validate_annotation.cpp @@ -136,8 +136,8 @@ std::string LogStringForDecoration(uint32_t decoration) { return "PerViewNV"; case SpvDecorationPerTaskNV: return "PerTaskNV"; - case SpvDecorationPerVertexNV: - return "PerVertexNV"; + case SpvDecorationPerVertexKHR: + return "PerVertexKHR"; case SpvDecorationNonUniform: return "NonUniform"; case SpvDecorationRestrictPointer: @@ -366,6 +366,11 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, return fail(4670) << "storage class must be Input or Output"; } break; + case SpvDecorationPerVertexKHR: + if (sc != SpvStorageClassInput) { + return fail(6777) << "storage class must be Input"; + } + break; default: break; } @@ -576,7 +581,7 @@ spv_result_t RegisterDecorations(ValidationState_t& _, // Word 1 is the group . All subsequent words are target s that // are going to be decorated with the decorations. const uint32_t decoration_group_id = inst->word(1); - std::vector& group_decorations = + std::set& group_decorations = _.id_decorations(decoration_group_id); for (size_t i = 2; i < inst->words().size(); ++i) { const uint32_t target_id = inst->word(i); @@ -590,7 +595,7 @@ spv_result_t RegisterDecorations(ValidationState_t& _, // pairs. All decorations of the group should be applied to all the struct // members that are specified in the instructions. const uint32_t decoration_group_id = inst->word(1); - std::vector& group_decorations = + std::set& group_decorations = _.id_decorations(decoration_group_id); // Grammar checks ensures that the number of arguments to this instruction // is an odd number: 1 decoration group + (id,literal) pairs. diff --git a/3rdparty/spirv-tools/source/val/validate_builtins.cpp b/3rdparty/spirv-tools/source/val/validate_builtins.cpp index 0ee2b0978..379705ac0 100644 --- a/3rdparty/spirv-tools/source/val/validate_builtins.cpp +++ b/3rdparty/spirv-tools/source/val/validate_builtins.cpp @@ -120,7 +120,7 @@ typedef enum VUIDError_ { VUIDErrorMax, } VUIDError; -const static uint32_t NumVUIDBuiltins = 34; +const static uint32_t NumVUIDBuiltins = 36; typedef struct { SpvBuiltIn builtIn; @@ -163,6 +163,8 @@ std::array builtinVUIDInfo = {{ {SpvBuiltInFragStencilRefEXT, {4223, 4224, 4225}}, {SpvBuiltInFullyCoveredEXT, {4232, 4233, 4234}}, {SpvBuiltInCullMaskKHR, {6735, 6736, 6737}}, + {SpvBuiltInBaryCoordKHR, {4154, 4155, 4156}}, + {SpvBuiltInBaryCoordNoPerspKHR, {4160, 4161, 4162}}, // clang-format off } }; @@ -333,7 +335,9 @@ class BuiltInsValidator { const Decoration& decoration, const Instruction& inst); spv_result_t ValidateSMBuiltinsAtDefinition(const Decoration& decoration, const Instruction& inst); - + // Used for BaryCoord, BaryCoordNoPersp. + spv_result_t ValidateFragmentShaderF32Vec3InputAtDefinition( + const Decoration& decoration, const Instruction& inst); // Used for SubgroupEqMask, SubgroupGeMask, SubgroupGtMask, SubgroupLtMask, // SubgroupLeMask. spv_result_t ValidateI32Vec4InputAtDefinition(const Decoration& decoration, @@ -511,6 +515,13 @@ class BuiltInsValidator { const Decoration& decoration, const Instruction& built_in_inst, const Instruction& referenced_inst, const Instruction& referenced_from_inst); + + // Used for BaryCoord, BaryCoordNoPersp. + spv_result_t ValidateFragmentShaderF32Vec3InputAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + // Used for SubgroupId and NumSubgroups. spv_result_t ValidateComputeI32InputAtReference( const Decoration& decoration, const Instruction& built_in_inst, @@ -2790,6 +2801,80 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference( return SPV_SUCCESS; } +spv_result_t BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); + if (spv_result_t error = ValidateF32Vec( + decoration, inst, 3, + [this, &inst, builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + builtin) + << " variable needs to be a 3-component 32-bit float " + "vector. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateFragmentShaderF32Vec3InputAtReference(decoration, inst, inst, + inst); +} + +spv_result_t BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelFragment) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) + << " to be used only with Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtReference, this, + decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtDefinition( const Decoration& decoration, const Instruction& inst) { if (spvIsVulkanEnv(_.context()->target_env)) { @@ -4030,6 +4115,10 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( case SpvBuiltInWorkgroupId: { return ValidateComputeShaderI32Vec3InputAtDefinition(decoration, inst); } + case SpvBuiltInBaryCoordKHR: + case SpvBuiltInBaryCoordNoPerspKHR: { + return ValidateFragmentShaderF32Vec3InputAtDefinition(decoration, inst); + } case SpvBuiltInHelperInvocation: { return ValidateHelperInvocationAtDefinition(decoration, inst); } @@ -4186,8 +4275,6 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( case SpvBuiltInLayerPerViewNV: case SpvBuiltInMeshViewCountNV: case SpvBuiltInMeshViewIndicesNV: - case SpvBuiltInBaryCoordNV: - case SpvBuiltInBaryCoordNoPerspNV: case SpvBuiltInCurrentRayTimeNV: // No validation rules (for the moment). break; diff --git a/3rdparty/spirv-tools/source/val/validate_cfg.cpp b/3rdparty/spirv-tools/source/val/validate_cfg.cpp index dd605d2e3..d4ca20e5d 100644 --- a/3rdparty/spirv-tools/source/val/validate_cfg.cpp +++ b/3rdparty/spirv-tools/source/val/validate_cfg.cpp @@ -466,7 +466,7 @@ spv_result_t FindCaseFallThrough( std::vector stack; stack.push_back(target_block); std::unordered_set visited; - bool target_reachable = target_block->reachable(); + bool target_reachable = target_block->structurally_reachable(); int target_depth = function->GetBlockDepth(target_block); while (!stack.empty()) { auto block = stack.back(); @@ -476,8 +476,8 @@ spv_result_t FindCaseFallThrough( if (!visited.insert(block).second) continue; - if (target_reachable && block->reachable() && - target_block->dominates(*block)) { + if (target_reachable && block->structurally_reachable() && + target_block->structurally_dominates(*block)) { // Still in the case construct. for (auto successor : *block->successors()) { stack.push_back(successor); @@ -549,11 +549,12 @@ spv_result_t StructuredSwitchChecks(ValidationState_t& _, Function* function, if (seen_iter == seen_to_fall_through.end()) { const auto target_block = function->GetBlock(target).first; // OpSwitch must dominate all its case constructs. - if (header->reachable() && target_block->reachable() && - !header->dominates(*target_block)) { + if (header->structurally_reachable() && + target_block->structurally_reachable() && + !header->structurally_dominates(*target_block)) { return _.diag(SPV_ERROR_INVALID_CFG, header->label()) << "Selection header " << _.getIdName(header->id()) - << " does not dominate its case construct " + << " does not structurally dominate its case construct " << _.getIdName(target); } @@ -653,7 +654,7 @@ spv_result_t ValidateStructuredSelections( } // Skip unreachable blocks. - if (!block->reachable()) continue; + if (!block->structurally_reachable()) continue; if (terminator->opcode() == SpvOpBranchConditional) { const auto true_label = terminator->GetOperandAs(1); @@ -708,7 +709,7 @@ spv_result_t StructuredControlFlowChecks( // Check the loop headers have exactly one back-edge branching to it for (BasicBlock* loop_header : function->ordered_blocks()) { - if (!loop_header->reachable()) continue; + if (!loop_header->structurally_reachable()) continue; if (!loop_header->is_type(kBlockTypeLoop)) continue; auto loop_header_id = loop_header->id(); auto num_latch_blocks = loop_latch_blocks[loop_header_id].size(); @@ -723,9 +724,10 @@ spv_result_t StructuredControlFlowChecks( // Check construct rules for (const Construct& construct : function->constructs()) { auto header = construct.entry_block(); + if (!header->structurally_reachable()) continue; auto merge = construct.exit_block(); - if (header->reachable() && !merge) { + if (!merge) { std::string construct_name, header_name, exit_name; std::tie(construct_name, header_name, exit_name) = ConstructNames(construct.type()); @@ -735,32 +737,31 @@ spv_result_t StructuredControlFlowChecks( exit_name + ". This may be a bug in the validator."; } - // If the exit block is reachable then it's dominated by the - // header. - if (merge && merge->reachable()) { - if (!header->dominates(*merge)) { - return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id())) - << ConstructErrorString(construct, _.getIdName(header->id()), - _.getIdName(merge->id()), - "does not dominate"); - } - // If it's really a merge block for a selection or loop, then it must be - // *strictly* dominated by the header. - if (construct.ExitBlockIsMergeBlock() && (header == merge)) { - return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id())) - << ConstructErrorString(construct, _.getIdName(header->id()), - _.getIdName(merge->id()), - "does not strictly dominate"); - } + // If the header is reachable, the merge is guaranteed to be structurally + // reachable. + if (!header->structurally_dominates(*merge)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id())) + << ConstructErrorString(construct, _.getIdName(header->id()), + _.getIdName(merge->id()), + "does not structurally dominate"); } + // If it's really a merge block for a selection or loop, then it must be + // *strictly* structrually dominated by the header. + if (construct.ExitBlockIsMergeBlock() && (header == merge)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id())) + << ConstructErrorString(construct, _.getIdName(header->id()), + _.getIdName(merge->id()), + "does not strictly structurally dominate"); + } + // Check post-dominance for continue constructs. But dominance and // post-dominance only make sense when the construct is reachable. - if (header->reachable() && construct.type() == ConstructType::kContinue) { - if (!merge->postdominates(*header)) { + if (construct.type() == ConstructType::kContinue) { + if (!merge->structurally_postdominates(*header)) { return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id())) << ConstructErrorString(construct, _.getIdName(header->id()), _.getIdName(merge->id()), - "is not post dominated by"); + "is not structurally post dominated by"); } } @@ -771,7 +772,7 @@ spv_result_t StructuredControlFlowChecks( for (auto block : construct_blocks) { // Check that all exits from the construct are via structured exits. for (auto succ : *block->successors()) { - if (block->reachable() && !construct_blocks.count(succ) && + if (!construct_blocks.count(succ) && !construct.IsStructuredExit(_, succ)) { return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) << "block " << _.getIdName(block->id()) << " exits the " @@ -784,7 +785,7 @@ spv_result_t StructuredControlFlowChecks( // Check that for all non-header blocks, all predecessors are within this // construct. for (auto pred : *block->predecessors()) { - if (pred->reachable() && !construct_blocks.count(pred)) { + if (pred->structurally_reachable() && !construct_blocks.count(pred)) { return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(pred->id())) << "block " << pred->id() << " branches to the " << construct_name << " construct, but not to the " @@ -800,7 +801,7 @@ spv_result_t StructuredControlFlowChecks( merge_inst.opcode() == SpvOpLoopMerge) { uint32_t merge_id = merge_inst.GetOperandAs(0); auto merge_block = function->GetBlock(merge_id).first; - if (merge_block->reachable() && + if (merge_block->structurally_reachable() && !construct_blocks.count(merge_block)) { return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) << "Header block " << _.getIdName(block->id()) @@ -813,6 +814,43 @@ spv_result_t StructuredControlFlowChecks( } } + if (construct.type() == ConstructType::kLoop) { + // If the continue target differs from the loop header, then check that + // all edges into the continue construct come from within the loop. + const auto index = header->terminator() - &_.ordered_instructions()[0]; + const auto& merge_inst = _.ordered_instructions()[index - 1]; + const auto continue_id = merge_inst.GetOperandAs(1); + const auto* continue_inst = _.FindDef(continue_id); + // OpLabel instructions aren't stored as part of the basic block for + // legacy reaasons. Grab the next instruction and use it's block pointer + // instead. + const auto next_index = + (continue_inst - &_.ordered_instructions()[0]) + 1; + const auto& next_inst = _.ordered_instructions()[next_index]; + const auto* continue_target = next_inst.block(); + if (header->id() != continue_id) { + for (auto pred : *continue_target->predecessors()) { + // Ignore back-edges from within the continue construct. + bool is_back_edge = false; + for (auto back_edge : back_edges) { + uint32_t back_edge_block; + uint32_t header_block; + std::tie(back_edge_block, header_block) = back_edge; + if (header_block == continue_id && back_edge_block == pred->id()) + is_back_edge = true; + } + if (!construct_blocks.count(pred) && !is_back_edge) { + return _.diag(SPV_ERROR_INVALID_CFG, pred->terminator()) + << "Block " << _.getIdName(pred->id()) + << " branches to the loop continue target " + << _.getIdName(continue_id) + << ", but is not contained in the associated loop construct " + << _.getIdName(header->id()); + } + } + } + } + // Checks rules for case constructs. if (construct.type() == ConstructType::kSelection && header->terminator()->opcode() == SpvOpSwitch) { @@ -850,52 +888,28 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) { << _.getIdName(function.id()); } - // Set each block's immediate dominator and immediate postdominator, - // and find all back-edges. + // Set each block's immediate dominator. // // We want to analyze all the blocks in the function, even in degenerate // control flow cases including unreachable blocks. So use the augmented // CFG to ensure we cover all the blocks. std::vector postorder; - std::vector postdom_postorder; - std::vector> back_edges; auto ignore_block = [](const BasicBlock*) {}; auto ignore_edge = [](const BasicBlock*, const BasicBlock*) {}; + auto no_terminal_blocks = [](const BasicBlock*) { return false; }; if (!function.ordered_blocks().empty()) { /// calculate dominators CFA::DepthFirstTraversal( function.first_block(), function.AugmentedCFGSuccessorsFunction(), ignore_block, [&](const BasicBlock* b) { postorder.push_back(b); }, - ignore_edge); + ignore_edge, no_terminal_blocks); auto edges = CFA::CalculateDominators( postorder, function.AugmentedCFGPredecessorsFunction()); for (auto edge : edges) { if (edge.first != edge.second) edge.first->SetImmediateDominator(edge.second); } - - /// calculate post dominators - CFA::DepthFirstTraversal( - function.pseudo_exit_block(), - function.AugmentedCFGPredecessorsFunction(), ignore_block, - [&](const BasicBlock* b) { postdom_postorder.push_back(b); }, - ignore_edge); - auto postdom_edges = CFA::CalculateDominators( - postdom_postorder, function.AugmentedCFGSuccessorsFunction()); - for (auto edge : postdom_edges) { - edge.first->SetImmediatePostDominator(edge.second); - } - /// calculate back edges. - CFA::DepthFirstTraversal( - function.pseudo_entry_block(), - function - .AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge(), - ignore_block, ignore_block, - [&](const BasicBlock* from, const BasicBlock* to) { - back_edges.emplace_back(from->id(), to->id()); - }); } - UpdateContinueConstructExitBlocks(function, back_edges); auto& blocks = function.ordered_blocks(); if (!blocks.empty()) { @@ -929,6 +943,52 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) { /// Structured control flow checks are only required for shader capabilities if (_.HasCapability(SpvCapabilityShader)) { + // Calculate structural dominance. + postorder.clear(); + std::vector postdom_postorder; + std::vector> back_edges; + if (!function.ordered_blocks().empty()) { + /// calculate dominators + CFA::DepthFirstTraversal( + function.first_block(), + function.AugmentedStructuralCFGSuccessorsFunction(), ignore_block, + [&](const BasicBlock* b) { postorder.push_back(b); }, ignore_edge, + no_terminal_blocks); + auto edges = CFA::CalculateDominators( + postorder, function.AugmentedStructuralCFGPredecessorsFunction()); + for (auto edge : edges) { + if (edge.first != edge.second) + edge.first->SetImmediateStructuralDominator(edge.second); + } + + /// calculate post dominators + CFA::DepthFirstTraversal( + function.pseudo_exit_block(), + function.AugmentedStructuralCFGPredecessorsFunction(), ignore_block, + [&](const BasicBlock* b) { postdom_postorder.push_back(b); }, + ignore_edge, no_terminal_blocks); + auto postdom_edges = CFA::CalculateDominators( + postdom_postorder, + function.AugmentedStructuralCFGSuccessorsFunction()); + for (auto edge : postdom_edges) { + edge.first->SetImmediateStructuralPostDominator(edge.second); + } + /// calculate back edges. + CFA::DepthFirstTraversal( + function.pseudo_entry_block(), + function.AugmentedStructuralCFGSuccessorsFunction(), ignore_block, + ignore_block, + [&](const BasicBlock* from, const BasicBlock* to) { + // A back edge must be a real edge. Since the augmented successors + // contain structural edges, filter those from consideration. + for (const auto* succ : *(from->successors())) { + if (succ == to) back_edges.emplace_back(from->id(), to->id()); + } + }, + no_terminal_blocks); + } + UpdateContinueConstructExitBlocks(function, back_edges); + if (auto error = StructuredControlFlowChecks(_, &function, back_edges, postorder)) return error; @@ -1054,6 +1114,26 @@ void ReachabilityPass(ValidationState_t& _) { } } } + + // Repeat for structural reachability. + for (auto& f : _.functions()) { + std::vector stack; + auto entry = f.first_block(); + // Skip function declarations. + if (entry) stack.push_back(entry); + + while (!stack.empty()) { + auto block = stack.back(); + stack.pop_back(); + + if (block->structurally_reachable()) continue; + + block->set_structurally_reachable(true); + for (auto succ : *block->structural_successors()) { + stack.push_back(succ); + } + } + } } spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) { diff --git a/3rdparty/spirv-tools/source/val/validate_decorations.cpp b/3rdparty/spirv-tools/source/val/validate_decorations.cpp index 73d512a8f..986f32953 100644 --- a/3rdparty/spirv-tools/source/val/validate_decorations.cpp +++ b/3rdparty/spirv-tools/source/val/validate_decorations.cpp @@ -346,10 +346,13 @@ uint32_t getSize(uint32_t member_id, const LayoutConstraints& inherited, const auto& lastMember = members.back(); uint32_t offset = 0xffffffff; // Find the offset of the last element and add the size. - for (auto& decoration : vstate.id_decorations(member_id)) { - if (SpvDecorationOffset == decoration.dec_type() && - decoration.struct_member_index() == (int)lastIdx) { - offset = decoration.params()[0]; + auto member_decorations = + vstate.id_member_decorations(member_id, lastIdx); + for (auto decoration = member_decorations.begin; + decoration != member_decorations.end; ++decoration) { + assert(decoration->struct_member_index() == (int)lastIdx); + if (SpvDecorationOffset == decoration->dec_type()) { + offset = decoration->params()[0]; } } // This check depends on the fact that all members have offsets. This @@ -445,15 +448,17 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size()); memberIdx < numMembers; memberIdx++) { uint32_t offset = 0xffffffff; - for (auto& decoration : vstate.id_decorations(struct_id)) { - if (decoration.struct_member_index() == (int)memberIdx) { - switch (decoration.dec_type()) { - case SpvDecorationOffset: - offset = decoration.params()[0]; - break; - default: - break; - } + auto member_decorations = + vstate.id_member_decorations(struct_id, memberIdx); + for (auto decoration = member_decorations.begin; + decoration != member_decorations.end; ++decoration) { + assert(decoration->struct_member_index() == (int)memberIdx); + switch (decoration->dec_type()) { + case SpvDecorationOffset: + offset = decoration->params()[0]; + break; + default: + break; } } member_offsets.push_back( @@ -806,6 +811,56 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { ++num_workgroup_variables_with_aliased; } } + + if (spvIsVulkanEnv(vstate.context()->target_env)) { + const auto* models = vstate.GetExecutionModels(entry_point); + const bool has_frag = + models->find(SpvExecutionModelFragment) != models->end(); + const bool has_vert = + models->find(SpvExecutionModelVertex) != models->end(); + for (const auto& decoration : + vstate.id_decorations(var_instr->id())) { + if (decoration == SpvDecorationFlat || + decoration == SpvDecorationNoPerspective || + decoration == SpvDecorationSample || + decoration == SpvDecorationCentroid) { + // VUID 04670 already validates these decorations are input/output + if (storage_class == SpvStorageClassInput && + (models->size() > 1 || has_vert)) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << vstate.VkErrorID(6202) + << "OpEntryPoint interfaces variable must not be vertex " + "execution model with an input storage class for " + "Entry Point id " + << entry_point << "."; + } else if (storage_class == SpvStorageClassOutput && + (models->size() > 1 || has_frag)) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << vstate.VkErrorID(6201) + << "OpEntryPoint interfaces variable must not be " + "fragment " + "execution model with an output storage class for " + "Entry Point id " + << entry_point << "."; + } + } + } + + const bool has_flat = + hasDecoration(var_instr->id(), SpvDecorationFlat, vstate); + if (has_frag && storage_class == SpvStorageClassInput && !has_flat && + ((vstate.IsFloatScalarType(type_id) && + vstate.GetBitWidth(type_id) == 64) || + vstate.IsIntScalarOrVectorType(type_id))) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << vstate.VkErrorID(4744) + << "Fragment OpEntryPoint operand " + << interface << " with Input interfaces with integer or " + "float type must have a Flat decoration " + "for Entry Point id " + << entry_point << "."; + } + } } if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1) { return vstate.diag(SPV_ERROR_INVALID_BINARY, @@ -878,21 +933,23 @@ void ComputeMemberConstraintsForStruct(MemberConstraints* constraints, LayoutConstraints& constraint = (*constraints)[std::make_pair(struct_id, memberIdx)]; constraint = inherited; - for (auto& decoration : vstate.id_decorations(struct_id)) { - if (decoration.struct_member_index() == (int)memberIdx) { - switch (decoration.dec_type()) { - case SpvDecorationRowMajor: - constraint.majorness = kRowMajor; - break; - case SpvDecorationColMajor: - constraint.majorness = kColumnMajor; - break; - case SpvDecorationMatrixStride: - constraint.matrix_stride = decoration.params()[0]; - break; - default: - break; - } + auto member_decorations = + vstate.id_member_decorations(struct_id, memberIdx); + for (auto decoration = member_decorations.begin; + decoration != member_decorations.end; ++decoration) { + assert(decoration->struct_member_index() == (int)memberIdx); + switch (decoration->dec_type()) { + case SpvDecorationRowMajor: + constraint.majorness = kRowMajor; + break; + case SpvDecorationColMajor: + constraint.majorness = kColumnMajor; + break; + case SpvDecorationMatrixStride: + constraint.matrix_stride = decoration->params()[0]; + break; + default: + break; } } @@ -1654,6 +1711,24 @@ spv_result_t CheckLocationDecoration(ValidationState_t& vstate, "of a structure type"; } +spv_result_t CheckRelaxPrecisionDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + // This is not the most precise check, but the rules for RelaxPrecision are + // very general, and it will be difficult to implement precisely. For now, + // I will only check for the cases that cause problems for the optimizer. + if (!spvOpcodeGeneratesType(inst.opcode())) { + return SPV_SUCCESS; + } + + if (decoration.struct_member_index() != Decoration::kInvalidMember && + inst.opcode() == SpvOpTypeStruct) { + return SPV_SUCCESS; + } + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "RelaxPrecision decoration cannot be applied to a type"; +} + #define PASS_OR_BAIL_AT_LINE(X, LINE) \ { \ spv_result_t e##LINE = (X); \ @@ -1708,6 +1783,10 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) { case SpvDecorationLocation: PASS_OR_BAIL(CheckLocationDecoration(vstate, *inst, decoration)); break; + case SpvDecorationRelaxedPrecision: + PASS_OR_BAIL( + CheckRelaxPrecisionDecoration(vstate, *inst, decoration)); + break; default: break; } diff --git a/3rdparty/spirv-tools/source/val/validate_interfaces.cpp b/3rdparty/spirv-tools/source/val/validate_interfaces.cpp index adf2e4724..7f2d6488c 100644 --- a/3rdparty/spirv-tools/source/val/validate_interfaces.cpp +++ b/3rdparty/spirv-tools/source/val/validate_interfaces.cpp @@ -238,7 +238,7 @@ spv_result_t GetLocationsForVariable( uint32_t index = 0; bool has_patch = false; bool has_per_task_nv = false; - bool has_per_vertex_nv = false; + bool has_per_vertex_khr = false; for (auto& dec : _.id_decorations(variable->id())) { if (dec.dec_type() == SpvDecorationLocation) { if (has_location && dec.params()[0] != location) { @@ -272,8 +272,20 @@ spv_result_t GetLocationsForVariable( has_patch = true; } else if (dec.dec_type() == SpvDecorationPerTaskNV) { has_per_task_nv = true; - } else if (dec.dec_type() == SpvDecorationPerVertexNV) { - has_per_vertex_nv = true; + } else if (dec.dec_type() == SpvDecorationPerVertexKHR) { + if (!is_fragment) { + return _.diag(SPV_ERROR_INVALID_DATA, variable) + << _.VkErrorID(6777) + << "PerVertexKHR can only be applied to Fragment Execution " + "Models"; + } + if (type->opcode() != SpvOpTypeArray && + type->opcode() != SpvOpTypeRuntimeArray) { + return _.diag(SPV_ERROR_INVALID_DATA, variable) + << _.VkErrorID(6778) + << "PerVertexKHR must be declared as arrays"; + } + has_per_vertex_khr = true; } } @@ -298,7 +310,7 @@ spv_result_t GetLocationsForVariable( } break; case SpvExecutionModelFragment: - if (!is_output && has_per_vertex_nv) { + if (!is_output && has_per_vertex_khr) { is_arrayed = true; } break; diff --git a/3rdparty/spirv-tools/source/val/validate_memory.cpp b/3rdparty/spirv-tools/source/val/validate_memory.cpp index af9da678e..ee053f821 100644 --- a/3rdparty/spirv-tools/source/val/validate_memory.cpp +++ b/3rdparty/spirv-tools/source/val/validate_memory.cpp @@ -35,8 +35,8 @@ bool HaveLayoutCompatibleMembers(ValidationState_t&, const Instruction*, const Instruction*); bool HaveSameLayoutDecorations(ValidationState_t&, const Instruction*, const Instruction*); -bool HasConflictingMemberOffsets(const std::vector&, - const std::vector&); +bool HasConflictingMemberOffsets(const std::set&, + const std::set&); bool IsAllowedTypeOrArrayOfSame(ValidationState_t& _, const Instruction* type, std::initializer_list allowed) { @@ -105,10 +105,8 @@ bool HaveSameLayoutDecorations(ValidationState_t& _, const Instruction* type1, "type1 must be an OpTypeStruct instruction."); assert(type2->opcode() == SpvOpTypeStruct && "type2 must be an OpTypeStruct instruction."); - const std::vector& type1_decorations = - _.id_decorations(type1->id()); - const std::vector& type2_decorations = - _.id_decorations(type2->id()); + const std::set& type1_decorations = _.id_decorations(type1->id()); + const std::set& type2_decorations = _.id_decorations(type2->id()); // TODO: Will have to add other check for arrays an matricies if we want to // handle them. @@ -120,8 +118,8 @@ bool HaveSameLayoutDecorations(ValidationState_t& _, const Instruction* type1, } bool HasConflictingMemberOffsets( - const std::vector& type1_decorations, - const std::vector& type2_decorations) { + const std::set& type1_decorations, + const std::set& type2_decorations) { { // We are interested in conflicting decoration. If a decoration is in one // list but not the other, then we will assume the code is correct. We are @@ -526,8 +524,8 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { if (storage_class == SpvStorageClassPushConstant) { if (pointee->opcode() != SpvOpTypeStruct) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "PushConstant OpVariable '" << _.getIdName(inst->id()) - << "' has illegal type.\n" + << _.VkErrorID(6808) << "PushConstant OpVariable '" + << _.getIdName(inst->id()) << "' has illegal type.\n" << "From Vulkan spec, Push Constant Interface section:\n" << "Such variables must be typed as OpTypeStruct"; } @@ -554,9 +552,9 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { if (storage_class == SpvStorageClassUniform) { if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Uniform OpVariable '" << _.getIdName(inst->id()) - << "' has illegal type.\n" - << "From Vulkan spec, section 14.5.2:\n" + << _.VkErrorID(6807) << "Uniform OpVariable '" + << _.getIdName(inst->id()) << "' has illegal type.\n" + << "From Vulkan spec:\n" << "Variables identified with the Uniform storage class are " << "used to access transparent buffer backed resources. Such " << "variables must be typed as OpTypeStruct, or an array of " @@ -567,9 +565,9 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { if (storage_class == SpvStorageClassStorageBuffer) { if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "StorageBuffer OpVariable '" << _.getIdName(inst->id()) - << "' has illegal type.\n" - << "From Vulkan spec, section 14.5.2:\n" + << _.VkErrorID(6807) << "StorageBuffer OpVariable '" + << _.getIdName(inst->id()) << "' has illegal type.\n" + << "From Vulkan spec:\n" << "Variables identified with the StorageBuffer storage class " "are used to access transparent buffer backed resources. " "Such variables must be typed as OpTypeStruct, or an array " diff --git a/3rdparty/spirv-tools/source/val/validate_mode_setting.cpp b/3rdparty/spirv-tools/source/val/validate_mode_setting.cpp index 963526870..67b10c5ac 100644 --- a/3rdparty/spirv-tools/source/val/validate_mode_setting.cpp +++ b/3rdparty/spirv-tools/source/val/validate_mode_setting.cpp @@ -112,6 +112,44 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) { << "Fragment execution model entry points can specify at most " "one fragment shader interlock execution mode."; } + if (execution_modes && + 1 < std::count_if( + execution_modes->begin(), execution_modes->end(), + [](const SpvExecutionMode& mode) { + switch (mode) { + case SpvExecutionModeStencilRefUnchangedFrontAMD: + case SpvExecutionModeStencilRefLessFrontAMD: + case SpvExecutionModeStencilRefGreaterFrontAMD: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Fragment execution model entry points can specify at most " + "one of StencilRefUnchangedFrontAMD, " + "StencilRefLessFrontAMD or StencilRefGreaterFrontAMD " + "execution modes."; + } + if (execution_modes && + 1 < std::count_if( + execution_modes->begin(), execution_modes->end(), + [](const SpvExecutionMode& mode) { + switch (mode) { + case SpvExecutionModeStencilRefUnchangedBackAMD: + case SpvExecutionModeStencilRefLessBackAMD: + case SpvExecutionModeStencilRefGreaterBackAMD: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Fragment execution model entry points can specify at most " + "one of StencilRefUnchangedBackAMD, " + "StencilRefLessBackAMD or StencilRefGreaterBackAMD " + "execution modes."; + } break; case SpvExecutionModelTessellationControl: case SpvExecutionModelTessellationEvaluation: @@ -412,6 +450,13 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _, case SpvExecutionModeSampleInterlockUnorderedEXT: case SpvExecutionModeShadingRateInterlockOrderedEXT: case SpvExecutionModeShadingRateInterlockUnorderedEXT: + case SpvExecutionModeEarlyAndLateFragmentTestsAMD: + case SpvExecutionModeStencilRefUnchangedFrontAMD: + case SpvExecutionModeStencilRefGreaterFrontAMD: + case SpvExecutionModeStencilRefLessFrontAMD: + case SpvExecutionModeStencilRefUnchangedBackAMD: + case SpvExecutionModeStencilRefGreaterBackAMD: + case SpvExecutionModeStencilRefLessBackAMD: if (!std::all_of(models->begin(), models->end(), [](const SpvExecutionModel& model) { return model == SpvExecutionModelFragment; diff --git a/3rdparty/spirv-tools/source/val/validation_state.cpp b/3rdparty/spirv-tools/source/val/validation_state.cpp index 9ae6d0768..811f92aee 100644 --- a/3rdparty/spirv-tools/source/val/validation_state.cpp +++ b/3rdparty/spirv-tools/source/val/validation_state.cpp @@ -1411,6 +1411,18 @@ std::string ValidationState_t::VkErrorID(uint32_t id, // Clang format adds spaces between hyphens // clang-format off switch (id) { + case 4154: + return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04154); + case 4155: + return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04155); + case 4156: + return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04156); + case 4160: + return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04160); + case 4161: + return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04161); + case 4162: + return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04162); case 4181: return VUID_WRAP(VUID-BaseInstance-BaseInstance-04181); case 4182: @@ -1866,7 +1878,7 @@ std::string ValidationState_t::VkErrorID(uint32_t id, case 4677: return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677); case 4680: - return VUID_WRAP( VUID-StandaloneSpirv-OpTypeRuntimeArray-04680); + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeRuntimeArray-04680); case 4682: return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682); case 6426: @@ -1891,6 +1903,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04733); case 4734: return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04734); + case 4744: + return VUID_WRAP(VUID-StandaloneSpirv-Flat-04744); case 4777: return VUID_WRAP(VUID-StandaloneSpirv-OpImage-04777); case 4780: @@ -1907,6 +1921,10 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-Location-04918); case 4919: return VUID_WRAP(VUID-StandaloneSpirv-Location-04919); + case 6201: + return VUID_WRAP(VUID-StandaloneSpirv-Flat-06201); + case 6202: + return VUID_WRAP(VUID-StandaloneSpirv-Flat-06202); case 6214: return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06214); case 6491: @@ -1925,6 +1943,14 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-UniformConstant-06677); case 6678: return VUID_WRAP(VUID-StandaloneSpirv-InputAttachmentIndex-06678); + case 6777: + return VUID_WRAP(VUID-StandaloneSpirv-PerVertexKHR-06777); + case 6778: + return VUID_WRAP(VUID-StandaloneSpirv-Input-06778); + case 6807: + return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06807); + case 6808: + return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06808); default: return ""; // unknown id } diff --git a/3rdparty/spirv-tools/source/val/validation_state.h b/3rdparty/spirv-tools/source/val/validation_state.h index 4888840bd..217804644 100644 --- a/3rdparty/spirv-tools/source/val/validation_state.h +++ b/3rdparty/spirv-tools/source/val/validation_state.h @@ -375,17 +375,14 @@ class ValidationState_t { /// Registers the decoration for the given void RegisterDecorationForId(uint32_t id, const Decoration& dec) { auto& dec_list = id_decorations_[id]; - auto lb = std::find(dec_list.begin(), dec_list.end(), dec); - if (lb == dec_list.end()) { - dec_list.push_back(dec); - } + dec_list.insert(dec); } /// Registers the list of decorations for the given template void RegisterDecorationsForId(uint32_t id, InputIt begin, InputIt end) { - std::vector& cur_decs = id_decorations_[id]; - cur_decs.insert(cur_decs.end(), begin, end); + std::set& cur_decs = id_decorations_[id]; + cur_decs.insert(begin, end); } /// Registers the list of decorations for the given member of the given @@ -394,21 +391,44 @@ class ValidationState_t { void RegisterDecorationsForStructMember(uint32_t struct_id, uint32_t member_index, InputIt begin, InputIt end) { - RegisterDecorationsForId(struct_id, begin, end); - for (auto& decoration : id_decorations_[struct_id]) { - decoration.set_struct_member_index(member_index); + std::set& cur_decs = id_decorations_[struct_id]; + for (InputIt iter = begin; iter != end; ++iter) { + Decoration dec = *iter; + dec.set_struct_member_index(member_index); + cur_decs.insert(dec); } } /// Returns all the decorations for the given . If no decorations exist - /// for the , it registers an empty vector for it in the map and - /// returns the empty vector. - std::vector& id_decorations(uint32_t id) { + /// for the , it registers an empty set for it in the map and + /// returns the empty set. + std::set& id_decorations(uint32_t id) { return id_decorations_[id]; } + /// Returns the range of decorations for the given field of the given . + struct FieldDecorationsIter { + std::set::const_iterator begin; + std::set::const_iterator end; + }; + FieldDecorationsIter id_member_decorations(uint32_t id, + uint32_t member_index) { + const auto& decorations = id_decorations_[id]; + + // The decorations are sorted by member_index, so this look up will give the + // exact range of decorations for this member index. + Decoration min_decoration((SpvDecoration)0, {}, member_index); + Decoration max_decoration(SpvDecorationMax, {}, member_index); + + FieldDecorationsIter result; + result.begin = decorations.lower_bound(min_decoration); + result.end = decorations.upper_bound(max_decoration); + + return result; + } + // Returns const pointer to the internal decoration container. - const std::map>& id_decorations() const { + const std::map>& id_decorations() const { return id_decorations_; } @@ -826,7 +846,7 @@ class ValidationState_t { struct_has_nested_blockorbufferblock_struct_; /// Stores the list of decorations for a given - std::map> id_decorations_; + std::map> id_decorations_; /// Stores type declarations which need to be unique (i.e. non-aggregates), /// in the form [opcode, operand words], result_id is not stored.