mirror of https://github.com/bkaradzic/bgfx
Updated spirv-tools.
This commit is contained in:
parent
7f9cdba788
commit
6c09cb564a
|
@ -1 +1 @@
|
|||
"v2022.3-dev", "SPIRV-Tools v2022.3-dev b8091498a3d8f2f7a46df2009d7c340eef1939b6"
|
||||
"v2022.3-dev", "SPIRV-Tools v2022.3-dev 05862b9695f45c8a8810d69c52ced4440446c1b7"
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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}}
|
||||
};
|
|
@ -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},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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",
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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>(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_ &&
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue