Updated spirv-tools.

This commit is contained in:
Бранимир Караџић 2022-07-10 10:49:09 -07:00
parent 7f9cdba788
commit 6c09cb564a
45 changed files with 1484 additions and 272 deletions

View File

@ -1 +1 @@
"v2022.3-dev", "SPIRV-Tools v2022.3-dev b8091498a3d8f2f7a46df2009d7c340eef1939b6"
"v2022.3-dev", "SPIRV-Tools v2022.3-dev 05862b9695f45c8a8810d69c52ced4440446c1b7"

View File

@ -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(

View File

@ -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,

View File

@ -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}}
};

View File

@ -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},

View File

@ -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<void(cbb_ptr)> preorder,
std::function<void(cbb_ptr)> postorder,
std::function<void(cbb_ptr, cbb_ptr)> backedge);
std::function<void(cbb_ptr, cbb_ptr)> backedge,
std::function<bool(cbb_ptr)> terminal);
/// @brief Calculates dominator edges for a set of blocks
///
@ -138,7 +141,8 @@ void CFA<BB>::DepthFirstTraversal(
const BB* entry, get_blocks_func successor_func,
std::function<void(cbb_ptr)> preorder,
std::function<void(cbb_ptr)> postorder,
std::function<void(cbb_ptr, cbb_ptr)> backedge) {
std::function<void(cbb_ptr, cbb_ptr)> backedge,
std::function<bool(cbb_ptr)> terminal) {
std::unordered_set<uint32_t> processed;
/// NOTE: work_list is the sequence of nodes from the root node to the node
@ -152,7 +156,7 @@ void CFA<BB>::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<BB*> CFA<BB>::TraversalRoots(const std::vector<BB*>& 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<BB*> result;

View File

@ -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,

View File

@ -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);

View File

@ -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",
});
}

View File

@ -74,6 +74,12 @@ void CFG::RemoveNonExistingEdges(uint32_t blk_id) {
void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root,
std::list<BasicBlock*>* order) {
ComputeStructuredOrder(func, root, nullptr, order);
}
void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root,
BasicBlock* end,
std::list<BasicBlock*>* 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<BasicBlock*>(b));
};
CFA<BasicBlock>::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<uint32_t> preheader_phi_ops;

View File

@ -66,6 +66,14 @@ class CFG {
void ComputeStructuredOrder(Function* func, BasicBlock* root,
std::list<BasicBlock*>* 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<BasicBlock*>* order);
// Applies |f| to all blocks that can be reach from |bb| in post order.
void ForEachBlockInPostOrder(BasicBlock* bb,
const std::function<void(BasicBlock*)>& f);

View File

@ -251,6 +251,193 @@ ConstantFoldingRule FoldVectorTimesScalar() {
};
}
ConstantFoldingRule FoldVectorTimesMatrix() {
return [](IRContext* context, Instruction* inst,
const std::vector<const analysis::Constant*>& 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<const analysis::Constant*> c1_components =
c1->GetVectorComponents(const_mgr);
std::vector<const analysis::Constant*> c2_components =
c2->AsMatrixConstant()->GetComponents();
uint32_t resultVectorSize = result_type->AsVector()->element_count();
std::vector<uint32_t> ids;
if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) {
std::vector<uint32_t> 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<float> result(result_scalar);
std::vector<uint32_t> 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<double> result(result_scalar);
std::vector<uint32_t> 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<const analysis::Constant*>& 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<const analysis::Constant*> c1_components =
c1->AsMatrixConstant()->GetComponents();
std::vector<const analysis::Constant*> c2_components =
c2->GetVectorComponents(const_mgr);
uint32_t resultVectorSize = result_type->AsVector()->element_count();
std::vector<uint32_t> ids;
if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) {
std::vector<uint32_t> 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<float> result(result_scalar);
std::vector<uint32_t> 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<double> result(result_scalar);
std::vector<uint32_t> 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());

View File

@ -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<BBType>::DepthFirstTraversal(bb, successors, pre, post, nop_backedge);
auto no_terminal_blocks = [](const BBType*) { return false; };
CFA<BBType>::DepthFirstTraversal(bb, successors, pre, post, nop_backedge,
no_terminal_blocks);
}
// Wrapper around CFA::DepthFirstTraversal to provide an interface to perform

View File

@ -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<uint32_t> words;
if (c->IsZero()) {
return 0;
}
if (width == 64) {
spvtools::utils::FloatProxy<double> 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<Operand> 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<const analysis::Constant*>&) {
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<const analysis::Constant*>& 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<Operand>(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<uint32_t>(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<uint32_t, uint32_t> GetInsertedValues(Instruction* inst) {
analysis::DefUseManager* def_use_mgr = inst->context()->get_def_use_mgr();
std::map<uint32_t, uint32_t> 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<uint32_t, uint32_t>& 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<uint32_t, uint32_t>& values_inserted,
Instruction* insert_before) {
InstructionBuilder ir_builder(
insert_before->context(), insert_before,
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
std::vector<uint32_t> 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<const analysis::Constant*>&) {
assert(inst->opcode() == SpvOpCompositeInsert &&
"Wrong opcode. Should be OpCompositeInsert.");
if (inst->NumInOperands() < 3) return false;
std::map<uint32_t, uint32_t> 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());

View File

@ -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.

View File

@ -508,6 +508,37 @@ void InlinePass::MoveLoopMergeInstToFirstBlock(
delete &*loop_merge_itr;
}
void InlinePass::UpdateSingleBlockLoopContinueTarget(
uint32_t new_id, std::vector<std::unique_ptr<BasicBlock>>* 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<BasicBlock> new_block =
MakeUnique<BasicBlock>(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<Instruction> 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<std::unique_ptr<BasicBlock>>* new_blocks,
std::vector<std::unique_ptr<Instruction>>* 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;

View File

@ -235,6 +235,12 @@ class InlinePass : public Pass {
// Move the OpLoopMerge from the last block back to the first.
void MoveLoopMergeInstToFirstBlock(
std::vector<std::unique_ptr<BasicBlock>>* 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<std::unique_ptr<BasicBlock>>* new_blocks);
};
} // namespace opt

View File

@ -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 {

View File

@ -212,8 +212,12 @@ void InterfaceVariableScalarReplacement::KillInstructionAndUsers(
context()->KillInst(inst);
return;
}
std::vector<Instruction*> 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);
}

View File

@ -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<uint32_t>(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

View File

@ -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();

View File

@ -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",
});
}

View File

@ -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) {

View File

@ -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<BasicBlock*> 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;

View File

@ -384,6 +384,7 @@ void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
std::unique_ptr<Instruction> new_label{new Instruction(
context_, SpvOp::SpvOpLabel, 0, context_->TakeNextId(), {})};
std::unique_ptr<BasicBlock> 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();

View File

@ -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

View File

@ -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<uint32_t>(

View File

@ -15,6 +15,7 @@
#ifndef SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_
#define SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_
#include <cassert>
#include <cstdio>
#include <memory>
#include <queue>
@ -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

View File

@ -16,6 +16,7 @@
#include <algorithm>
#include <cassert>
#include <climits>
#include <cstdint>
#include <sstream>
#include <string>
@ -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<uint64_t>(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_ &&

View File

@ -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

View File

@ -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();
}

View File

@ -64,9 +64,32 @@ class BasicBlock {
/// Returns the successors of the BasicBlock
std::vector<BasicBlock*>* successors() { return &successors_; }
/// Returns true if the block is reachable in the CFG
/// Returns the structural successors of the BasicBlock
std::vector<BasicBlock*>* structural_predecessors() {
return &structural_predecessors_;
}
/// Returns the structural predecessors of the BasicBlock
const std::vector<BasicBlock*>* structural_predecessors() const {
return &structural_predecessors_;
}
/// Returns the structural successors of the BasicBlock
std::vector<BasicBlock*>* structural_successors() {
return &structural_successors_;
}
/// Returns the structural predecessors of the BasicBlock
const std::vector<BasicBlock*>* 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<BasicBlock*> 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<BasicBlock*> structural_predecessors_;
std::vector<BasicBlock*> structural_successors_;
};
/// @brief Returns true if the iterators point to the same element or if both

View File

@ -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<BasicBlock*>(header));
ConstructBlockSet construct_blocks;
std::unordered_set<BasicBlock*> 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<BasicBlock*> stack;
stack.push_back(const_cast<BasicBlock*>(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<uint32_t>(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;
}

View File

@ -69,6 +69,15 @@ class Decoration {
std::vector<uint32_t>& params() { return params_; }
const std::vector<uint32_t>& 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_);

View File

@ -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<BasicBlock>::ComputeAugmentedCFG(
ordered_blocks_, &pseudo_entry_block_, &pseudo_exit_block_,
&augmented_successors_map_, &augmented_predecessors_map_, succ_func,

View File

@ -184,12 +184,12 @@ class Function {
std::function<const std::vector<BasicBlock*>*(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.

View File

@ -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 <id>. All subsequent words are target <id>s that
// are going to be decorated with the decorations.
const uint32_t decoration_group_id = inst->word(1);
std::vector<Decoration>& group_decorations =
std::set<Decoration>& 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<Decoration>& group_decorations =
std::set<Decoration>& 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.

View File

@ -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<BuiltinVUIDMapping, NumVUIDBuiltins> 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;

View File

@ -466,7 +466,7 @@ spv_result_t FindCaseFallThrough(
std::vector<BasicBlock*> stack;
stack.push_back(target_block);
std::unordered_set<const BasicBlock*> 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<uint32_t>(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 <ID> " << _.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 <ID> " << 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<uint32_t>(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<uint32_t>(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<const BasicBlock*> postorder;
std::vector<const BasicBlock*> postdom_postorder;
std::vector<std::pair<uint32_t, uint32_t>> 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<BasicBlock>::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<BasicBlock>::CalculateDominators(
postorder, function.AugmentedCFGPredecessorsFunction());
for (auto edge : edges) {
if (edge.first != edge.second)
edge.first->SetImmediateDominator(edge.second);
}
/// calculate post dominators
CFA<BasicBlock>::DepthFirstTraversal(
function.pseudo_exit_block(),
function.AugmentedCFGPredecessorsFunction(), ignore_block,
[&](const BasicBlock* b) { postdom_postorder.push_back(b); },
ignore_edge);
auto postdom_edges = CFA<BasicBlock>::CalculateDominators(
postdom_postorder, function.AugmentedCFGSuccessorsFunction());
for (auto edge : postdom_edges) {
edge.first->SetImmediatePostDominator(edge.second);
}
/// calculate back edges.
CFA<BasicBlock>::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<const BasicBlock*> postdom_postorder;
std::vector<std::pair<uint32_t, uint32_t>> back_edges;
if (!function.ordered_blocks().empty()) {
/// calculate dominators
CFA<BasicBlock>::DepthFirstTraversal(
function.first_block(),
function.AugmentedStructuralCFGSuccessorsFunction(), ignore_block,
[&](const BasicBlock* b) { postorder.push_back(b); }, ignore_edge,
no_terminal_blocks);
auto edges = CFA<BasicBlock>::CalculateDominators(
postorder, function.AugmentedStructuralCFGPredecessorsFunction());
for (auto edge : edges) {
if (edge.first != edge.second)
edge.first->SetImmediateStructuralDominator(edge.second);
}
/// calculate post dominators
CFA<BasicBlock>::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<BasicBlock>::CalculateDominators(
postdom_postorder,
function.AugmentedStructuralCFGSuccessorsFunction());
for (auto edge : postdom_edges) {
edge.first->SetImmediateStructuralPostDominator(edge.second);
}
/// calculate back edges.
CFA<BasicBlock>::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<BasicBlock*> 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) {

View File

@ -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;
}

View File

@ -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;

View File

@ -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<Decoration>&,
const std::vector<Decoration>&);
bool HasConflictingMemberOffsets(const std::set<Decoration>&,
const std::set<Decoration>&);
bool IsAllowedTypeOrArrayOfSame(ValidationState_t& _, const Instruction* type,
std::initializer_list<uint32_t> 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<Decoration>& type1_decorations =
_.id_decorations(type1->id());
const std::vector<Decoration>& type2_decorations =
_.id_decorations(type2->id());
const std::set<Decoration>& type1_decorations = _.id_decorations(type1->id());
const std::set<Decoration>& 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<Decoration>& type1_decorations,
const std::vector<Decoration>& type2_decorations) {
const std::set<Decoration>& type1_decorations,
const std::set<Decoration>& 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 <id> '" << _.getIdName(inst->id())
<< "' has illegal type.\n"
<< _.VkErrorID(6808) << "PushConstant OpVariable <id> '"
<< _.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 <id> '" << _.getIdName(inst->id())
<< "' has illegal type.\n"
<< "From Vulkan spec, section 14.5.2:\n"
<< _.VkErrorID(6807) << "Uniform OpVariable <id> '"
<< _.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 <id> '" << _.getIdName(inst->id())
<< "' has illegal type.\n"
<< "From Vulkan spec, section 14.5.2:\n"
<< _.VkErrorID(6807) << "StorageBuffer OpVariable <id> '"
<< _.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 "

View File

@ -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;

View File

@ -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
}

View File

@ -375,17 +375,14 @@ class ValidationState_t {
/// Registers the decoration for the given <id>
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 <id>
template <class InputIt>
void RegisterDecorationsForId(uint32_t id, InputIt begin, InputIt end) {
std::vector<Decoration>& cur_decs = id_decorations_[id];
cur_decs.insert(cur_decs.end(), begin, end);
std::set<Decoration>& 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<Decoration>& 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 <id>. If no decorations exist
/// for the <id>, it registers an empty vector for it in the map and
/// returns the empty vector.
std::vector<Decoration>& id_decorations(uint32_t id) {
/// for the <id>, it registers an empty set for it in the map and
/// returns the empty set.
std::set<Decoration>& id_decorations(uint32_t id) {
return id_decorations_[id];
}
/// Returns the range of decorations for the given field of the given <id>.
struct FieldDecorationsIter {
std::set<Decoration>::const_iterator begin;
std::set<Decoration>::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<uint32_t, std::vector<Decoration>>& id_decorations() const {
const std::map<uint32_t, std::set<Decoration>>& 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 <id>
std::map<uint32_t, std::vector<Decoration>> id_decorations_;
std::map<uint32_t, std::set<Decoration>> 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.