mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-02-17 20:52:36 +01:00
Updated spirv-tools.
This commit is contained in:
@@ -1 +1 @@
|
||||
"v2021.4-dev", "SPIRV-Tools v2021.4-dev e180849b89618fde67379735eae3a633a97e293c"
|
||||
"v2021.4-dev", "SPIRV-Tools v2021.4-dev 7e0cccc269f6476f79172f4446ab902f6c451f17"
|
||||
|
||||
@@ -7,6 +7,7 @@ static const SpvCapability pygen_variable_caps_ArbitraryPrecisionFloatingPointIN
|
||||
static const SpvCapability pygen_variable_caps_AsmINTEL[] = {SpvCapabilityAsmINTEL};
|
||||
static const SpvCapability pygen_variable_caps_AtomicFloat16AddEXTAtomicFloat32AddEXTAtomicFloat64AddEXT[] = {SpvCapabilityAtomicFloat16AddEXT, SpvCapabilityAtomicFloat32AddEXT, SpvCapabilityAtomicFloat64AddEXT};
|
||||
static const SpvCapability pygen_variable_caps_AtomicFloat16MinMaxEXTAtomicFloat32MinMaxEXTAtomicFloat64MinMaxEXT[] = {SpvCapabilityAtomicFloat16MinMaxEXT, SpvCapabilityAtomicFloat32MinMaxEXT, SpvCapabilityAtomicFloat64MinMaxEXT};
|
||||
static const SpvCapability pygen_variable_caps_BindlessTextureNV[] = {SpvCapabilityBindlessTextureNV};
|
||||
static const SpvCapability pygen_variable_caps_BlockingPipesINTEL[] = {SpvCapabilityBlockingPipesINTEL};
|
||||
static const SpvCapability pygen_variable_caps_CooperativeMatrixNV[] = {SpvCapabilityCooperativeMatrixNV};
|
||||
static const SpvCapability pygen_variable_caps_DemoteToHelperInvocationEXT[] = {SpvCapabilityDemoteToHelperInvocationEXT};
|
||||
@@ -498,6 +499,13 @@ static const spv_opcode_desc_t kOpcodeTableEntries[] = {
|
||||
{"EndInvocationInterlockEXT", SpvOpEndInvocationInterlockEXT, 3, pygen_variable_caps_FragmentShaderSampleInterlockEXTFragmentShaderPixelInterlockEXTFragmentShaderShadingRateInterlockEXT, 0, {}, 0, 0, 1, pygen_variable_exts_SPV_EXT_fragment_shader_interlock, 0xffffffffu, 0xffffffffu},
|
||||
{"DemoteToHelperInvocationEXT", SpvOpDemoteToHelperInvocationEXT, 1, pygen_variable_caps_DemoteToHelperInvocationEXT, 0, {}, 0, 0, 1, pygen_variable_exts_SPV_EXT_demote_to_helper_invocation, 0xffffffffu, 0xffffffffu},
|
||||
{"IsHelperInvocationEXT", SpvOpIsHelperInvocationEXT, 1, pygen_variable_caps_DemoteToHelperInvocationEXT, 2, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID}, 1, 1, 1, pygen_variable_exts_SPV_EXT_demote_to_helper_invocation, 0xffffffffu, 0xffffffffu},
|
||||
{"ConvertUToImageNV", SpvOpConvertUToImageNV, 1, pygen_variable_caps_BindlessTextureNV, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
|
||||
{"ConvertUToSamplerNV", SpvOpConvertUToSamplerNV, 1, pygen_variable_caps_BindlessTextureNV, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
|
||||
{"ConvertImageToUNV", SpvOpConvertImageToUNV, 1, pygen_variable_caps_BindlessTextureNV, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
|
||||
{"ConvertSamplerToUNV", SpvOpConvertSamplerToUNV, 1, pygen_variable_caps_BindlessTextureNV, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
|
||||
{"ConvertUToSampledImageNV", SpvOpConvertUToSampledImageNV, 1, pygen_variable_caps_BindlessTextureNV, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
|
||||
{"ConvertSampledImageToUNV", SpvOpConvertSampledImageToUNV, 1, pygen_variable_caps_BindlessTextureNV, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
|
||||
{"SamplerImageAddressingModeNV", SpvOpSamplerImageAddressingModeNV, 1, pygen_variable_caps_BindlessTextureNV, 1, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, 0, 0, 0, nullptr, 0xffffffffu, 0xffffffffu},
|
||||
{"SubgroupShuffleINTEL", SpvOpSubgroupShuffleINTEL, 1, pygen_variable_caps_SubgroupShuffleINTEL, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
|
||||
{"SubgroupShuffleDownINTEL", SpvOpSubgroupShuffleDownINTEL, 1, pygen_variable_caps_SubgroupShuffleINTEL, 5, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
|
||||
{"SubgroupShuffleUpINTEL", SpvOpSubgroupShuffleUpINTEL, 1, pygen_variable_caps_SubgroupShuffleINTEL, 5, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
|
||||
|
||||
@@ -162,6 +162,8 @@ const char* ExtensionToString(Extension extension) {
|
||||
return "SPV_KHR_workgroup_memory_explicit_layout";
|
||||
case Extension::kSPV_NVX_multiview_per_view_attributes:
|
||||
return "SPV_NVX_multiview_per_view_attributes";
|
||||
case Extension::kSPV_NV_bindless_texture:
|
||||
return "SPV_NV_bindless_texture";
|
||||
case Extension::kSPV_NV_compute_shader_derivatives:
|
||||
return "SPV_NV_compute_shader_derivatives";
|
||||
case Extension::kSPV_NV_cooperative_matrix:
|
||||
@@ -199,8 +201,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_optnone", "SPV_INTEL_shader_integer_functions2", "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_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_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_uniform_control_flow", "SPV_KHR_subgroup_vote", "SPV_KHR_terminate_invocation", "SPV_KHR_variable_pointers", "SPV_KHR_vulkan_memory_model", "SPV_KHR_workgroup_memory_explicit_layout", "SPV_NVX_multiview_per_view_attributes", "SPV_NV_compute_shader_derivatives", "SPV_NV_cooperative_matrix", "SPV_NV_fragment_shader_barycentric", "SPV_NV_geometry_shader_passthrough", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing", "SPV_NV_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_optnone, Extension::kSPV_INTEL_shader_integer_functions2, 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_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_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_uniform_control_flow, Extension::kSPV_KHR_subgroup_vote, Extension::kSPV_KHR_terminate_invocation, 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_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_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_optnone", "SPV_INTEL_shader_integer_functions2", "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_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_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_uniform_control_flow", "SPV_KHR_subgroup_vote", "SPV_KHR_terminate_invocation", "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_optnone, Extension::kSPV_INTEL_shader_integer_functions2, 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_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_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_uniform_control_flow, Extension::kSPV_KHR_subgroup_vote, Extension::kSPV_KHR_terminate_invocation, 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(
|
||||
@@ -502,6 +504,8 @@ const char* CapabilityToString(SpvCapability capability) {
|
||||
return "FragmentShaderPixelInterlockEXT";
|
||||
case SpvCapabilityDemoteToHelperInvocationEXT:
|
||||
return "DemoteToHelperInvocationEXT";
|
||||
case SpvCapabilityBindlessTextureNV:
|
||||
return "BindlessTextureNV";
|
||||
case SpvCapabilitySubgroupShuffleINTEL:
|
||||
return "SubgroupShuffleINTEL";
|
||||
case SpvCapabilitySubgroupBufferBlockIOINTEL:
|
||||
|
||||
@@ -79,6 +79,7 @@ kSPV_KHR_variable_pointers,
|
||||
kSPV_KHR_vulkan_memory_model,
|
||||
kSPV_KHR_workgroup_memory_explicit_layout,
|
||||
kSPV_NVX_multiview_per_view_attributes,
|
||||
kSPV_NV_bindless_texture,
|
||||
kSPV_NV_compute_shader_derivatives,
|
||||
kSPV_NV_cooperative_matrix,
|
||||
kSPV_NV_fragment_shader_barycentric,
|
||||
|
||||
@@ -28,4 +28,5 @@
|
||||
{27, "Embark Studios", "Rust GPU Compiler Backend", "Embark Studios Rust GPU Compiler Backend"},
|
||||
{28, "gfx-rs community", "Naga", "gfx-rs community Naga"},
|
||||
{29, "Mikkosoft Productions", "MSP Shader Compiler", "Mikkosoft Productions MSP Shader Compiler"},
|
||||
{30, "SpvGenTwo community", "SpvGenTwo SPIR-V IR Tools", "SpvGenTwo community SpvGenTwo SPIR-V IR Tools"},
|
||||
{30, "SpvGenTwo community", "SpvGenTwo SPIR-V IR Tools", "SpvGenTwo community SpvGenTwo SPIR-V IR Tools"},
|
||||
{31, "Google", "Skia SkSL", "Google Skia SkSL"},
|
||||
@@ -2,6 +2,7 @@ static const SpvCapability pygen_variable_caps_Addresses[] = {SpvCapabilityAddre
|
||||
static const SpvCapability pygen_variable_caps_ArbitraryPrecisionFixedPointINTEL[] = {SpvCapabilityArbitraryPrecisionFixedPointINTEL};
|
||||
static const SpvCapability pygen_variable_caps_AsmINTEL[] = {SpvCapabilityAsmINTEL};
|
||||
static const SpvCapability pygen_variable_caps_AtomicStorage[] = {SpvCapabilityAtomicStorage};
|
||||
static const SpvCapability pygen_variable_caps_BindlessTextureNV[] = {SpvCapabilityBindlessTextureNV};
|
||||
static const SpvCapability pygen_variable_caps_ClipDistance[] = {SpvCapabilityClipDistance};
|
||||
static const SpvCapability pygen_variable_caps_ComputeDerivativeGroupLinearNV[] = {SpvCapabilityComputeDerivativeGroupLinearNV};
|
||||
static const SpvCapability pygen_variable_caps_ComputeDerivativeGroupQuadsNV[] = {SpvCapabilityComputeDerivativeGroupQuadsNV};
|
||||
@@ -194,6 +195,7 @@ static const spvtools::Extension pygen_variable_exts_SPV_KHR_vulkan_memory_model
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_KHR_workgroup_memory_explicit_layout[] = {spvtools::Extension::kSPV_KHR_workgroup_memory_explicit_layout};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_NVX_multiview_per_view_attributes[] = {spvtools::Extension::kSPV_NVX_multiview_per_view_attributes};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_NVX_multiview_per_view_attributesSPV_NV_mesh_shader[] = {spvtools::Extension::kSPV_NVX_multiview_per_view_attributes, spvtools::Extension::kSPV_NV_mesh_shader};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_NV_bindless_texture[] = {spvtools::Extension::kSPV_NV_bindless_texture};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_NV_compute_shader_derivatives[] = {spvtools::Extension::kSPV_NV_compute_shader_derivatives};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_NV_cooperative_matrix[] = {spvtools::Extension::kSPV_NV_cooperative_matrix};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_NV_fragment_shader_barycentric[] = {spvtools::Extension::kSPV_NV_fragment_shader_barycentric};
|
||||
@@ -228,7 +230,8 @@ static const spv_operand_desc_t pygen_variable_ImageOperandsEntries[] = {
|
||||
{"VolatileTexel", 0x0800, 1, pygen_variable_caps_VulkanMemoryModel, 1, pygen_variable_exts_SPV_KHR_vulkan_memory_model, {}, SPV_SPIRV_VERSION_WORD(1,5), 0xffffffffu},
|
||||
{"VolatileTexelKHR", 0x0800, 1, pygen_variable_caps_VulkanMemoryModel, 1, pygen_variable_exts_SPV_KHR_vulkan_memory_model, {}, SPV_SPIRV_VERSION_WORD(1,5), 0xffffffffu},
|
||||
{"SignExtend", 0x1000, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
|
||||
{"ZeroExtend", 0x2000, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu}
|
||||
{"ZeroExtend", 0x2000, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
|
||||
{"Offsets", 0x10000, 0, nullptr, 0, nullptr, {SPV_OPERAND_TYPE_ID}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu}
|
||||
};
|
||||
|
||||
static const spv_operand_desc_t pygen_variable_FPFastMathModeEntries[] = {
|
||||
@@ -726,6 +729,10 @@ static const spv_operand_desc_t pygen_variable_DecorationEntries[] = {
|
||||
{"RestrictPointerEXT", 5355, 1, pygen_variable_caps_PhysicalStorageBufferAddresses, 2, pygen_variable_exts_SPV_EXT_physical_storage_bufferSPV_KHR_physical_storage_buffer, {}, SPV_SPIRV_VERSION_WORD(1,5), 0xffffffffu},
|
||||
{"AliasedPointer", 5356, 1, pygen_variable_caps_PhysicalStorageBufferAddresses, 2, pygen_variable_exts_SPV_EXT_physical_storage_bufferSPV_KHR_physical_storage_buffer, {}, SPV_SPIRV_VERSION_WORD(1,5), 0xffffffffu},
|
||||
{"AliasedPointerEXT", 5356, 1, pygen_variable_caps_PhysicalStorageBufferAddresses, 2, pygen_variable_exts_SPV_EXT_physical_storage_bufferSPV_KHR_physical_storage_buffer, {}, SPV_SPIRV_VERSION_WORD(1,5), 0xffffffffu},
|
||||
{"BindlessSamplerNV", 5398, 1, pygen_variable_caps_BindlessTextureNV, 0, nullptr, {}, 0xffffffffu, 0xffffffffu},
|
||||
{"BindlessImageNV", 5399, 1, pygen_variable_caps_BindlessTextureNV, 0, nullptr, {}, 0xffffffffu, 0xffffffffu},
|
||||
{"BoundSamplerNV", 5400, 1, pygen_variable_caps_BindlessTextureNV, 0, nullptr, {}, 0xffffffffu, 0xffffffffu},
|
||||
{"BoundImageNV", 5401, 1, pygen_variable_caps_BindlessTextureNV, 0, nullptr, {}, 0xffffffffu, 0xffffffffu},
|
||||
{"SIMTCallINTEL", 5599, 1, pygen_variable_caps_VectorComputeINTEL, 0, nullptr, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, 0xffffffffu, 0xffffffffu},
|
||||
{"ReferencedIndirectlyINTEL", 5602, 1, pygen_variable_caps_IndirectReferencesINTEL, 1, pygen_variable_exts_SPV_INTEL_function_pointers, {}, 0xffffffffu, 0xffffffffu},
|
||||
{"ClobberINTEL", 5607, 1, pygen_variable_caps_AsmINTEL, 0, nullptr, {SPV_OPERAND_TYPE_LITERAL_STRING}, 0xffffffffu, 0xffffffffu},
|
||||
@@ -1079,6 +1086,7 @@ static const spv_operand_desc_t pygen_variable_CapabilityEntries[] = {
|
||||
{"ShaderSMBuiltinsNV", 5373, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_NV_shader_sm_builtins, {}, 0xffffffffu, 0xffffffffu},
|
||||
{"FragmentShaderPixelInterlockEXT", 5378, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_EXT_fragment_shader_interlock, {}, 0xffffffffu, 0xffffffffu},
|
||||
{"DemoteToHelperInvocationEXT", 5379, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_EXT_demote_to_helper_invocation, {}, 0xffffffffu, 0xffffffffu},
|
||||
{"BindlessTextureNV", 5390, 0, nullptr, 1, pygen_variable_exts_SPV_NV_bindless_texture, {}, 0xffffffffu, 0xffffffffu},
|
||||
{"SubgroupShuffleINTEL", 5568, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_subgroups, {}, 0xffffffffu, 0xffffffffu},
|
||||
{"SubgroupBufferBlockIOINTEL", 5569, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_subgroups, {}, 0xffffffffu, 0xffffffffu},
|
||||
{"SubgroupImageBlockIOINTEL", 5570, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_subgroups, {}, 0xffffffffu, 0xffffffffu},
|
||||
|
||||
@@ -517,6 +517,7 @@ typedef enum {
|
||||
|
||||
SPV_ENV_UNIVERSAL_1_5, // SPIR-V 1.5 latest revision, no other restrictions.
|
||||
SPV_ENV_VULKAN_1_2, // Vulkan 1.2 latest revision.
|
||||
SPV_ENV_MAX // Keep this as the last enum value.
|
||||
} spv_target_env;
|
||||
|
||||
// SPIR-V Validator can be parameterized with the following Universal Limits.
|
||||
|
||||
@@ -833,6 +833,13 @@ Optimizer::PassToken CreateFixStorageClassPass();
|
||||
// inclusive.
|
||||
Optimizer::PassToken CreateGraphicsRobustAccessPass();
|
||||
|
||||
// Create a pass to replace a descriptor access using variable index.
|
||||
// This pass replaces every access using a variable index to array variable
|
||||
// |desc| that has a DescriptorSet and Binding decorations with a constant
|
||||
// element of the array. In order to replace the access using a variable index
|
||||
// with the constant element, it uses a switch statement.
|
||||
Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass();
|
||||
|
||||
// Create descriptor scalar replacement pass.
|
||||
// This pass replaces every array variable |desc| that has a DescriptorSet and
|
||||
// Binding decorations with a new variable for each element of the array.
|
||||
|
||||
3
3rdparty/spirv-tools/source/link/linker.cpp
vendored
3
3rdparty/spirv-tools/source/link/linker.cpp
vendored
@@ -34,6 +34,7 @@
|
||||
#include "source/opt/pass_manager.h"
|
||||
#include "source/opt/remove_duplicates_pass.h"
|
||||
#include "source/opt/type_manager.h"
|
||||
#include "source/spirv_constant.h"
|
||||
#include "source/spirv_target_env.h"
|
||||
#include "source/util/make_unique.h"
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
@@ -207,7 +208,7 @@ spv_result_t GenerateHeader(const MessageConsumer& consumer,
|
||||
|
||||
header->magic_number = SpvMagicNumber;
|
||||
header->version = version;
|
||||
header->generator = 17u;
|
||||
header->generator = SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_LINKER, 0);
|
||||
header->bound = max_id_bound;
|
||||
header->reserved = 0u;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2017 The Khronos Group Inc.
|
||||
// Copyright (c) 2017 Valve Corporation
|
||||
// Copyright (c) 2017 LunarG Inc.
|
||||
// Copyright (c) 2018 Google LLC
|
||||
// Copyright (c) 2018-2021 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "source/cfa.h"
|
||||
#include "source/latest_version_glsl_std_450_header.h"
|
||||
#include "source/opt/eliminate_dead_functions_util.h"
|
||||
#include "source/opt/ir_builder.h"
|
||||
#include "source/opt/iterator.h"
|
||||
#include "source/opt/reflect.h"
|
||||
#include "source/spirv_constant.h"
|
||||
@@ -38,6 +39,7 @@ const uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
|
||||
const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
|
||||
const uint32_t kCopyMemoryTargetAddrInIdx = 0;
|
||||
const uint32_t kCopyMemorySourceAddrInIdx = 1;
|
||||
const uint32_t kLoadSourceAddrInIdx = 0;
|
||||
const uint32_t kDebugDeclareOperandVariableIndex = 5;
|
||||
const uint32_t kGlobalVariableVariableIndex = 12;
|
||||
|
||||
@@ -95,16 +97,21 @@ bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId, uint32_t storageClass) {
|
||||
storageClass;
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::IsLocalVar(uint32_t varId) {
|
||||
bool AggressiveDCEPass::IsLocalVar(uint32_t varId, Function* func) {
|
||||
if (IsVarOfStorage(varId, SpvStorageClassFunction)) {
|
||||
return true;
|
||||
}
|
||||
if (!private_like_local_) {
|
||||
|
||||
if (!IsVarOfStorage(varId, SpvStorageClassPrivate) &&
|
||||
!IsVarOfStorage(varId, SpvStorageClassWorkgroup)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsVarOfStorage(varId, SpvStorageClassPrivate) ||
|
||||
IsVarOfStorage(varId, SpvStorageClassWorkgroup);
|
||||
// For a variable in the Private or WorkGroup storage class, the variable will
|
||||
// get a new instance for every call to an entry point. If the entry point
|
||||
// does not have a call, then no other function can read or write to that
|
||||
// instance of the variable.
|
||||
return IsEntryPointWithNoCalls(func);
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) {
|
||||
@@ -160,14 +167,6 @@ bool AggressiveDCEPass::AllExtensionsSupported() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::IsDead(Instruction* inst) {
|
||||
if (IsLive(inst)) return false;
|
||||
if ((inst->IsBranch() || inst->opcode() == SpvOpUnreachable) &&
|
||||
context()->get_instr_block(inst)->GetMergeInst() == nullptr)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::IsTargetDead(Instruction* inst) {
|
||||
const uint32_t tId = inst->GetSingleWordInOperand(0);
|
||||
Instruction* tInst = get_def_use_mgr()->GetDef(tId);
|
||||
@@ -184,12 +183,12 @@ bool AggressiveDCEPass::IsTargetDead(Instruction* inst) {
|
||||
});
|
||||
return dead;
|
||||
}
|
||||
return IsDead(tInst);
|
||||
return !IsLive(tInst);
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) {
|
||||
// Only process locals
|
||||
if (!IsLocalVar(varId)) return;
|
||||
if (!IsLocalVar(varId, func)) return;
|
||||
// Return if already processed
|
||||
if (live_local_vars_.find(varId) != live_local_vars_.end()) return;
|
||||
// Mark all stores to varId as live
|
||||
@@ -265,170 +264,36 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::AggressiveDCE(Function* func) {
|
||||
// Mark function parameters as live.
|
||||
AddToWorklist(&func->DefInst());
|
||||
MarkFunctionParameterAsLive(func);
|
||||
|
||||
std::list<BasicBlock*> structuredOrder;
|
||||
cfg()->ComputeStructuredOrder(func, &*func->begin(), &structuredOrder);
|
||||
bool modified = false;
|
||||
std::list<BasicBlock*> structured_order;
|
||||
cfg()->ComputeStructuredOrder(func, &*func->begin(), &structured_order);
|
||||
live_local_vars_.clear();
|
||||
InitializeWorkList(func, structuredOrder);
|
||||
InitializeWorkList(func, structured_order);
|
||||
ProcessWorkList(func);
|
||||
return KillDeadInstructions(func, structured_order);
|
||||
}
|
||||
|
||||
// Perform closure on live instruction set.
|
||||
while (!worklist_.empty()) {
|
||||
Instruction* liveInst = worklist_.front();
|
||||
// Add all operand instructions of Debug[No]Lines
|
||||
for (auto& lineInst : liveInst->dbg_line_insts()) {
|
||||
if (lineInst.IsDebugLineInst()) {
|
||||
lineInst.ForEachInId([this](const uint32_t* iid) {
|
||||
AddToWorklist(get_def_use_mgr()->GetDef(*iid));
|
||||
});
|
||||
}
|
||||
}
|
||||
// Add all operand instructions if not already live
|
||||
liveInst->ForEachInId([&liveInst, this](const uint32_t* iid) {
|
||||
Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
|
||||
// Do not add label if an operand of a branch. This is not needed
|
||||
// as part of live code discovery and can create false live code,
|
||||
// for example, the branch to a header of a loop.
|
||||
if (inInst->opcode() == SpvOpLabel && liveInst->IsBranch()) return;
|
||||
AddToWorklist(inInst);
|
||||
});
|
||||
if (liveInst->type_id() != 0) {
|
||||
AddToWorklist(get_def_use_mgr()->GetDef(liveInst->type_id()));
|
||||
}
|
||||
BasicBlock* basic_block = context()->get_instr_block(liveInst);
|
||||
if (basic_block != nullptr) {
|
||||
AddToWorklist(basic_block->GetLabelInst());
|
||||
}
|
||||
|
||||
// If in a structured if or loop construct, add the controlling
|
||||
// conditional branch and its merge.
|
||||
BasicBlock* blk = context()->get_instr_block(liveInst);
|
||||
Instruction* branchInst = GetHeaderBranch(blk);
|
||||
if (branchInst != nullptr) {
|
||||
AddToWorklist(branchInst);
|
||||
Instruction* mergeInst = GetMergeInstruction(branchInst);
|
||||
AddToWorklist(mergeInst);
|
||||
}
|
||||
// If the block is a header, add the next outermost controlling
|
||||
// conditional branch and its merge.
|
||||
Instruction* nextBranchInst = GetBranchForNextHeader(blk);
|
||||
if (nextBranchInst != nullptr) {
|
||||
AddToWorklist(nextBranchInst);
|
||||
Instruction* mergeInst = GetMergeInstruction(nextBranchInst);
|
||||
AddToWorklist(mergeInst);
|
||||
}
|
||||
// If local load, add all variable's stores if variable not already live
|
||||
if (liveInst->opcode() == SpvOpLoad || liveInst->IsAtomicWithLoad()) {
|
||||
uint32_t varId;
|
||||
(void)GetPtr(liveInst, &varId);
|
||||
if (varId != 0) {
|
||||
ProcessLoad(func, varId);
|
||||
}
|
||||
// Process memory copies like loads
|
||||
} else if (liveInst->opcode() == SpvOpCopyMemory ||
|
||||
liveInst->opcode() == SpvOpCopyMemorySized) {
|
||||
uint32_t varId;
|
||||
(void)GetPtr(liveInst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx),
|
||||
&varId);
|
||||
if (varId != 0) {
|
||||
ProcessLoad(func, varId);
|
||||
}
|
||||
// If DebugDeclare, process as load of variable
|
||||
} else if (liveInst->GetCommonDebugOpcode() ==
|
||||
CommonDebugInfoDebugDeclare) {
|
||||
uint32_t varId =
|
||||
liveInst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
|
||||
ProcessLoad(func, varId);
|
||||
// If DebugValue with Deref, process as load of variable
|
||||
} else if (liveInst->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) {
|
||||
uint32_t varId = context()
|
||||
->get_debug_info_mgr()
|
||||
->GetVariableIdOfDebugValueUsedForDeclare(liveInst);
|
||||
if (varId != 0) ProcessLoad(func, varId);
|
||||
// If merge, add other branches that are part of its control structure
|
||||
} else if (liveInst->opcode() == SpvOpLoopMerge ||
|
||||
liveInst->opcode() == SpvOpSelectionMerge) {
|
||||
AddBreaksAndContinuesToWorklist(liveInst);
|
||||
// If function call, treat as if it loads from all pointer arguments
|
||||
} else if (liveInst->opcode() == SpvOpFunctionCall) {
|
||||
liveInst->ForEachInId([this, func](const uint32_t* iid) {
|
||||
// Skip non-ptr args
|
||||
if (!IsPtr(*iid)) return;
|
||||
uint32_t varId;
|
||||
(void)GetPtr(*iid, &varId);
|
||||
ProcessLoad(func, varId);
|
||||
});
|
||||
// If function parameter, treat as if it's result id is loaded from
|
||||
} else if (liveInst->opcode() == SpvOpFunctionParameter) {
|
||||
ProcessLoad(func, liveInst->result_id());
|
||||
// We treat an OpImageTexelPointer as a load of the pointer, and
|
||||
// that value is manipulated to get the result.
|
||||
} else if (liveInst->opcode() == SpvOpImageTexelPointer) {
|
||||
uint32_t varId;
|
||||
(void)GetPtr(liveInst, &varId);
|
||||
if (varId != 0) {
|
||||
ProcessLoad(func, varId);
|
||||
}
|
||||
}
|
||||
|
||||
// Add OpDecorateId instructions that apply to this instruction to the work
|
||||
// list. We use the decoration manager to look through the group
|
||||
// decorations to get to the OpDecorate* instructions themselves.
|
||||
auto decorations =
|
||||
get_decoration_mgr()->GetDecorationsFor(liveInst->result_id(), false);
|
||||
for (Instruction* dec : decorations) {
|
||||
// We only care about OpDecorateId instructions because the are the only
|
||||
// decorations that will reference an id that will have to be kept live
|
||||
// because of that use.
|
||||
if (dec->opcode() != SpvOpDecorateId) {
|
||||
continue;
|
||||
}
|
||||
if (dec->GetSingleWordInOperand(1) ==
|
||||
SpvDecorationHlslCounterBufferGOOGLE) {
|
||||
// These decorations should not force the use id to be live. It will be
|
||||
// removed if either the target or the in operand are dead.
|
||||
continue;
|
||||
}
|
||||
AddToWorklist(dec);
|
||||
}
|
||||
|
||||
// Add DebugScope and DebugInlinedAt for |liveInst| to the work list.
|
||||
if (liveInst->GetDebugScope().GetLexicalScope() != kNoDebugScope) {
|
||||
auto* scope = get_def_use_mgr()->GetDef(
|
||||
liveInst->GetDebugScope().GetLexicalScope());
|
||||
AddToWorklist(scope);
|
||||
}
|
||||
if (liveInst->GetDebugInlinedAt() != kNoInlinedAt) {
|
||||
auto* inlined_at =
|
||||
get_def_use_mgr()->GetDef(liveInst->GetDebugInlinedAt());
|
||||
AddToWorklist(inlined_at);
|
||||
}
|
||||
worklist_.pop();
|
||||
}
|
||||
|
||||
// Kill dead instructions and remember dead blocks
|
||||
for (auto bi = structuredOrder.begin(); bi != structuredOrder.end();) {
|
||||
uint32_t mergeBlockId = 0;
|
||||
(*bi)->ForEachInst([this, &modified, &mergeBlockId](Instruction* inst) {
|
||||
if (!IsDead(inst)) return;
|
||||
bool AggressiveDCEPass::KillDeadInstructions(
|
||||
const Function* func, std::list<BasicBlock*>& structured_order) {
|
||||
bool modified = false;
|
||||
for (auto bi = structured_order.begin(); bi != structured_order.end();) {
|
||||
uint32_t merge_block_id = 0;
|
||||
(*bi)->ForEachInst([this, &modified, &merge_block_id](Instruction* inst) {
|
||||
if (IsLive(inst)) return;
|
||||
if (inst->opcode() == SpvOpLabel) return;
|
||||
// If dead instruction is selection merge, remember merge block
|
||||
// for new branch at end of block
|
||||
if (inst->opcode() == SpvOpSelectionMerge ||
|
||||
inst->opcode() == SpvOpLoopMerge)
|
||||
mergeBlockId = inst->GetSingleWordInOperand(0);
|
||||
merge_block_id = inst->GetSingleWordInOperand(0);
|
||||
to_kill_.push_back(inst);
|
||||
modified = true;
|
||||
});
|
||||
// If a structured if or loop was deleted, add a branch to its merge
|
||||
// block, and traverse to the merge block and continue processing there.
|
||||
// We know the block still exists because the label is not deleted.
|
||||
if (mergeBlockId != 0) {
|
||||
AddBranch(mergeBlockId, *bi);
|
||||
for (++bi; (*bi)->id() != mergeBlockId; ++bi) {
|
||||
if (merge_block_id != 0) {
|
||||
AddBranch(merge_block_id, *bi);
|
||||
for (++bi; (*bi)->id() != merge_block_id; ++bi) {
|
||||
}
|
||||
|
||||
auto merge_terminator = (*bi)->terminator();
|
||||
@@ -451,94 +316,250 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) {
|
||||
live_insts_.Set(merge_terminator->unique_id());
|
||||
}
|
||||
} else {
|
||||
Instruction* inst = (*bi)->terminator();
|
||||
if (!IsLive(inst)) {
|
||||
// If the terminator is not live, this block has no live instructions,
|
||||
// and it will be unreachable.
|
||||
AddUnreachable(*bi);
|
||||
}
|
||||
++bi;
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::ProcessWorkList(Function* func) {
|
||||
while (!worklist_.empty()) {
|
||||
Instruction* live_inst = worklist_.front();
|
||||
worklist_.pop();
|
||||
AddOperandsToWorkList(live_inst);
|
||||
MarkBlockAsLive(live_inst);
|
||||
MarkLoadedVariablesAsLive(func, live_inst);
|
||||
AddDecorationsToWorkList(live_inst);
|
||||
AddDebugInstructionsToWorkList(live_inst);
|
||||
}
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::AddDebugInstructionsToWorkList(
|
||||
const Instruction* inst) {
|
||||
for (auto& line_inst : inst->dbg_line_insts()) {
|
||||
if (line_inst.IsDebugLineInst()) {
|
||||
AddOperandsToWorkList(&line_inst);
|
||||
}
|
||||
}
|
||||
|
||||
if (inst->GetDebugScope().GetLexicalScope() != kNoDebugScope) {
|
||||
auto* scope =
|
||||
get_def_use_mgr()->GetDef(inst->GetDebugScope().GetLexicalScope());
|
||||
AddToWorklist(scope);
|
||||
}
|
||||
if (inst->GetDebugInlinedAt() != kNoInlinedAt) {
|
||||
auto* inlined_at = get_def_use_mgr()->GetDef(inst->GetDebugInlinedAt());
|
||||
AddToWorklist(inlined_at);
|
||||
}
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::AddDecorationsToWorkList(const Instruction* inst) {
|
||||
// Add OpDecorateId instructions that apply to this instruction to the work
|
||||
// list. We use the decoration manager to look through the group
|
||||
// decorations to get to the OpDecorate* instructions themselves.
|
||||
auto decorations =
|
||||
get_decoration_mgr()->GetDecorationsFor(inst->result_id(), false);
|
||||
for (Instruction* dec : decorations) {
|
||||
// We only care about OpDecorateId instructions because the are the only
|
||||
// decorations that will reference an id that will have to be kept live
|
||||
// because of that use.
|
||||
if (dec->opcode() != SpvOpDecorateId) {
|
||||
continue;
|
||||
}
|
||||
if (dec->GetSingleWordInOperand(1) ==
|
||||
SpvDecorationHlslCounterBufferGOOGLE) {
|
||||
// These decorations should not force the use id to be live. It will be
|
||||
// removed if either the target or the in operand are dead.
|
||||
continue;
|
||||
}
|
||||
AddToWorklist(dec);
|
||||
}
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::MarkLoadedVariablesAsLive(Function* func,
|
||||
Instruction* inst) {
|
||||
std::vector<uint32_t> live_variables = GetLoadedVariables(inst);
|
||||
for (uint32_t var_id : live_variables) {
|
||||
ProcessLoad(func, var_id);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> AggressiveDCEPass::GetLoadedVariables(Instruction* inst) {
|
||||
if (inst->opcode() == SpvOpFunctionCall) {
|
||||
return GetLoadedVariablesFromFunctionCall(inst);
|
||||
}
|
||||
uint32_t var_id = GetLoadedVariableFromNonFunctionCalls(inst);
|
||||
if (var_id == 0) {
|
||||
return {};
|
||||
}
|
||||
return {var_id};
|
||||
}
|
||||
|
||||
uint32_t AggressiveDCEPass::GetLoadedVariableFromNonFunctionCalls(
|
||||
Instruction* inst) {
|
||||
std::vector<uint32_t> live_variables;
|
||||
if (inst->IsAtomicWithLoad()) {
|
||||
return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx));
|
||||
}
|
||||
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpLoad:
|
||||
case SpvOpImageTexelPointer:
|
||||
return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx));
|
||||
case SpvOpCopyMemory:
|
||||
case SpvOpCopyMemorySized:
|
||||
return GetVariableId(
|
||||
inst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (inst->GetCommonDebugOpcode()) {
|
||||
case CommonDebugInfoDebugDeclare:
|
||||
return inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
|
||||
case CommonDebugInfoDebugValue: {
|
||||
analysis::DebugInfoManager* debug_info_mgr =
|
||||
context()->get_debug_info_mgr();
|
||||
return debug_info_mgr->GetVariableIdOfDebugValueUsedForDeclare(inst);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> AggressiveDCEPass::GetLoadedVariablesFromFunctionCall(
|
||||
const Instruction* inst) {
|
||||
assert(inst->opcode() == SpvOpFunctionCall);
|
||||
std::vector<uint32_t> live_variables;
|
||||
inst->ForEachInId([this, &live_variables](const uint32_t* operand_id) {
|
||||
if (!IsPtr(*operand_id)) return;
|
||||
uint32_t var_id = GetVariableId(*operand_id);
|
||||
live_variables.push_back(var_id);
|
||||
});
|
||||
return live_variables;
|
||||
}
|
||||
|
||||
uint32_t AggressiveDCEPass::GetVariableId(uint32_t ptr_id) {
|
||||
assert(IsPtr(ptr_id) &&
|
||||
"Cannot get the variable when input is not a pointer.");
|
||||
uint32_t varId = 0;
|
||||
(void)GetPtr(ptr_id, &varId);
|
||||
return varId;
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::MarkBlockAsLive(Instruction* inst) {
|
||||
BasicBlock* basic_block = context()->get_instr_block(inst);
|
||||
if (basic_block == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we intend to keep this instruction, we need the block label and
|
||||
// block terminator to have a valid block for the instruction.
|
||||
AddToWorklist(basic_block->GetLabelInst());
|
||||
|
||||
// We need to mark the successors blocks that follow as live. If this is
|
||||
// header of the merge construct, the construct may be folded, but we will
|
||||
// definitely need the merge label. If it is not a construct, the terminator
|
||||
// must be live, and the successor blocks will be marked as live when
|
||||
// processing the terminator.
|
||||
uint32_t merge_id = basic_block->MergeBlockIdIfAny();
|
||||
if (merge_id == 0) {
|
||||
AddToWorklist(basic_block->terminator());
|
||||
} else {
|
||||
AddToWorklist(context()->get_def_use_mgr()->GetDef(merge_id));
|
||||
}
|
||||
|
||||
// Mark the structured control flow constructs that contains this block as
|
||||
// live. If |inst| is an instruction in the loop header, then it is part of
|
||||
// the loop, so the loop construct must be live. We exclude the label because
|
||||
// it does not matter how many times it is executed. This could be extended
|
||||
// to more instructions, but we will need it for now.
|
||||
if (inst->opcode() != SpvOpLabel)
|
||||
MarkLoopConstructAsLiveIfLoopHeader(basic_block);
|
||||
|
||||
Instruction* next_branch_inst = GetBranchForNextHeader(basic_block);
|
||||
if (next_branch_inst != nullptr) {
|
||||
AddToWorklist(next_branch_inst);
|
||||
Instruction* mergeInst = GetMergeInstruction(next_branch_inst);
|
||||
AddToWorklist(mergeInst);
|
||||
}
|
||||
|
||||
if (inst->opcode() == SpvOpLoopMerge ||
|
||||
inst->opcode() == SpvOpSelectionMerge) {
|
||||
AddBreaksAndContinuesToWorklist(inst);
|
||||
}
|
||||
}
|
||||
void AggressiveDCEPass::MarkLoopConstructAsLiveIfLoopHeader(
|
||||
BasicBlock* basic_block) {
|
||||
// If this is the header for a loop, then loop structure needs to keep as well
|
||||
// because the loop header is also part of the loop.
|
||||
Instruction* merge_inst = basic_block->GetLoopMergeInst();
|
||||
if (merge_inst != nullptr) {
|
||||
AddToWorklist(basic_block->terminator());
|
||||
AddToWorklist(merge_inst);
|
||||
}
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::AddOperandsToWorkList(const Instruction* inst) {
|
||||
inst->ForEachInId([this](const uint32_t* iid) {
|
||||
Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
|
||||
AddToWorklist(inInst);
|
||||
});
|
||||
if (inst->type_id() != 0) {
|
||||
AddToWorklist(get_def_use_mgr()->GetDef(inst->type_id()));
|
||||
}
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::InitializeWorkList(
|
||||
Function* func, std::list<BasicBlock*>& structuredOrder) {
|
||||
Function* func, std::list<BasicBlock*>& structured_order) {
|
||||
AddToWorklist(&func->DefInst());
|
||||
MarkFunctionParameterAsLive(func);
|
||||
MarkFirstBlockAsLive(func);
|
||||
|
||||
// Add instructions with external side effects to the worklist. Also add
|
||||
// branches that are not attached to a structured construct.
|
||||
// TODO(s-perron): The handling of branch seems to be adhoc. This needs to be
|
||||
// cleaned up.
|
||||
bool call_in_func = false;
|
||||
bool func_is_entry_point = false;
|
||||
|
||||
// TODO(s-perron): We need to check if this is actually needed. In cases
|
||||
// where private variable can be treated as if they are function scope, the
|
||||
// private-to-local pass should be able to change them to function scope.
|
||||
std::vector<Instruction*> private_stores;
|
||||
|
||||
for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
|
||||
for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) {
|
||||
for (auto& bi : structured_order) {
|
||||
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
|
||||
SpvOp op = ii->opcode();
|
||||
if (ii->IsBranch()) {
|
||||
continue;
|
||||
}
|
||||
switch (op) {
|
||||
case SpvOpStore: {
|
||||
uint32_t varId;
|
||||
(void)GetPtr(&*ii, &varId);
|
||||
// Mark stores as live if their variable is not function scope
|
||||
// and is not private scope. Remember private stores for possible
|
||||
// later inclusion. We cannot call IsLocalVar at this point because
|
||||
// private_like_local_ has not been set yet.
|
||||
if (IsVarOfStorage(varId, SpvStorageClassPrivate) ||
|
||||
IsVarOfStorage(varId, SpvStorageClassWorkgroup))
|
||||
private_stores.push_back(&*ii);
|
||||
else if (!IsVarOfStorage(varId, SpvStorageClassFunction))
|
||||
AddToWorklist(&*ii);
|
||||
uint32_t var_id = 0;
|
||||
(void)GetPtr(&*ii, &var_id);
|
||||
if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii);
|
||||
} break;
|
||||
case SpvOpCopyMemory:
|
||||
case SpvOpCopyMemorySized: {
|
||||
uint32_t varId;
|
||||
(void)GetPtr(ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx),
|
||||
&varId);
|
||||
if (IsVarOfStorage(varId, SpvStorageClassPrivate) ||
|
||||
IsVarOfStorage(varId, SpvStorageClassWorkgroup))
|
||||
private_stores.push_back(&*ii);
|
||||
else if (!IsVarOfStorage(varId, SpvStorageClassFunction))
|
||||
AddToWorklist(&*ii);
|
||||
} break;
|
||||
case SpvOpSwitch:
|
||||
case SpvOpBranch:
|
||||
case SpvOpBranchConditional:
|
||||
case SpvOpUnreachable: {
|
||||
bool branchRelatedToConstruct =
|
||||
(GetMergeInstruction(&*ii) == nullptr &&
|
||||
GetHeaderBlock(context()->get_instr_block(&*ii)) == nullptr);
|
||||
if (branchRelatedToConstruct) {
|
||||
AddToWorklist(&*ii);
|
||||
}
|
||||
uint32_t var_id = 0;
|
||||
uint32_t target_addr_id =
|
||||
ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx);
|
||||
(void)GetPtr(target_addr_id, &var_id);
|
||||
if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii);
|
||||
} break;
|
||||
case SpvOpLoopMerge:
|
||||
case SpvOpSelectionMerge:
|
||||
case SpvOpUnreachable:
|
||||
break;
|
||||
default: {
|
||||
// Function calls, atomics, function params, function returns, etc.
|
||||
if (!ii->IsOpcodeSafeToDelete()) {
|
||||
AddToWorklist(&*ii);
|
||||
}
|
||||
// Remember function calls
|
||||
if (op == SpvOpFunctionCall) call_in_func = true;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// See if current function is an entry point
|
||||
for (auto& ei : get_module()->entry_points()) {
|
||||
if (ei.GetSingleWordInOperand(kEntryPointFunctionIdInIdx) ==
|
||||
func->result_id()) {
|
||||
func_is_entry_point = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If the current function is an entry point and has no function calls,
|
||||
// we can optimize private variables as locals
|
||||
private_like_local_ = func_is_entry_point && !call_in_func;
|
||||
// If privates are not like local, add their stores to worklist
|
||||
if (!private_like_local_)
|
||||
for (auto& ps : private_stores) AddToWorklist(ps);
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
|
||||
@@ -596,16 +617,25 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
|
||||
}
|
||||
|
||||
// For each DebugInfo GlobalVariable keep all operands except the Variable.
|
||||
// Later, if the variable is dead, we will set the operand to DebugInfoNone.
|
||||
// Later, if the variable is killed with KillInst(), we will set the operand
|
||||
// to DebugInfoNone. Create and save DebugInfoNone now for this possible
|
||||
// later use. This is slightly unoptimal, but it avoids generating it during
|
||||
// instruction killing when the module is not consistent.
|
||||
bool debug_global_seen = false;
|
||||
for (auto& dbg : get_module()->ext_inst_debuginfo()) {
|
||||
if (dbg.GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable)
|
||||
continue;
|
||||
debug_global_seen = true;
|
||||
dbg.ForEachInId([this](const uint32_t* iid) {
|
||||
Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
|
||||
if (inInst->opcode() == SpvOpVariable) return;
|
||||
AddToWorklist(inInst);
|
||||
Instruction* in_inst = get_def_use_mgr()->GetDef(*iid);
|
||||
if (in_inst->opcode() == SpvOpVariable) return;
|
||||
AddToWorklist(in_inst);
|
||||
});
|
||||
}
|
||||
if (debug_global_seen) {
|
||||
auto dbg_none = context()->get_debug_info_mgr()->GetDebugInfoNone();
|
||||
AddToWorklist(dbg_none);
|
||||
}
|
||||
}
|
||||
|
||||
Pass::Status AggressiveDCEPass::ProcessImpl() {
|
||||
@@ -743,7 +773,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
|
||||
uint32_t counter_buffer_id = annotation->GetSingleWordInOperand(2);
|
||||
Instruction* counter_buffer_inst =
|
||||
get_def_use_mgr()->GetDef(counter_buffer_id);
|
||||
if (IsDead(counter_buffer_inst)) {
|
||||
if (!IsLive(counter_buffer_inst)) {
|
||||
context()->KillInst(annotation);
|
||||
modified = true;
|
||||
}
|
||||
@@ -758,7 +788,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
|
||||
for (uint32_t i = 1; i < annotation->NumOperands();) {
|
||||
Instruction* opInst =
|
||||
get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
|
||||
if (IsDead(opInst)) {
|
||||
if (!IsLive(opInst)) {
|
||||
// Don't increment |i|.
|
||||
annotation->RemoveOperand(i);
|
||||
modified = true;
|
||||
@@ -785,7 +815,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
|
||||
for (uint32_t i = 1; i < annotation->NumOperands();) {
|
||||
Instruction* opInst =
|
||||
get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
|
||||
if (IsDead(opInst)) {
|
||||
if (!IsLive(opInst)) {
|
||||
// Don't increment |i|.
|
||||
annotation->RemoveOperand(i + 1);
|
||||
annotation->RemoveOperand(i);
|
||||
@@ -819,13 +849,13 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
|
||||
}
|
||||
|
||||
for (auto& dbg : get_module()->ext_inst_debuginfo()) {
|
||||
if (!IsDead(&dbg)) continue;
|
||||
if (IsLive(&dbg)) continue;
|
||||
// Save GlobalVariable if its variable is live, otherwise null out variable
|
||||
// index
|
||||
if (dbg.GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) {
|
||||
auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex);
|
||||
Instruction* var_inst = get_def_use_mgr()->GetDef(var_id);
|
||||
if (!IsDead(var_inst)) continue;
|
||||
if (IsLive(var_inst)) continue;
|
||||
context()->ForgetUses(&dbg);
|
||||
dbg.SetOperand(
|
||||
kGlobalVariableVariableIndex,
|
||||
@@ -840,7 +870,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
|
||||
// Since ADCE is disabled for non-shaders, we don't check for export linkage
|
||||
// attributes here.
|
||||
for (auto& val : get_module()->types_values()) {
|
||||
if (IsDead(&val)) {
|
||||
if (!IsLive(&val)) {
|
||||
// Save forwarded pointer if pointer is live since closure does not mark
|
||||
// this live as it does not have a result id. This is a little too
|
||||
// conservative since it is not known if the structure type that needed
|
||||
@@ -848,7 +878,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
|
||||
if (val.opcode() == SpvOpTypeForwardPointer) {
|
||||
uint32_t ptr_ty_id = val.GetSingleWordInOperand(0);
|
||||
Instruction* ptr_ty_inst = get_def_use_mgr()->GetDef(ptr_ty_id);
|
||||
if (!IsDead(ptr_ty_inst)) continue;
|
||||
if (IsLive(ptr_ty_inst)) continue;
|
||||
}
|
||||
to_kill_.push_back(&val);
|
||||
modified = true;
|
||||
@@ -867,7 +897,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
|
||||
} else {
|
||||
auto* var =
|
||||
get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i));
|
||||
if (!IsDead(var)) {
|
||||
if (IsLive(var)) {
|
||||
new_operands.push_back(entry.GetInOperand(i));
|
||||
}
|
||||
}
|
||||
@@ -1018,5 +1048,43 @@ bool AggressiveDCEPass::BlockIsInConstruct(BasicBlock* header_block,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::IsEntryPointWithNoCalls(Function* func) {
|
||||
auto cached_result = entry_point_with_no_calls_cache_.find(func->result_id());
|
||||
if (cached_result != entry_point_with_no_calls_cache_.end()) {
|
||||
return cached_result->second;
|
||||
}
|
||||
bool result = IsEntryPoint(func) && !HasCall(func);
|
||||
entry_point_with_no_calls_cache_[func->result_id()] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::IsEntryPoint(Function* func) {
|
||||
for (const Instruction& entry_point : get_module()->entry_points()) {
|
||||
uint32_t entry_point_id =
|
||||
entry_point.GetSingleWordInOperand(kEntryPointFunctionIdInIdx);
|
||||
if (entry_point_id == func->result_id()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::HasCall(Function* func) {
|
||||
return !func->WhileEachInst(
|
||||
[](Instruction* inst) { return inst->opcode() != SpvOpFunctionCall; });
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::MarkFirstBlockAsLive(Function* func) {
|
||||
BasicBlock* first_block = &*func->begin();
|
||||
MarkBlockAsLive(first_block->GetLabelInst());
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::AddUnreachable(BasicBlock*& block) {
|
||||
InstructionBuilder builder(
|
||||
context(), block,
|
||||
IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDefUse);
|
||||
builder.AddUnreachable();
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -67,19 +67,16 @@ class AggressiveDCEPass : public MemPass {
|
||||
// be 0 or the result of an instruction.
|
||||
bool IsVarOfStorage(uint32_t varId, uint32_t storageClass);
|
||||
|
||||
// Return true if |varId| is variable of function storage class or is
|
||||
// private variable and privates can be optimized like locals (see
|
||||
// privates_like_local_).
|
||||
bool IsLocalVar(uint32_t varId);
|
||||
// Return true if the instance of the variable |varId| can only be access in
|
||||
// |func|. For example, a function scope variable, or a private variable
|
||||
// where |func| is an entry point with no function calls.
|
||||
bool IsLocalVar(uint32_t varId, Function* func);
|
||||
|
||||
// Return true if |inst| is marked live.
|
||||
bool IsLive(const Instruction* inst) const {
|
||||
return live_insts_.Get(inst->unique_id());
|
||||
}
|
||||
|
||||
// Returns true if |inst| is dead.
|
||||
bool IsDead(Instruction* inst);
|
||||
|
||||
// Adds entry points, execution modes and workgroup size decorations to the
|
||||
// worklist for processing with the first function.
|
||||
void InitializeModuleScopeLiveInstructions();
|
||||
@@ -139,7 +136,50 @@ class AggressiveDCEPass : public MemPass {
|
||||
// Adds instructions which must be kept because of they have side-effects
|
||||
// that ADCE cannot model to the work list.
|
||||
void InitializeWorkList(Function* func,
|
||||
std::list<BasicBlock*>& structuredOrder);
|
||||
std::list<BasicBlock*>& structured_order);
|
||||
|
||||
// Process each instruction in the work list by marking any instruction that
|
||||
// that it depends on as live, and adding it to the work list. The work list
|
||||
// will be empty at the end.
|
||||
void ProcessWorkList(Function* func);
|
||||
|
||||
// Kills any instructions in |func| that have not been marked as live.
|
||||
bool KillDeadInstructions(const Function* func,
|
||||
std::list<BasicBlock*>& structured_order);
|
||||
|
||||
// Adds the instructions that define the operands of |inst| to the work list.
|
||||
void AddOperandsToWorkList(const Instruction* inst);
|
||||
|
||||
// Marks all of the labels and branch that inst requires as live.
|
||||
void MarkBlockAsLive(Instruction* inst);
|
||||
|
||||
// Marks any variables from which |inst| may require data as live.
|
||||
void MarkLoadedVariablesAsLive(Function* func, Instruction* inst);
|
||||
|
||||
// Returns the id of the variable that |ptr_id| point to. |ptr_id| must be a
|
||||
// value whose type is a pointer.
|
||||
uint32_t GetVariableId(uint32_t ptr_id);
|
||||
|
||||
// Returns all of the ids for the variables from which |inst| will load data.
|
||||
std::vector<uint32_t> GetLoadedVariables(Instruction* inst);
|
||||
|
||||
// Returns all of the ids for the variables from which |inst| will load data.
|
||||
// The opcode of |inst| must be OpFunctionCall.
|
||||
std::vector<uint32_t> GetLoadedVariablesFromFunctionCall(
|
||||
const Instruction* inst);
|
||||
|
||||
// Returns the id of the variable from which |inst| will load data. |inst|
|
||||
// must not be an OpFunctionCall. Returns 0 if no data is read or the
|
||||
// variable cannot be determined. Note that in logical addressing mode the
|
||||
// latter is not possible for function and private storage class because there
|
||||
// cannot be variable pointers pointing to those storage classes.
|
||||
uint32_t GetLoadedVariableFromNonFunctionCalls(Instruction* inst);
|
||||
|
||||
// Adds all decorations of |inst| to the work list.
|
||||
void AddDecorationsToWorkList(const Instruction* inst);
|
||||
|
||||
// Adds all debug instruction associated with |inst| to the work list.
|
||||
void AddDebugInstructionsToWorkList(const Instruction* inst);
|
||||
|
||||
// Marks all of the OpFunctionParameter instructions in |func| as live.
|
||||
void MarkFunctionParameterAsLive(const Function* func);
|
||||
@@ -164,8 +204,29 @@ class AggressiveDCEPass : public MemPass {
|
||||
// Returns true if |bb| is in the construct with header |header_block|.
|
||||
bool BlockIsInConstruct(BasicBlock* header_block, BasicBlock* bb);
|
||||
|
||||
// True if current function is entry point and has no function calls.
|
||||
bool private_like_local_;
|
||||
// Returns true if |func| is an entry point that does not have any function
|
||||
// calls.
|
||||
bool IsEntryPointWithNoCalls(Function* func);
|
||||
|
||||
// Returns true if |func| is an entry point.
|
||||
bool IsEntryPoint(Function* func);
|
||||
|
||||
// Returns true if |func| contains a function call.
|
||||
bool HasCall(Function* func);
|
||||
|
||||
// Marks the first block, which is the entry block, in |func| as live.
|
||||
void MarkFirstBlockAsLive(Function* func);
|
||||
|
||||
// Adds an OpUnreachable instruction at the end of |block|.
|
||||
void AddUnreachable(BasicBlock*& block);
|
||||
|
||||
// Marks the OpLoopMerge and the terminator in |basic_block| as live if
|
||||
// |basic_block| is a loop header.
|
||||
void MarkLoopConstructAsLiveIfLoopHeader(BasicBlock* basic_block);
|
||||
|
||||
// The cached results for |IsEntryPointWithNoCalls|. It maps the function's
|
||||
// result id to the return value.
|
||||
std::unordered_map<uint32_t, bool> entry_point_with_no_calls_cache_;
|
||||
|
||||
// Live Instruction Worklist. An instruction is added to this list
|
||||
// if it might have a side effect, either directly or indirectly.
|
||||
|
||||
@@ -44,7 +44,7 @@ bool BlockMergePass::MergeBlocks(Function* func) {
|
||||
Pass::Status BlockMergePass::Process() {
|
||||
// Process all entry point functions.
|
||||
ProcessFunction pfn = [this](Function* fp) { return MergeBlocks(fp); };
|
||||
bool modified = context()->ProcessEntryPointCallTree(pfn);
|
||||
bool modified = context()->ProcessReachableCallTree(pfn);
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
|
||||
@@ -171,12 +171,17 @@ void MergeWithSuccessor(IRContext* context, Function* func,
|
||||
// and OpBranchConditional.
|
||||
auto terminator = bi->terminator();
|
||||
auto& vec = terminator->dbg_line_insts();
|
||||
auto& new_vec = merge_inst->dbg_line_insts();
|
||||
new_vec.insert(new_vec.end(), vec.begin(), vec.end());
|
||||
terminator->ClearDbgLineInsts();
|
||||
for (auto& l_inst : new_vec)
|
||||
context->get_def_use_mgr()->AnalyzeInstDefUse(&l_inst);
|
||||
|
||||
if (vec.size() > 0) {
|
||||
merge_inst->ClearDbgLineInsts();
|
||||
auto& new_vec = merge_inst->dbg_line_insts();
|
||||
new_vec.insert(new_vec.end(), vec.begin(), vec.end());
|
||||
terminator->ClearDbgLineInsts();
|
||||
for (auto& l_inst : new_vec)
|
||||
context->get_def_use_mgr()->AnalyzeInstDefUse(&l_inst);
|
||||
}
|
||||
// Clear debug scope of terminator to avoid DebugScope
|
||||
// emitted between terminator and merge.
|
||||
terminator->SetDebugScope(DebugScope(kNoDebugScope, kNoInlinedAt));
|
||||
// Move the merge instruction to just before the terminator.
|
||||
merge_inst->InsertBefore(terminator);
|
||||
}
|
||||
|
||||
4
3rdparty/spirv-tools/source/opt/ccp_pass.cpp
vendored
4
3rdparty/spirv-tools/source/opt/ccp_pass.cpp
vendored
@@ -291,6 +291,10 @@ bool CCPPass::ReplaceValues() {
|
||||
}
|
||||
|
||||
bool CCPPass::PropagateConstants(Function* fp) {
|
||||
if (fp->IsDeclaration()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mark function parameters as varying.
|
||||
fp->ForEachParam([this](const Instruction* inst) {
|
||||
values_[inst->result_id()] = kVaryingSSAId;
|
||||
|
||||
@@ -34,6 +34,10 @@ Pass::Status CombineAccessChains::Process() {
|
||||
}
|
||||
|
||||
bool CombineAccessChains::ProcessFunction(Function& function) {
|
||||
if (function.IsDeclaration()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool modified = false;
|
||||
|
||||
cfg()->ForEachBlockInReversePostOrder(
|
||||
|
||||
@@ -217,7 +217,8 @@ Instruction* ConstantManager::BuildInstructionAndAddToModule(
|
||||
auto* new_inst_ptr = new_inst.get();
|
||||
*pos = pos->InsertBefore(std::move(new_inst));
|
||||
++(*pos);
|
||||
context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inst_ptr);
|
||||
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
|
||||
context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inst_ptr);
|
||||
MapConstantToInst(new_const, new_inst_ptr);
|
||||
return new_inst_ptr;
|
||||
}
|
||||
|
||||
@@ -340,7 +340,7 @@ Pass::Status ConvertToHalfPass::ProcessImpl() {
|
||||
Pass::ProcessFunction pfn = [this](Function* fp) {
|
||||
return ProcessFunction(fp);
|
||||
};
|
||||
bool modified = context()->ProcessEntryPointCallTree(pfn);
|
||||
bool modified = context()->ProcessReachableCallTree(pfn);
|
||||
// If modified, make sure module has Float16 capability
|
||||
if (modified) context()->AddCapability(SpvCapabilityFloat16);
|
||||
// Remove all RelaxedPrecision decorations from instructions and globals
|
||||
|
||||
@@ -40,6 +40,10 @@ bool IsDebugDeclareOrValue(Instruction* di) {
|
||||
Pass::Status CopyPropagateArrays::Process() {
|
||||
bool modified = false;
|
||||
for (Function& function : *get_module()) {
|
||||
if (function.IsDeclaration()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BasicBlock* entry_bb = &*function.begin();
|
||||
|
||||
for (auto var_inst = entry_bb->begin(); var_inst->opcode() == SpvOpVariable;
|
||||
|
||||
@@ -346,6 +346,7 @@ bool DeadBranchElimPass::FixPhiNodesInLiveBlocks(
|
||||
if (operands.size() == 4) {
|
||||
// First input data operands is at index 2.
|
||||
uint32_t replId = operands[2u].words[0];
|
||||
context()->KillNamesAndDecorates(inst->result_id());
|
||||
context()->ReplaceAllUsesWith(inst->result_id(), replId);
|
||||
iter = context()->KillInst(&*inst);
|
||||
} else {
|
||||
@@ -419,6 +420,10 @@ bool DeadBranchElimPass::EraseDeadBlocks(
|
||||
}
|
||||
|
||||
bool DeadBranchElimPass::EliminateDeadBranches(Function* func) {
|
||||
if (func->IsDeclaration()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool modified = false;
|
||||
std::unordered_set<BasicBlock*> live_blocks;
|
||||
modified |= MarkLiveBlocks(func, &live_blocks);
|
||||
|
||||
@@ -256,7 +256,7 @@ Pass::Status DeadInsertElimPass::Process() {
|
||||
ProcessFunction pfn = [this](Function* fp) {
|
||||
return EliminateDeadInserts(fp);
|
||||
};
|
||||
bool modified = context()->ProcessEntryPointCallTree(pfn);
|
||||
bool modified = context()->ProcessReachableCallTree(pfn);
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
|
||||
@@ -146,6 +146,25 @@ void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id,
|
||||
}
|
||||
}
|
||||
|
||||
// Create new constant directly into global value area, bypassing the
|
||||
// Constant manager. This is used when the DefUse or Constant managers
|
||||
// are invalid and cannot be regenerated due to the module being in an
|
||||
// inconsistant state e.g. in the middle of significant modification
|
||||
// such as inlining. Invalidate Constant and DefUse managers if used.
|
||||
uint32_t AddNewConstInGlobals(IRContext* context, uint32_t const_value) {
|
||||
uint32_t id = context->TakeNextId();
|
||||
std::unique_ptr<Instruction> new_const(new Instruction(
|
||||
context, SpvOpConstant, context->get_type_mgr()->GetUIntTypeId(), id,
|
||||
{
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER,
|
||||
{const_value}},
|
||||
}));
|
||||
context->module()->AddGlobalValue(std::move(new_const));
|
||||
context->InvalidateAnalyses(IRContext::kAnalysisConstants);
|
||||
context->InvalidateAnalyses(IRContext::kAnalysisDefUse);
|
||||
return id;
|
||||
}
|
||||
|
||||
uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
|
||||
const DebugScope& scope) {
|
||||
uint32_t setId = GetDbgSetImportId();
|
||||
@@ -194,10 +213,18 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
|
||||
line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex);
|
||||
|
||||
// If we need the line number as an ID, generate that constant now.
|
||||
// If Constant or DefUse managers are invalid, generate constant
|
||||
// directly into the global value section of the module; do not
|
||||
// use Constant manager which may attempt to invoke building of the
|
||||
// DefUse manager which cannot be done during inlining. The extra
|
||||
// constants that may be generated here is likely not significant
|
||||
// and will likely be cleaned up in later passes.
|
||||
if (line_number_type == spv_operand_type_t::SPV_OPERAND_TYPE_ID) {
|
||||
uint32_t line_id =
|
||||
context()->get_constant_mgr()->GetUIntConst(line_number);
|
||||
line_number = line_id;
|
||||
if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse) ||
|
||||
!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisConstants))
|
||||
line_number = AddNewConstInGlobals(context(), line_number);
|
||||
else
|
||||
line_number = context()->get_constant_mgr()->GetUIntConst(line_number);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -490,6 +490,14 @@ void DecorationManager::ForEachDecoration(
|
||||
});
|
||||
}
|
||||
|
||||
bool DecorationManager::HasDecoration(uint32_t id, uint32_t decoration) {
|
||||
bool has_decoration = false;
|
||||
ForEachDecoration(id, decoration, [&has_decoration](const Instruction&) {
|
||||
has_decoration = true;
|
||||
});
|
||||
return has_decoration;
|
||||
}
|
||||
|
||||
bool DecorationManager::FindDecoration(
|
||||
uint32_t id, uint32_t decoration,
|
||||
std::function<bool(const Instruction&)> f) {
|
||||
|
||||
@@ -90,6 +90,10 @@ class DecorationManager {
|
||||
bool AreDecorationsTheSame(const Instruction* inst1, const Instruction* inst2,
|
||||
bool ignore_target) const;
|
||||
|
||||
// Returns whether a decoration instruction for |id| with decoration
|
||||
// |decoration| exists or not.
|
||||
bool HasDecoration(uint32_t id, uint32_t decoration);
|
||||
|
||||
// |f| is run on each decoration instruction for |id| with decoration
|
||||
// |decoration|. Processed are all decorations which target |id| either
|
||||
// directly or indirectly by Decoration Groups.
|
||||
|
||||
119
3rdparty/spirv-tools/source/opt/desc_sroa.cpp
vendored
119
3rdparty/spirv-tools/source/opt/desc_sroa.cpp
vendored
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "source/opt/desc_sroa.h"
|
||||
|
||||
#include "source/opt/desc_sroa_util.h"
|
||||
#include "source/util/string_utils.h"
|
||||
|
||||
namespace spvtools {
|
||||
@@ -25,7 +26,7 @@ Pass::Status DescriptorScalarReplacement::Process() {
|
||||
std::vector<Instruction*> vars_to_kill;
|
||||
|
||||
for (Instruction& var : context()->types_values()) {
|
||||
if (IsCandidate(&var)) {
|
||||
if (descsroautil::IsDescriptorArray(context(), &var)) {
|
||||
modified = true;
|
||||
if (!ReplaceCandidate(&var)) {
|
||||
return Status::Failure;
|
||||
@@ -41,72 +42,6 @@ Pass::Status DescriptorScalarReplacement::Process() {
|
||||
return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
|
||||
}
|
||||
|
||||
bool DescriptorScalarReplacement::IsCandidate(Instruction* var) {
|
||||
if (var->opcode() != SpvOpVariable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t ptr_type_id = var->type_id();
|
||||
Instruction* ptr_type_inst =
|
||||
context()->get_def_use_mgr()->GetDef(ptr_type_id);
|
||||
if (ptr_type_inst->opcode() != SpvOpTypePointer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1);
|
||||
Instruction* var_type_inst =
|
||||
context()->get_def_use_mgr()->GetDef(var_type_id);
|
||||
if (var_type_inst->opcode() != SpvOpTypeArray &&
|
||||
var_type_inst->opcode() != SpvOpTypeStruct) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// All structures with descriptor assignments must be replaced by variables,
|
||||
// one for each of their members - with the exceptions of buffers.
|
||||
if (IsTypeOfStructuredBuffer(var_type_inst)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_desc_set_decoration = false;
|
||||
context()->get_decoration_mgr()->ForEachDecoration(
|
||||
var->result_id(), SpvDecorationDescriptorSet,
|
||||
[&has_desc_set_decoration](const Instruction&) {
|
||||
has_desc_set_decoration = true;
|
||||
});
|
||||
if (!has_desc_set_decoration) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_binding_decoration = false;
|
||||
context()->get_decoration_mgr()->ForEachDecoration(
|
||||
var->result_id(), SpvDecorationBinding,
|
||||
[&has_binding_decoration](const Instruction&) {
|
||||
has_binding_decoration = true;
|
||||
});
|
||||
if (!has_binding_decoration) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DescriptorScalarReplacement::IsTypeOfStructuredBuffer(
|
||||
const Instruction* type) const {
|
||||
if (type->opcode() != SpvOpTypeStruct) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// All buffers have offset decorations for members of their structure types.
|
||||
// This is how we distinguish it from a structure of descriptors.
|
||||
bool has_offset_decoration = false;
|
||||
context()->get_decoration_mgr()->ForEachDecoration(
|
||||
type->result_id(), SpvDecorationOffset,
|
||||
[&has_offset_decoration](const Instruction&) {
|
||||
has_offset_decoration = true;
|
||||
});
|
||||
return has_offset_decoration;
|
||||
}
|
||||
|
||||
bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) {
|
||||
std::vector<Instruction*> access_chain_work_list;
|
||||
std::vector<Instruction*> load_work_list;
|
||||
@@ -162,16 +97,15 @@ bool DescriptorScalarReplacement::ReplaceAccessChain(Instruction* var,
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t idx_id = use->GetSingleWordInOperand(1);
|
||||
const analysis::Constant* idx_const =
|
||||
context()->get_constant_mgr()->FindDeclaredConstant(idx_id);
|
||||
if (idx_const == nullptr) {
|
||||
const analysis::Constant* const_index =
|
||||
descsroautil::GetAccessChainIndexAsConst(context(), use);
|
||||
if (const_index == nullptr) {
|
||||
context()->EmitErrorMessage("Variable cannot be replaced: invalid index",
|
||||
use);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t idx = idx_const->GetU32();
|
||||
uint32_t idx = const_index->GetU32();
|
||||
uint32_t replacement_var = GetReplacementVariable(var, idx);
|
||||
|
||||
if (use->NumInOperands() == 2) {
|
||||
@@ -208,39 +142,12 @@ uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var,
|
||||
uint32_t idx) {
|
||||
auto replacement_vars = replacement_variables_.find(var);
|
||||
if (replacement_vars == replacement_variables_.end()) {
|
||||
uint32_t ptr_type_id = var->type_id();
|
||||
Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
|
||||
assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
|
||||
"Variable should be a pointer to an array or structure.");
|
||||
uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
|
||||
Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id);
|
||||
const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray;
|
||||
const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct;
|
||||
assert((is_array || is_struct) &&
|
||||
"Variable should be a pointer to an array or structure.");
|
||||
|
||||
// For arrays, each array element should be replaced with a new replacement
|
||||
// variable
|
||||
if (is_array) {
|
||||
uint32_t array_len_id = pointee_type_inst->GetSingleWordInOperand(1);
|
||||
const analysis::Constant* array_len_const =
|
||||
context()->get_constant_mgr()->FindDeclaredConstant(array_len_id);
|
||||
assert(array_len_const != nullptr && "Array length must be a constant.");
|
||||
uint32_t array_len = array_len_const->GetU32();
|
||||
|
||||
replacement_vars = replacement_variables_
|
||||
.insert({var, std::vector<uint32_t>(array_len, 0)})
|
||||
.first;
|
||||
}
|
||||
// For structures, each member should be replaced with a new replacement
|
||||
// variable
|
||||
if (is_struct) {
|
||||
const uint32_t num_members = pointee_type_inst->NumInOperands();
|
||||
replacement_vars =
|
||||
replacement_variables_
|
||||
.insert({var, std::vector<uint32_t>(num_members, 0)})
|
||||
.first;
|
||||
}
|
||||
uint32_t number_of_elements =
|
||||
descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var);
|
||||
replacement_vars =
|
||||
replacement_variables_
|
||||
.insert({var, std::vector<uint32_t>(number_of_elements, 0)})
|
||||
.first;
|
||||
}
|
||||
|
||||
if (replacement_vars->second[idx] == 0) {
|
||||
@@ -377,7 +284,7 @@ uint32_t DescriptorScalarReplacement::GetNumBindingsUsedByType(
|
||||
// The number of bindings consumed by a structure is the sum of the bindings
|
||||
// used by its members.
|
||||
if (type_inst->opcode() == SpvOpTypeStruct &&
|
||||
!IsTypeOfStructuredBuffer(type_inst)) {
|
||||
!descsroautil::IsTypeOfStructuredBuffer(context(), type_inst)) {
|
||||
uint32_t sum = 0;
|
||||
for (uint32_t i = 0; i < type_inst->NumInOperands(); i++)
|
||||
sum += GetNumBindingsUsedByType(type_inst->GetSingleWordInOperand(i));
|
||||
|
||||
9
3rdparty/spirv-tools/source/opt/desc_sroa.h
vendored
9
3rdparty/spirv-tools/source/opt/desc_sroa.h
vendored
@@ -46,10 +46,6 @@ class DescriptorScalarReplacement : public Pass {
|
||||
}
|
||||
|
||||
private:
|
||||
// Returns true if |var| is an OpVariable instruction that represents a
|
||||
// descriptor array. These are the variables that we want to replace.
|
||||
bool IsCandidate(Instruction* var);
|
||||
|
||||
// Replaces all references to |var| by new variables, one for each element of
|
||||
// the array |var|. The binding for the new variables corresponding to
|
||||
// element i will be the binding of |var| plus i. Returns true if successful.
|
||||
@@ -93,11 +89,6 @@ class DescriptorScalarReplacement : public Pass {
|
||||
// bindings used by its members.
|
||||
uint32_t GetNumBindingsUsedByType(uint32_t type_id);
|
||||
|
||||
// Returns true if |type| is a type that could be used for a structured buffer
|
||||
// as opposed to a type that would be used for a structure of resource
|
||||
// descriptors.
|
||||
bool IsTypeOfStructuredBuffer(const Instruction* type) const;
|
||||
|
||||
// A map from an OpVariable instruction to the set of variables that will be
|
||||
// used to replace it. The entry |replacement_variables_[var][i]| is the id of
|
||||
// a variable that will be used in the place of the the ith element of the
|
||||
|
||||
117
3rdparty/spirv-tools/source/opt/desc_sroa_util.cpp
vendored
Normal file
117
3rdparty/spirv-tools/source/opt/desc_sroa_util.cpp
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright (c) 2021 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/opt/desc_sroa_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
namespace {
|
||||
|
||||
const uint32_t kOpAccessChainInOperandIndexes = 1;
|
||||
|
||||
// Returns the length of array type |type|.
|
||||
uint32_t GetLengthOfArrayType(IRContext* context, Instruction* type) {
|
||||
assert(type->opcode() == SpvOpTypeArray && "type must be array");
|
||||
uint32_t length_id = type->GetSingleWordInOperand(1);
|
||||
const analysis::Constant* length_const =
|
||||
context->get_constant_mgr()->FindDeclaredConstant(length_id);
|
||||
assert(length_const != nullptr);
|
||||
return length_const->GetU32();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace descsroautil {
|
||||
|
||||
bool IsDescriptorArray(IRContext* context, Instruction* var) {
|
||||
if (var->opcode() != SpvOpVariable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t ptr_type_id = var->type_id();
|
||||
Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id);
|
||||
if (ptr_type_inst->opcode() != SpvOpTypePointer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1);
|
||||
Instruction* var_type_inst = context->get_def_use_mgr()->GetDef(var_type_id);
|
||||
if (var_type_inst->opcode() != SpvOpTypeArray &&
|
||||
var_type_inst->opcode() != SpvOpTypeStruct) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// All structures with descriptor assignments must be replaced by variables,
|
||||
// one for each of their members - with the exceptions of buffers.
|
||||
if (IsTypeOfStructuredBuffer(context, var_type_inst)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!context->get_decoration_mgr()->HasDecoration(
|
||||
var->result_id(), SpvDecorationDescriptorSet)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return context->get_decoration_mgr()->HasDecoration(var->result_id(),
|
||||
SpvDecorationBinding);
|
||||
}
|
||||
|
||||
bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type) {
|
||||
if (type->opcode() != SpvOpTypeStruct) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// All buffers have offset decorations for members of their structure types.
|
||||
// This is how we distinguish it from a structure of descriptors.
|
||||
return context->get_decoration_mgr()->HasDecoration(type->result_id(),
|
||||
SpvDecorationOffset);
|
||||
}
|
||||
|
||||
const analysis::Constant* GetAccessChainIndexAsConst(
|
||||
IRContext* context, Instruction* access_chain) {
|
||||
if (access_chain->NumInOperands() <= 1) {
|
||||
return nullptr;
|
||||
}
|
||||
uint32_t idx_id = GetFirstIndexOfAccessChain(access_chain);
|
||||
const analysis::Constant* idx_const =
|
||||
context->get_constant_mgr()->FindDeclaredConstant(idx_id);
|
||||
return idx_const;
|
||||
}
|
||||
|
||||
uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain) {
|
||||
assert(access_chain->NumInOperands() > 1 &&
|
||||
"OpAccessChain does not have Indexes operand");
|
||||
return access_chain->GetSingleWordInOperand(kOpAccessChainInOperandIndexes);
|
||||
}
|
||||
|
||||
uint32_t GetNumberOfElementsForArrayOrStruct(IRContext* context,
|
||||
Instruction* var) {
|
||||
uint32_t ptr_type_id = var->type_id();
|
||||
Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id);
|
||||
assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
|
||||
"Variable should be a pointer to an array or structure.");
|
||||
uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
|
||||
Instruction* pointee_type_inst =
|
||||
context->get_def_use_mgr()->GetDef(pointee_type_id);
|
||||
if (pointee_type_inst->opcode() == SpvOpTypeArray) {
|
||||
return GetLengthOfArrayType(context, pointee_type_inst);
|
||||
}
|
||||
assert(pointee_type_inst->opcode() == SpvOpTypeStruct &&
|
||||
"Variable should be a pointer to an array or structure.");
|
||||
return pointee_type_inst->NumInOperands();
|
||||
}
|
||||
|
||||
} // namespace descsroautil
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
54
3rdparty/spirv-tools/source/opt/desc_sroa_util.h
vendored
Normal file
54
3rdparty/spirv-tools/source/opt/desc_sroa_util.h
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2021 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_OPT_DESC_SROA_UTIL_H_
|
||||
#define SOURCE_OPT_DESC_SROA_UTIL_H_
|
||||
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
// Provides functions for the descriptor array SROA.
|
||||
namespace descsroautil {
|
||||
|
||||
// Returns true if |var| is an OpVariable instruction that represents a
|
||||
// descriptor array.
|
||||
bool IsDescriptorArray(IRContext* context, Instruction* var);
|
||||
|
||||
// Returns true if |type| is a type that could be used for a structured buffer
|
||||
// as opposed to a type that would be used for a structure of resource
|
||||
// descriptors.
|
||||
bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type);
|
||||
|
||||
// Returns the first index of the OpAccessChain instruction |access_chain| as
|
||||
// a constant. Returns nullptr if it is not a constant.
|
||||
const analysis::Constant* GetAccessChainIndexAsConst(IRContext* context,
|
||||
Instruction* access_chain);
|
||||
|
||||
// Returns the number of elements of an OpVariable instruction |var| whose type
|
||||
// must be a pointer to an array or a struct.
|
||||
uint32_t GetNumberOfElementsForArrayOrStruct(IRContext* context,
|
||||
Instruction* var);
|
||||
|
||||
// Returns the first Indexes operand id of the OpAccessChain or
|
||||
// OpInBoundsAccessChain instruction |access_chain|. The access chain must have
|
||||
// at least 1 index.
|
||||
uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain);
|
||||
|
||||
} // namespace descsroautil
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_OPT_DESC_SROA_UTIL_H_
|
||||
@@ -968,25 +968,17 @@ FoldingRule MergeDivMulArithmetic() {
|
||||
FoldingRule MergeDivNegateArithmetic() {
|
||||
return [](IRContext* context, Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == SpvOpFDiv || inst->opcode() == SpvOpSDiv);
|
||||
assert(inst->opcode() == SpvOpFDiv);
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
bool uses_float = HasFloatingPoint(type);
|
||||
if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false;
|
||||
|
||||
uint32_t width = ElementWidth(type);
|
||||
if (width != 32 && width != 64) return false;
|
||||
if (!inst->IsFloatingPointFoldingAllowed()) return false;
|
||||
|
||||
const analysis::Constant* const_input1 = ConstInput(constants);
|
||||
if (!const_input1) return false;
|
||||
Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
|
||||
return false;
|
||||
if (!other_inst->IsFloatingPointFoldingAllowed()) return false;
|
||||
|
||||
bool first_is_variable = constants[0] == nullptr;
|
||||
if (other_inst->opcode() == SpvOpFNegate ||
|
||||
other_inst->opcode() == SpvOpSNegate) {
|
||||
if (other_inst->opcode() == SpvOpFNegate) {
|
||||
uint32_t neg_id = NegateConstant(const_mgr, const_input1);
|
||||
|
||||
if (first_is_variable) {
|
||||
@@ -2589,8 +2581,6 @@ void FoldingRules::AddFoldingRules() {
|
||||
|
||||
rules_[SpvOpPhi].push_back(RedundantPhi());
|
||||
|
||||
rules_[SpvOpSDiv].push_back(MergeDivNegateArithmetic());
|
||||
|
||||
rules_[SpvOpSNegate].push_back(MergeNegateArithmetic());
|
||||
rules_[SpvOpSNegate].push_back(MergeNegateMulDivArithmetic());
|
||||
rules_[SpvOpSNegate].push_back(MergeNegateAddSubArithmetic());
|
||||
|
||||
3
3rdparty/spirv-tools/source/opt/function.h
vendored
3
3rdparty/spirv-tools/source/opt/function.h
vendored
@@ -177,6 +177,9 @@ class Function {
|
||||
// debuggers.
|
||||
void Dump() const;
|
||||
|
||||
// Returns true is a function declaration and not a function definition.
|
||||
bool IsDeclaration() { return begin() == end(); }
|
||||
|
||||
private:
|
||||
// The OpFunction instruction that begins the definition of this function.
|
||||
std::unique_ptr<Instruction> def_inst_;
|
||||
|
||||
@@ -65,7 +65,7 @@ Pass::Status InlineExhaustivePass::ProcessImpl() {
|
||||
status = CombineStatus(status, InlineExhaustive(fp));
|
||||
return false;
|
||||
};
|
||||
context()->ProcessEntryPointCallTree(pfn);
|
||||
context()->ProcessReachableCallTree(pfn);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ Pass::Status InlineOpaquePass::ProcessImpl() {
|
||||
status = CombineStatus(status, InlineOpaque(fp));
|
||||
return false;
|
||||
};
|
||||
context()->ProcessEntryPointCallTree(pfn);
|
||||
context()->ProcessReachableCallTree(pfn);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
2
3rdparty/spirv-tools/source/opt/iterator.h
vendored
2
3rdparty/spirv-tools/source/opt/iterator.h
vendored
@@ -142,7 +142,7 @@ inline IteratorRange<IteratorType> make_range(const IteratorType& begin,
|
||||
template <typename IteratorType>
|
||||
inline IteratorRange<IteratorType> make_range(IteratorType&& begin,
|
||||
IteratorType&& end) {
|
||||
return {std::move(begin), std::move(end)};
|
||||
return {std::forward<IteratorType>(begin), std::forward<IteratorType>(end)};
|
||||
}
|
||||
|
||||
// Returns a (begin, end) iterator pair for the given container.
|
||||
|
||||
@@ -223,7 +223,7 @@ Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() {
|
||||
return LocalSingleBlockLoadStoreElim(fp);
|
||||
};
|
||||
|
||||
bool modified = context()->ProcessEntryPointCallTree(pfn);
|
||||
bool modified = context()->ProcessReachableCallTree(pfn);
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ Pass::Status LocalSingleStoreElimPass::ProcessImpl() {
|
||||
ProcessFunction pfn = [this](Function* fp) {
|
||||
return LocalSingleStoreElim(fp);
|
||||
};
|
||||
bool modified = context()->ProcessEntryPointCallTree(pfn);
|
||||
bool modified = context()->ProcessReachableCallTree(pfn);
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
|
||||
@@ -1102,6 +1102,10 @@ void LoopUtils::Finalize() {
|
||||
Pass::Status LoopUnroller::Process() {
|
||||
bool changed = false;
|
||||
for (Function& f : *context()->module()) {
|
||||
if (f.IsDeclaration()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LoopDescriptor* LD = context()->GetLoopDescriptor(&f);
|
||||
for (Loop& loop : *LD) {
|
||||
LoopUtils loop_utils{context(), &loop};
|
||||
|
||||
2
3rdparty/spirv-tools/source/opt/mem_pass.cpp
vendored
2
3rdparty/spirv-tools/source/opt/mem_pass.cpp
vendored
@@ -86,8 +86,8 @@ bool MemPass::IsPtr(uint32_t ptrId) {
|
||||
}
|
||||
const SpvOp op = ptrInst->opcode();
|
||||
if (op == SpvOpVariable || IsNonPtrAccessChain(op)) return true;
|
||||
if (op != SpvOpFunctionParameter) return false;
|
||||
const uint32_t varTypeId = ptrInst->type_id();
|
||||
if (varTypeId == 0) return false;
|
||||
const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
|
||||
return varTypeInst->opcode() == SpvOpTypePointer;
|
||||
}
|
||||
|
||||
@@ -320,6 +320,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
|
||||
RegisterPass(CreateCombineAccessChainsPass());
|
||||
} else if (pass_name == "convert-local-access-chains") {
|
||||
RegisterPass(CreateLocalAccessChainConvertPass());
|
||||
} else if (pass_name == "replace-desc-array-access-using-var-index") {
|
||||
RegisterPass(CreateReplaceDescArrayAccessUsingVarIndexPass());
|
||||
} else if (pass_name == "descriptor-scalar-replacement") {
|
||||
RegisterPass(CreateDescriptorScalarReplacementPass());
|
||||
} else if (pass_name == "eliminate-dead-code-aggressive") {
|
||||
@@ -958,6 +960,11 @@ Optimizer::PassToken CreateGraphicsRobustAccessPass() {
|
||||
MakeUnique<opt::GraphicsRobustAccessPass>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::ReplaceDescArrayAccessUsingVarIndex>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::DescriptorScalarReplacement>());
|
||||
|
||||
1
3rdparty/spirv-tools/source/opt/passes.h
vendored
1
3rdparty/spirv-tools/source/opt/passes.h
vendored
@@ -66,6 +66,7 @@
|
||||
#include "source/opt/relax_float_ops_pass.h"
|
||||
#include "source/opt/remove_duplicates_pass.h"
|
||||
#include "source/opt/remove_unused_interface_variables_pass.h"
|
||||
#include "source/opt/replace_desc_array_access_using_var_index.h"
|
||||
#include "source/opt/replace_invalid_opc.h"
|
||||
#include "source/opt/scalar_replacement_pass.h"
|
||||
#include "source/opt/set_spec_constant_default_value_pass.h"
|
||||
|
||||
@@ -24,6 +24,10 @@ Pass::Status RedundancyEliminationPass::Process() {
|
||||
ValueNumberTable vnTable(context());
|
||||
|
||||
for (auto& func : *get_module()) {
|
||||
if (func.IsDeclaration()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Build the dominator tree for this function. It is how the code is
|
||||
// traversed.
|
||||
DominatorTree& dom_tree =
|
||||
|
||||
3
3rdparty/spirv-tools/source/opt/reflect.h
vendored
3
3rdparty/spirv-tools/source/opt/reflect.h
vendored
@@ -51,7 +51,8 @@ inline bool IsTypeInst(SpvOp opcode) {
|
||||
opcode == SpvOpTypeCooperativeMatrixNV;
|
||||
}
|
||||
inline bool IsConstantInst(SpvOp opcode) {
|
||||
return opcode >= SpvOpConstantTrue && opcode <= SpvOpSpecConstantOp;
|
||||
return (opcode >= SpvOpConstantTrue && opcode <= SpvOpSpecConstantOp) ||
|
||||
opcode == SpvOpConstFunctionPointerINTEL;
|
||||
}
|
||||
inline bool IsCompileTimeConstantInst(SpvOp opcode) {
|
||||
return opcode >= SpvOpConstantTrue && opcode <= SpvOpConstantNull;
|
||||
|
||||
@@ -76,7 +76,7 @@ Pass::Status RelaxFloatOpsPass::ProcessImpl() {
|
||||
Pass::ProcessFunction pfn = [this](Function* fp) {
|
||||
return ProcessFunction(fp);
|
||||
};
|
||||
bool modified = context()->ProcessEntryPointCallTree(pfn);
|
||||
bool modified = context()->ProcessReachableCallTree(pfn);
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
|
||||
423
3rdparty/spirv-tools/source/opt/replace_desc_array_access_using_var_index.cpp
vendored
Normal file
423
3rdparty/spirv-tools/source/opt/replace_desc_array_access_using_var_index.cpp
vendored
Normal file
@@ -0,0 +1,423 @@
|
||||
// Copyright (c) 2021 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/opt/replace_desc_array_access_using_var_index.h"
|
||||
|
||||
#include "source/opt/desc_sroa_util.h"
|
||||
#include "source/opt/ir_builder.h"
|
||||
#include "source/util/string_utils.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
namespace {
|
||||
|
||||
const uint32_t kOpAccessChainInOperandIndexes = 1;
|
||||
const uint32_t kOpTypePointerInOperandType = 1;
|
||||
const uint32_t kOpTypeArrayInOperandType = 0;
|
||||
const uint32_t kOpTypeStructInOperandMember = 0;
|
||||
IRContext::Analysis kAnalysisDefUseAndInstrToBlockMapping =
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping;
|
||||
|
||||
uint32_t GetValueWithKeyExistenceCheck(
|
||||
uint32_t key, const std::unordered_map<uint32_t, uint32_t>& map) {
|
||||
auto itr = map.find(key);
|
||||
assert(itr != map.end() && "Key does not exist");
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Pass::Status ReplaceDescArrayAccessUsingVarIndex::Process() {
|
||||
Status status = Status::SuccessWithoutChange;
|
||||
for (Instruction& var : context()->types_values()) {
|
||||
if (descsroautil::IsDescriptorArray(context(), &var)) {
|
||||
if (ReplaceVariableAccessesWithConstantElements(&var))
|
||||
status = Status::SuccessWithChange;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
bool ReplaceDescArrayAccessUsingVarIndex::
|
||||
ReplaceVariableAccessesWithConstantElements(Instruction* var) const {
|
||||
std::vector<Instruction*> work_list;
|
||||
get_def_use_mgr()->ForEachUser(var, [&work_list](Instruction* use) {
|
||||
switch (use->opcode()) {
|
||||
case SpvOpAccessChain:
|
||||
case SpvOpInBoundsAccessChain:
|
||||
work_list.push_back(use);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
bool updated = false;
|
||||
for (Instruction* access_chain : work_list) {
|
||||
if (descsroautil::GetAccessChainIndexAsConst(context(), access_chain) ==
|
||||
nullptr) {
|
||||
ReplaceAccessChain(var, access_chain);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
// Note that we do not consider OpLoad and OpCompositeExtract because
|
||||
// OpCompositeExtract always has constant literals for indices.
|
||||
return updated;
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain(
|
||||
Instruction* var, Instruction* access_chain) const {
|
||||
uint32_t number_of_elements =
|
||||
descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var);
|
||||
assert(number_of_elements != 0 && "Number of element is 0");
|
||||
if (number_of_elements == 1) {
|
||||
UseConstIndexForAccessChain(access_chain, 0);
|
||||
get_def_use_mgr()->AnalyzeInstUse(access_chain);
|
||||
return;
|
||||
}
|
||||
ReplaceUsersOfAccessChain(access_chain, number_of_elements);
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::ReplaceUsersOfAccessChain(
|
||||
Instruction* access_chain, uint32_t number_of_elements) const {
|
||||
std::vector<Instruction*> final_users;
|
||||
CollectRecursiveUsersWithConcreteType(access_chain, &final_users);
|
||||
for (auto* inst : final_users) {
|
||||
std::deque<Instruction*> insts_to_be_cloned =
|
||||
CollectRequiredImageInsts(inst);
|
||||
ReplaceNonUniformAccessWithSwitchCase(
|
||||
inst, access_chain, number_of_elements, insts_to_be_cloned);
|
||||
}
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::CollectRecursiveUsersWithConcreteType(
|
||||
Instruction* access_chain, std::vector<Instruction*>* final_users) const {
|
||||
std::queue<Instruction*> work_list;
|
||||
work_list.push(access_chain);
|
||||
while (!work_list.empty()) {
|
||||
auto* inst_from_work_list = work_list.front();
|
||||
work_list.pop();
|
||||
get_def_use_mgr()->ForEachUser(
|
||||
inst_from_work_list, [this, final_users, &work_list](Instruction* use) {
|
||||
// TODO: Support Boolean type as well.
|
||||
if (!use->HasResultId() || IsConcreteType(use->type_id())) {
|
||||
final_users->push_back(use);
|
||||
} else {
|
||||
work_list.push(use);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::deque<Instruction*>
|
||||
ReplaceDescArrayAccessUsingVarIndex::CollectRequiredImageInsts(
|
||||
Instruction* user_of_image_insts) const {
|
||||
std::unordered_set<uint32_t> seen_inst_ids;
|
||||
std::queue<Instruction*> work_list;
|
||||
|
||||
auto decision_to_include_operand = [this, &seen_inst_ids,
|
||||
&work_list](uint32_t* idp) {
|
||||
if (!seen_inst_ids.insert(*idp).second) return;
|
||||
Instruction* operand = get_def_use_mgr()->GetDef(*idp);
|
||||
if (context()->get_instr_block(operand) != nullptr &&
|
||||
HasImageOrImagePtrType(operand)) {
|
||||
work_list.push(operand);
|
||||
}
|
||||
};
|
||||
|
||||
std::deque<Instruction*> required_image_insts;
|
||||
required_image_insts.push_front(user_of_image_insts);
|
||||
user_of_image_insts->ForEachInId(decision_to_include_operand);
|
||||
while (!work_list.empty()) {
|
||||
auto* inst_from_work_list = work_list.front();
|
||||
work_list.pop();
|
||||
required_image_insts.push_front(inst_from_work_list);
|
||||
inst_from_work_list->ForEachInId(decision_to_include_operand);
|
||||
}
|
||||
return required_image_insts;
|
||||
}
|
||||
|
||||
bool ReplaceDescArrayAccessUsingVarIndex::HasImageOrImagePtrType(
|
||||
const Instruction* inst) const {
|
||||
assert(inst != nullptr && inst->type_id() != 0 && "Invalid instruction");
|
||||
return IsImageOrImagePtrType(get_def_use_mgr()->GetDef(inst->type_id()));
|
||||
}
|
||||
|
||||
bool ReplaceDescArrayAccessUsingVarIndex::IsImageOrImagePtrType(
|
||||
const Instruction* type_inst) const {
|
||||
if (type_inst->opcode() == SpvOpTypeImage ||
|
||||
type_inst->opcode() == SpvOpTypeSampler ||
|
||||
type_inst->opcode() == SpvOpTypeSampledImage) {
|
||||
return true;
|
||||
}
|
||||
if (type_inst->opcode() == SpvOpTypePointer) {
|
||||
Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(
|
||||
type_inst->GetSingleWordInOperand(kOpTypePointerInOperandType));
|
||||
return IsImageOrImagePtrType(pointee_type_inst);
|
||||
}
|
||||
if (type_inst->opcode() == SpvOpTypeArray) {
|
||||
Instruction* element_type_inst = get_def_use_mgr()->GetDef(
|
||||
type_inst->GetSingleWordInOperand(kOpTypeArrayInOperandType));
|
||||
return IsImageOrImagePtrType(element_type_inst);
|
||||
}
|
||||
if (type_inst->opcode() != SpvOpTypeStruct) return false;
|
||||
for (uint32_t in_operand_idx = kOpTypeStructInOperandMember;
|
||||
in_operand_idx < type_inst->NumInOperands(); ++in_operand_idx) {
|
||||
Instruction* member_type_inst = get_def_use_mgr()->GetDef(
|
||||
type_inst->GetSingleWordInOperand(kOpTypeStructInOperandMember));
|
||||
if (IsImageOrImagePtrType(member_type_inst)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReplaceDescArrayAccessUsingVarIndex::IsConcreteType(
|
||||
uint32_t type_id) const {
|
||||
Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
|
||||
if (type_inst->opcode() == SpvOpTypeInt ||
|
||||
type_inst->opcode() == SpvOpTypeFloat) {
|
||||
return true;
|
||||
}
|
||||
if (type_inst->opcode() == SpvOpTypeVector ||
|
||||
type_inst->opcode() == SpvOpTypeMatrix ||
|
||||
type_inst->opcode() == SpvOpTypeArray) {
|
||||
return IsConcreteType(type_inst->GetSingleWordInOperand(0));
|
||||
}
|
||||
if (type_inst->opcode() == SpvOpTypeStruct) {
|
||||
for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) {
|
||||
if (!IsConcreteType(type_inst->GetSingleWordInOperand(i))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateCaseBlock(
|
||||
Instruction* access_chain, uint32_t element_index,
|
||||
const std::deque<Instruction*>& insts_to_be_cloned,
|
||||
uint32_t branch_target_id,
|
||||
std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
|
||||
auto* case_block = CreateNewBlock();
|
||||
AddConstElementAccessToCaseBlock(case_block, access_chain, element_index,
|
||||
old_ids_to_new_ids);
|
||||
CloneInstsToBlock(case_block, access_chain, insts_to_be_cloned,
|
||||
old_ids_to_new_ids);
|
||||
AddBranchToBlock(case_block, branch_target_id);
|
||||
UseNewIdsInBlock(case_block, *old_ids_to_new_ids);
|
||||
return case_block;
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock(
|
||||
BasicBlock* block, Instruction* inst_to_skip_cloning,
|
||||
const std::deque<Instruction*>& insts_to_be_cloned,
|
||||
std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
|
||||
for (auto* inst_to_be_cloned : insts_to_be_cloned) {
|
||||
if (inst_to_be_cloned == inst_to_skip_cloning) continue;
|
||||
std::unique_ptr<Instruction> clone(inst_to_be_cloned->Clone(context()));
|
||||
if (inst_to_be_cloned->HasResultId()) {
|
||||
uint32_t new_id = context()->TakeNextId();
|
||||
clone->SetResultId(new_id);
|
||||
(*old_ids_to_new_ids)[inst_to_be_cloned->result_id()] = new_id;
|
||||
}
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(clone.get());
|
||||
context()->set_instr_block(clone.get(), block);
|
||||
block->AddInstruction(std::move(clone));
|
||||
}
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::UseNewIdsInBlock(
|
||||
BasicBlock* block,
|
||||
const std::unordered_map<uint32_t, uint32_t>& old_ids_to_new_ids) const {
|
||||
for (auto block_itr = block->begin(); block_itr != block->end();
|
||||
++block_itr) {
|
||||
(&*block_itr)->ForEachInId([&old_ids_to_new_ids](uint32_t* idp) {
|
||||
auto old_ids_to_new_ids_itr = old_ids_to_new_ids.find(*idp);
|
||||
if (old_ids_to_new_ids_itr == old_ids_to_new_ids.end()) return;
|
||||
*idp = old_ids_to_new_ids_itr->second;
|
||||
});
|
||||
get_def_use_mgr()->AnalyzeInstUse(&*block_itr);
|
||||
}
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase(
|
||||
Instruction* access_chain_final_user, Instruction* access_chain,
|
||||
uint32_t number_of_elements,
|
||||
const std::deque<Instruction*>& insts_to_be_cloned) const {
|
||||
// Create merge block and add terminator
|
||||
auto* block = context()->get_instr_block(access_chain_final_user);
|
||||
auto* merge_block = SeparateInstructionsIntoNewBlock(
|
||||
block, access_chain_final_user->NextNode());
|
||||
|
||||
auto* function = block->GetParent();
|
||||
|
||||
// Add case blocks
|
||||
std::vector<uint32_t> phi_operands;
|
||||
std::vector<uint32_t> case_block_ids;
|
||||
for (uint32_t idx = 0; idx < number_of_elements; ++idx) {
|
||||
std::unordered_map<uint32_t, uint32_t> old_ids_to_new_ids_for_cloned_insts;
|
||||
std::unique_ptr<BasicBlock> case_block(CreateCaseBlock(
|
||||
access_chain, idx, insts_to_be_cloned, merge_block->id(),
|
||||
&old_ids_to_new_ids_for_cloned_insts));
|
||||
case_block_ids.push_back(case_block->id());
|
||||
function->InsertBasicBlockBefore(std::move(case_block), merge_block);
|
||||
|
||||
// Keep the operand for OpPhi
|
||||
if (!access_chain_final_user->HasResultId()) continue;
|
||||
uint32_t phi_operand =
|
||||
GetValueWithKeyExistenceCheck(access_chain_final_user->result_id(),
|
||||
old_ids_to_new_ids_for_cloned_insts);
|
||||
phi_operands.push_back(phi_operand);
|
||||
}
|
||||
|
||||
// Create default block
|
||||
std::unique_ptr<BasicBlock> default_block(
|
||||
CreateDefaultBlock(access_chain_final_user->HasResultId(), &phi_operands,
|
||||
merge_block->id()));
|
||||
uint32_t default_block_id = default_block->id();
|
||||
function->InsertBasicBlockBefore(std::move(default_block), merge_block);
|
||||
|
||||
// Create OpSwitch
|
||||
uint32_t access_chain_index_var_id =
|
||||
descsroautil::GetFirstIndexOfAccessChain(access_chain);
|
||||
AddSwitchForAccessChain(block, access_chain_index_var_id, default_block_id,
|
||||
merge_block->id(), case_block_ids);
|
||||
|
||||
// Create phi instructions
|
||||
if (!phi_operands.empty()) {
|
||||
uint32_t phi_id = CreatePhiInstruction(merge_block, phi_operands,
|
||||
case_block_ids, default_block_id);
|
||||
context()->ReplaceAllUsesWith(access_chain_final_user->result_id(), phi_id);
|
||||
}
|
||||
|
||||
// Replace OpPhi incoming block operand that uses |block| with |merge_block|
|
||||
ReplacePhiIncomingBlock(block->id(), merge_block->id());
|
||||
}
|
||||
|
||||
BasicBlock*
|
||||
ReplaceDescArrayAccessUsingVarIndex::SeparateInstructionsIntoNewBlock(
|
||||
BasicBlock* block, Instruction* separation_begin_inst) const {
|
||||
auto separation_begin = block->begin();
|
||||
while (separation_begin != block->end() &&
|
||||
&*separation_begin != separation_begin_inst) {
|
||||
++separation_begin;
|
||||
}
|
||||
return block->SplitBasicBlock(context(), context()->TakeNextId(),
|
||||
separation_begin);
|
||||
}
|
||||
|
||||
BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateNewBlock() const {
|
||||
auto* new_block = new BasicBlock(std::unique_ptr<Instruction>(
|
||||
new Instruction(context(), SpvOpLabel, 0, context()->TakeNextId(), {})));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(new_block->GetLabelInst());
|
||||
context()->set_instr_block(new_block->GetLabelInst(), new_block);
|
||||
return new_block;
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::UseConstIndexForAccessChain(
|
||||
Instruction* access_chain, uint32_t const_element_idx) const {
|
||||
uint32_t const_element_idx_id =
|
||||
context()->get_constant_mgr()->GetUIntConst(const_element_idx);
|
||||
access_chain->SetInOperand(kOpAccessChainInOperandIndexes,
|
||||
{const_element_idx_id});
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::AddConstElementAccessToCaseBlock(
|
||||
BasicBlock* case_block, Instruction* access_chain,
|
||||
uint32_t const_element_idx,
|
||||
std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
|
||||
std::unique_ptr<Instruction> access_clone(access_chain->Clone(context()));
|
||||
UseConstIndexForAccessChain(access_clone.get(), const_element_idx);
|
||||
|
||||
uint32_t new_access_id = context()->TakeNextId();
|
||||
(*old_ids_to_new_ids)[access_clone->result_id()] = new_access_id;
|
||||
access_clone->SetResultId(new_access_id);
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(access_clone.get());
|
||||
|
||||
context()->set_instr_block(access_clone.get(), case_block);
|
||||
case_block->AddInstruction(std::move(access_clone));
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::AddBranchToBlock(
|
||||
BasicBlock* parent_block, uint32_t branch_destination) const {
|
||||
InstructionBuilder builder{context(), parent_block,
|
||||
kAnalysisDefUseAndInstrToBlockMapping};
|
||||
builder.AddBranch(branch_destination);
|
||||
}
|
||||
|
||||
BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateDefaultBlock(
|
||||
bool null_const_for_phi_is_needed, std::vector<uint32_t>* phi_operands,
|
||||
uint32_t merge_block_id) const {
|
||||
auto* default_block = CreateNewBlock();
|
||||
AddBranchToBlock(default_block, merge_block_id);
|
||||
if (!null_const_for_phi_is_needed) return default_block;
|
||||
|
||||
// Create null value for OpPhi
|
||||
Instruction* inst = context()->get_def_use_mgr()->GetDef((*phi_operands)[0]);
|
||||
auto* null_const_inst = GetConstNull(inst->type_id());
|
||||
phi_operands->push_back(null_const_inst->result_id());
|
||||
return default_block;
|
||||
}
|
||||
|
||||
Instruction* ReplaceDescArrayAccessUsingVarIndex::GetConstNull(
|
||||
uint32_t type_id) const {
|
||||
assert(type_id != 0 && "Result type is expected");
|
||||
auto* type = context()->get_type_mgr()->GetType(type_id);
|
||||
auto* null_const = context()->get_constant_mgr()->GetConstant(type, {});
|
||||
return context()->get_constant_mgr()->GetDefiningInstruction(null_const);
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::AddSwitchForAccessChain(
|
||||
BasicBlock* parent_block, uint32_t access_chain_index_var_id,
|
||||
uint32_t default_id, uint32_t merge_id,
|
||||
const std::vector<uint32_t>& case_block_ids) const {
|
||||
InstructionBuilder builder{context(), parent_block,
|
||||
kAnalysisDefUseAndInstrToBlockMapping};
|
||||
std::vector<std::pair<Operand::OperandData, uint32_t>> cases;
|
||||
for (uint32_t i = 0; i < static_cast<uint32_t>(case_block_ids.size()); ++i) {
|
||||
cases.emplace_back(Operand::OperandData{i}, case_block_ids[i]);
|
||||
}
|
||||
builder.AddSwitch(access_chain_index_var_id, default_id, cases, merge_id);
|
||||
}
|
||||
|
||||
uint32_t ReplaceDescArrayAccessUsingVarIndex::CreatePhiInstruction(
|
||||
BasicBlock* parent_block, const std::vector<uint32_t>& phi_operands,
|
||||
const std::vector<uint32_t>& case_block_ids,
|
||||
uint32_t default_block_id) const {
|
||||
std::vector<uint32_t> incomings;
|
||||
assert(case_block_ids.size() + 1 == phi_operands.size() &&
|
||||
"Number of Phi operands must be exactly 1 bigger than the one of case "
|
||||
"blocks");
|
||||
for (size_t i = 0; i < case_block_ids.size(); ++i) {
|
||||
incomings.push_back(phi_operands[i]);
|
||||
incomings.push_back(case_block_ids[i]);
|
||||
}
|
||||
incomings.push_back(phi_operands.back());
|
||||
incomings.push_back(default_block_id);
|
||||
|
||||
InstructionBuilder builder{context(), &*parent_block->begin(),
|
||||
kAnalysisDefUseAndInstrToBlockMapping};
|
||||
uint32_t phi_result_type_id =
|
||||
context()->get_def_use_mgr()->GetDef(phi_operands[0])->type_id();
|
||||
auto* phi = builder.AddPhi(phi_result_type_id, incomings);
|
||||
return phi->result_id();
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::ReplacePhiIncomingBlock(
|
||||
uint32_t old_incoming_block_id, uint32_t new_incoming_block_id) const {
|
||||
context()->ReplaceAllUsesWithPredicate(
|
||||
old_incoming_block_id, new_incoming_block_id,
|
||||
[](Instruction* use) { return use->opcode() == SpvOpPhi; });
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
204
3rdparty/spirv-tools/source/opt/replace_desc_array_access_using_var_index.h
vendored
Normal file
204
3rdparty/spirv-tools/source/opt/replace_desc_array_access_using_var_index.h
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
// Copyright (c) 2021 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_
|
||||
#define SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_
|
||||
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "source/opt/function.h"
|
||||
#include "source/opt/pass.h"
|
||||
#include "source/opt/type_manager.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
// See optimizer.hpp for documentation.
|
||||
class ReplaceDescArrayAccessUsingVarIndex : public Pass {
|
||||
public:
|
||||
ReplaceDescArrayAccessUsingVarIndex() {}
|
||||
|
||||
const char* name() const override {
|
||||
return "replace-desc-array-access-using-var-index";
|
||||
}
|
||||
|
||||
Status Process() override;
|
||||
|
||||
IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return IRContext::kAnalysisDefUse |
|
||||
IRContext::kAnalysisInstrToBlockMapping |
|
||||
IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
|
||||
}
|
||||
|
||||
private:
|
||||
// Replaces all acceses to |var| using variable indices with constant
|
||||
// elements of the array |var|. Creates switch-case statements to determine
|
||||
// the value of the variable index for all the possible cases. Returns
|
||||
// whether replacement is done or not.
|
||||
bool ReplaceVariableAccessesWithConstantElements(Instruction* var) const;
|
||||
|
||||
// Replaces the OpAccessChain or OpInBoundsAccessChain instruction |use| that
|
||||
// uses the descriptor variable |var| with the OpAccessChain or
|
||||
// OpInBoundsAccessChain instruction with a constant Indexes operand.
|
||||
void ReplaceAccessChain(Instruction* var, Instruction* use) const;
|
||||
|
||||
// Updates the first Indexes operand of the OpAccessChain or
|
||||
// OpInBoundsAccessChain instruction |access_chain| to let it use a constant
|
||||
// index |const_element_idx|.
|
||||
void UseConstIndexForAccessChain(Instruction* access_chain,
|
||||
uint32_t const_element_idx) const;
|
||||
|
||||
// Replaces users of the OpAccessChain or OpInBoundsAccessChain instruction
|
||||
// |access_chain| that accesses an array descriptor variable using variable
|
||||
// indices with constant elements. |number_of_elements| is the number
|
||||
// of array elements.
|
||||
void ReplaceUsersOfAccessChain(Instruction* access_chain,
|
||||
uint32_t number_of_elements) const;
|
||||
|
||||
// Puts all the recursive users of |access_chain| with concrete result types
|
||||
// or the ones without result it in |final_users|.
|
||||
void CollectRecursiveUsersWithConcreteType(
|
||||
Instruction* access_chain, std::vector<Instruction*>* final_users) const;
|
||||
|
||||
// Recursively collects the operands of |user_of_image_insts| (and operands
|
||||
// of the operands) whose result types are images/samplers or pointers/array/
|
||||
// struct of them and returns them.
|
||||
std::deque<Instruction*> CollectRequiredImageInsts(
|
||||
Instruction* user_of_image_insts) const;
|
||||
|
||||
// Returns whether result type of |inst| is an image/sampler/pointer of image
|
||||
// or sampler or not.
|
||||
bool HasImageOrImagePtrType(const Instruction* inst) const;
|
||||
|
||||
// Returns whether |type_inst| is an image/sampler or pointer/array/struct of
|
||||
// image or sampler or not.
|
||||
bool IsImageOrImagePtrType(const Instruction* type_inst) const;
|
||||
|
||||
// Returns whether the type with |type_id| is a concrete type or not.
|
||||
bool IsConcreteType(uint32_t type_id) const;
|
||||
|
||||
// Replaces the non-uniform access to a descriptor variable
|
||||
// |access_chain_final_user| with OpSwitch instruction and case blocks. Each
|
||||
// case block will contain a clone of |access_chain| and clones of
|
||||
// |non_uniform_accesses_to_clone| that are recursively used by
|
||||
// |access_chain_final_user|. The clone of |access_chain| (or
|
||||
// OpInBoundsAccessChain) will have a constant index for its first index. The
|
||||
// OpSwitch instruction will have the cases for the variable index of
|
||||
// |access_chain| from 0 to |number_of_elements| - 1.
|
||||
void ReplaceNonUniformAccessWithSwitchCase(
|
||||
Instruction* access_chain_final_user, Instruction* access_chain,
|
||||
uint32_t number_of_elements,
|
||||
const std::deque<Instruction*>& non_uniform_accesses_to_clone) const;
|
||||
|
||||
// Creates and returns a new basic block that contains all instructions of
|
||||
// |block| after |separation_begin_inst|. The new basic block is added to the
|
||||
// function in this method.
|
||||
BasicBlock* SeparateInstructionsIntoNewBlock(
|
||||
BasicBlock* block, Instruction* separation_begin_inst) const;
|
||||
|
||||
// Creates and returns a new block.
|
||||
BasicBlock* CreateNewBlock() const;
|
||||
|
||||
// Returns the first operand id of the OpAccessChain or OpInBoundsAccessChain
|
||||
// instruction |access_chain|.
|
||||
uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain) const;
|
||||
|
||||
// Adds a clone of the OpAccessChain or OpInBoundsAccessChain instruction
|
||||
// |access_chain| to |case_block|. The clone of |access_chain| will use
|
||||
// |const_element_idx| for its first index. |old_ids_to_new_ids| keeps the
|
||||
// mapping from the result id of |access_chain| to the result of its clone.
|
||||
void AddConstElementAccessToCaseBlock(
|
||||
BasicBlock* case_block, Instruction* access_chain,
|
||||
uint32_t const_element_idx,
|
||||
std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
|
||||
|
||||
// Clones all instructions in |insts_to_be_cloned| and put them to |block|.
|
||||
// |old_ids_to_new_ids| keeps the mapping from the result id of each
|
||||
// instruction of |insts_to_be_cloned| to the result of their clones.
|
||||
void CloneInstsToBlock(
|
||||
BasicBlock* block, Instruction* inst_to_skip_cloning,
|
||||
const std::deque<Instruction*>& insts_to_be_cloned,
|
||||
std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
|
||||
|
||||
// Adds OpBranch to |branch_destination| at the end of |parent_block|.
|
||||
void AddBranchToBlock(BasicBlock* parent_block,
|
||||
uint32_t branch_destination) const;
|
||||
|
||||
// Replaces in-operands of all instructions in the basic block |block| using
|
||||
// |old_ids_to_new_ids|. It conducts the replacement only if the in-operand
|
||||
// id is a key of |old_ids_to_new_ids|.
|
||||
void UseNewIdsInBlock(
|
||||
BasicBlock* block,
|
||||
const std::unordered_map<uint32_t, uint32_t>& old_ids_to_new_ids) const;
|
||||
|
||||
// Creates a case block for |element_index| case. It adds clones of
|
||||
// |insts_to_be_cloned| and a clone of |access_chain| with |element_index| as
|
||||
// its first index. The termination instruction of the created case block will
|
||||
// be a branch to |branch_target_id|. Puts old ids to new ids map for the
|
||||
// cloned instructions in |old_ids_to_new_ids|.
|
||||
BasicBlock* CreateCaseBlock(
|
||||
Instruction* access_chain, uint32_t element_index,
|
||||
const std::deque<Instruction*>& insts_to_be_cloned,
|
||||
uint32_t branch_target_id,
|
||||
std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
|
||||
|
||||
// Creates a default block for switch-case statement that has only a single
|
||||
// instruction OpBranch whose target is a basic block with |merge_block_id|.
|
||||
// If |null_const_for_phi_is_needed| is true, gets or creates a default null
|
||||
// constant value for a phi instruction whose operands are |phi_operands| and
|
||||
// puts it in |phi_operands|.
|
||||
BasicBlock* CreateDefaultBlock(bool null_const_for_phi_is_needed,
|
||||
std::vector<uint32_t>* phi_operands,
|
||||
uint32_t merge_block_id) const;
|
||||
|
||||
// Creates and adds an OpSwitch used for the selection of OpAccessChain whose
|
||||
// first Indexes operand is |access_chain_index_var_id|. The OpSwitch will be
|
||||
// added at the end of |parent_block|. It will jump to |default_id| for the
|
||||
// default case and jumps to one of case blocks whoes ids are |case_block_ids|
|
||||
// if |access_chain_index_var_id| matches the case number. |merge_id| is the
|
||||
// merge block id.
|
||||
void AddSwitchForAccessChain(
|
||||
BasicBlock* parent_block, uint32_t access_chain_index_var_id,
|
||||
uint32_t default_id, uint32_t merge_id,
|
||||
const std::vector<uint32_t>& case_block_ids) const;
|
||||
|
||||
// Creates a phi instruction with |phi_operands| as values and
|
||||
// |case_block_ids| and |default_block_id| as incoming blocks. The size of
|
||||
// |phi_operands| must be exactly 1 larger than the size of |case_block_ids|.
|
||||
// The last element of |phi_operands| will be used for |default_block_id|. It
|
||||
// adds the phi instruction to the beginning of |parent_block|.
|
||||
uint32_t CreatePhiInstruction(BasicBlock* parent_block,
|
||||
const std::vector<uint32_t>& phi_operands,
|
||||
const std::vector<uint32_t>& case_block_ids,
|
||||
uint32_t default_block_id) const;
|
||||
|
||||
// Replaces the incoming block operand of OpPhi instructions with
|
||||
// |new_incoming_block_id| if the incoming block operand is
|
||||
// |old_incoming_block_id|.
|
||||
void ReplacePhiIncomingBlock(uint32_t old_incoming_block_id,
|
||||
uint32_t new_incoming_block_id) const;
|
||||
|
||||
// Create an OpConstantNull instruction whose result type id is |type_id|.
|
||||
Instruction* GetConstNull(uint32_t type_id) const;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_
|
||||
@@ -35,6 +35,10 @@ namespace opt {
|
||||
Pass::Status ScalarReplacementPass::Process() {
|
||||
Status status = Status::SuccessWithoutChange;
|
||||
for (auto& f : *get_module()) {
|
||||
if (f.IsDeclaration()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Status functionStatus = ProcessFunction(&f);
|
||||
if (functionStatus == Status::Failure)
|
||||
return functionStatus;
|
||||
|
||||
@@ -85,6 +85,10 @@ std::vector<uint32_t> ParseDefaultValueStr(const char* text,
|
||||
// with 0x1, which represents a 'true'.
|
||||
// If all words in the bit pattern are zero, returns a bit pattern with 0x0,
|
||||
// which represents a 'false'.
|
||||
// For integer and floating point types narrower than 32 bits, the upper bits
|
||||
// in the input bit pattern are ignored. Instead the upper bits are set
|
||||
// according to SPIR-V literal requirements: sign extend a signed integer, and
|
||||
// otherwise set the upper bits to zero.
|
||||
std::vector<uint32_t> ParseDefaultValueBitPattern(
|
||||
const std::vector<uint32_t>& input_bit_pattern,
|
||||
const analysis::Type* type) {
|
||||
@@ -98,12 +102,33 @@ std::vector<uint32_t> ParseDefaultValueBitPattern(
|
||||
}
|
||||
return result;
|
||||
} else if (const auto* IT = type->AsInteger()) {
|
||||
if (IT->width() == input_bit_pattern.size() * sizeof(uint32_t) * 8) {
|
||||
return std::vector<uint32_t>(input_bit_pattern);
|
||||
const auto width = IT->width();
|
||||
assert(width > 0);
|
||||
const auto adjusted_width = std::max(32u, width);
|
||||
if (adjusted_width == input_bit_pattern.size() * sizeof(uint32_t) * 8) {
|
||||
result = std::vector<uint32_t>(input_bit_pattern);
|
||||
if (width < 32) {
|
||||
const uint32_t high_active_bit = (1u << width) >> 1;
|
||||
if (IT->IsSigned() && (high_active_bit & result[0])) {
|
||||
// Sign extend. This overwrites the sign bit again, but that's ok.
|
||||
result[0] = result[0] | ~(high_active_bit - 1);
|
||||
} else {
|
||||
// Upper bits must be zero.
|
||||
result[0] = result[0] & ((1u << width) - 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} else if (const auto* FT = type->AsFloat()) {
|
||||
if (FT->width() == input_bit_pattern.size() * sizeof(uint32_t) * 8) {
|
||||
return std::vector<uint32_t>(input_bit_pattern);
|
||||
const auto width = FT->width();
|
||||
const auto adjusted_width = std::max(32u, width);
|
||||
if (adjusted_width == input_bit_pattern.size() * sizeof(uint32_t) * 8) {
|
||||
result = std::vector<uint32_t>(input_bit_pattern);
|
||||
if (width < 32) {
|
||||
// Upper bits must be zero.
|
||||
result[0] = result[0] & ((1u << width) - 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
result.clear();
|
||||
|
||||
@@ -45,6 +45,10 @@ void SimplificationPass::AddNewOperands(
|
||||
}
|
||||
|
||||
bool SimplificationPass::SimplifyFunction(Function* function) {
|
||||
if (function->IsDeclaration()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool modified = false;
|
||||
// Phase 1: Traverse all instructions in dominance order.
|
||||
// The second phase will only be on the instructions whose inputs have changed
|
||||
|
||||
@@ -753,6 +753,9 @@ Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) {
|
||||
Pass::Status SSARewritePass::Process() {
|
||||
Status status = Status::SuccessWithoutChange;
|
||||
for (auto& fn : *get_module()) {
|
||||
if (fn.IsDeclaration()) {
|
||||
continue;
|
||||
}
|
||||
status =
|
||||
CombineStatus(status, SSARewriter(this).RewriteFunctionIntoSSA(&fn));
|
||||
// Kill DebugDeclares for target variables.
|
||||
|
||||
1
3rdparty/spirv-tools/source/spirv_constant.h
vendored
1
3rdparty/spirv-tools/source/spirv_constant.h
vendored
@@ -84,6 +84,7 @@ typedef enum spv_generator_t {
|
||||
SPV_GENERATOR_KHRONOS_LLVM_TRANSLATOR = 6,
|
||||
SPV_GENERATOR_KHRONOS_ASSEMBLER = 7,
|
||||
SPV_GENERATOR_KHRONOS_GLSLANG = 8,
|
||||
SPV_GENERATOR_KHRONOS_LINKER = 17,
|
||||
SPV_GENERATOR_NUM_ENTRIES,
|
||||
SPV_FORCE_16_BIT_ENUM(spv_generator_t)
|
||||
} spv_generator_t;
|
||||
|
||||
63
3rdparty/spirv-tools/source/spirv_target_env.cpp
vendored
63
3rdparty/spirv-tools/source/spirv_target_env.cpp
vendored
@@ -62,7 +62,7 @@ const char* spvTargetEnvDescription(spv_target_env env) {
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
return "SPIR-V 1.3 (under Vulkan 1.1 semantics)";
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
assert(false);
|
||||
assert(false && "Deprecated target environment value.");
|
||||
break;
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
return "SPIR-V 1.4";
|
||||
@@ -72,6 +72,9 @@ const char* spvTargetEnvDescription(spv_target_env env) {
|
||||
return "SPIR-V 1.5";
|
||||
case SPV_ENV_VULKAN_1_2:
|
||||
return "SPIR-V 1.5 (under Vulkan 1.2 semantics)";
|
||||
case SPV_ENV_MAX:
|
||||
assert(false && "Invalid target environment value.");
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -102,7 +105,7 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) {
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
return SPV_SPIRV_VERSION_WORD(1, 3);
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
assert(false);
|
||||
assert(false && "Deprecated target environment value.");
|
||||
break;
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
@@ -110,6 +113,9 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) {
|
||||
case SPV_ENV_UNIVERSAL_1_5:
|
||||
case SPV_ENV_VULKAN_1_2:
|
||||
return SPV_SPIRV_VERSION_WORD(1, 5);
|
||||
case SPV_ENV_MAX:
|
||||
assert(false && "Invalid target environment value.");
|
||||
break;
|
||||
}
|
||||
return SPV_SPIRV_VERSION_WORD(0, 0);
|
||||
}
|
||||
@@ -212,7 +218,10 @@ bool spvIsVulkanEnv(spv_target_env env) {
|
||||
case SPV_ENV_VULKAN_1_2:
|
||||
return true;
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
assert(false);
|
||||
assert(false && "Deprecated target environment value.");
|
||||
break;
|
||||
case SPV_ENV_MAX:
|
||||
assert(false && "Invalid target environment value.");
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
@@ -246,7 +255,10 @@ bool spvIsOpenCLEnv(spv_target_env env) {
|
||||
case SPV_ENV_OPENCL_2_2:
|
||||
return true;
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
assert(false);
|
||||
assert(false && "Deprecated target environment value.");
|
||||
break;
|
||||
case SPV_ENV_MAX:
|
||||
assert(false && "Invalid target environment value.");
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
@@ -280,7 +292,43 @@ bool spvIsOpenGLEnv(spv_target_env env) {
|
||||
case SPV_ENV_OPENGL_4_5:
|
||||
return true;
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
assert(false);
|
||||
assert(false && "Deprecated target environment value.");
|
||||
break;
|
||||
case SPV_ENV_MAX:
|
||||
assert(false && "Invalid target environment value.");
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool spvIsValidEnv(spv_target_env env) {
|
||||
switch (env) {
|
||||
case SPV_ENV_UNIVERSAL_1_0:
|
||||
case SPV_ENV_VULKAN_1_0:
|
||||
case SPV_ENV_UNIVERSAL_1_1:
|
||||
case SPV_ENV_UNIVERSAL_1_2:
|
||||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_OPENCL_1_2:
|
||||
case SPV_ENV_OPENCL_EMBEDDED_1_2:
|
||||
case SPV_ENV_OPENCL_2_0:
|
||||
case SPV_ENV_OPENCL_EMBEDDED_2_0:
|
||||
case SPV_ENV_OPENCL_EMBEDDED_2_1:
|
||||
case SPV_ENV_OPENCL_EMBEDDED_2_2:
|
||||
case SPV_ENV_OPENCL_2_1:
|
||||
case SPV_ENV_OPENCL_2_2:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
case SPV_ENV_UNIVERSAL_1_5:
|
||||
case SPV_ENV_VULKAN_1_2:
|
||||
case SPV_ENV_OPENGL_4_0:
|
||||
case SPV_ENV_OPENGL_4_1:
|
||||
case SPV_ENV_OPENGL_4_2:
|
||||
case SPV_ENV_OPENGL_4_3:
|
||||
case SPV_ENV_OPENGL_4_5:
|
||||
return true;
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
case SPV_ENV_MAX:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
@@ -320,7 +368,10 @@ std::string spvLogStringForEnv(spv_target_env env) {
|
||||
return "Universal";
|
||||
}
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
assert(false);
|
||||
assert(false && "Deprecated target environment value.");
|
||||
break;
|
||||
case SPV_ENV_MAX:
|
||||
assert(false && "Invalid target environment value.");
|
||||
break;
|
||||
}
|
||||
return "Unknown";
|
||||
|
||||
@@ -28,6 +28,9 @@ bool spvIsOpenCLEnv(spv_target_env env);
|
||||
// Returns true if |env| is an OPENGL environment, false otherwise.
|
||||
bool spvIsOpenGLEnv(spv_target_env env);
|
||||
|
||||
// Returns true if |env| is an implemented/valid environment, false otherwise.
|
||||
bool spvIsValidEnv(spv_target_env env);
|
||||
|
||||
// Returns the version number for the given SPIR-V target environment.
|
||||
uint32_t spvVersionForTargetEnv(spv_target_env env);
|
||||
|
||||
|
||||
@@ -659,9 +659,9 @@ spv_result_t ValidateStructuredSelections(
|
||||
// Mark the upcoming blocks as seen now, but only error out if this block
|
||||
// was missing a merge instruction and both labels hadn't been seen
|
||||
// previously.
|
||||
const bool both_unseen =
|
||||
seen.insert(true_label).second && seen.insert(false_label).second;
|
||||
if (!merge && both_unseen) {
|
||||
const bool true_label_unseen = seen.insert(true_label).second;
|
||||
const bool false_label_unseen = seen.insert(false_label).second;
|
||||
if (!merge && true_label_unseen && false_label_unseen) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, terminator)
|
||||
<< "Selection must be structured";
|
||||
}
|
||||
|
||||
@@ -140,6 +140,8 @@ bool isMissingOffsetInStruct(uint32_t struct_id, ValidationState_t& vstate) {
|
||||
for (auto& decoration : vstate.id_decorations(struct_id)) {
|
||||
if (SpvDecorationOffset == decoration.dec_type() &&
|
||||
Decoration::kInvalidMember != decoration.struct_member_index()) {
|
||||
// Offset 0xffffffff is not valid so ignore it for simplicity's sake.
|
||||
if (decoration.params()[0] == 0xffffffff) return true;
|
||||
hasOffset[decoration.struct_member_index()] = true;
|
||||
}
|
||||
}
|
||||
@@ -995,7 +997,9 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
|
||||
|
||||
const bool phys_storage_buffer =
|
||||
storageClass == SpvStorageClassPhysicalStorageBufferEXT;
|
||||
const bool workgroup = storageClass == SpvStorageClassWorkgroup;
|
||||
const bool workgroup =
|
||||
storageClass == SpvStorageClassWorkgroup &&
|
||||
vstate.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayoutKHR);
|
||||
if (uniform || push_constant || storage_buffer || phys_storage_buffer ||
|
||||
workgroup) {
|
||||
const auto ptrInst = vstate.FindDef(words[1]);
|
||||
|
||||
@@ -66,6 +66,11 @@ bool CheckAllImageOperandsHandled() {
|
||||
case SpvImageOperandsVolatileTexelKHRMask:
|
||||
case SpvImageOperandsSignExtendMask:
|
||||
case SpvImageOperandsZeroExtendMask:
|
||||
// TODO(jaebaek): Move this line properly after handling image offsets
|
||||
// operand. This line temporarily fixes CI failure that
|
||||
// blocks other PRs.
|
||||
// https://github.com/KhronosGroup/SPIRV-Tools/issues/4565
|
||||
case SpvImageOperandsOffsetsMask:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -281,13 +286,14 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
|
||||
// the module to be invalid.
|
||||
if (mask == 0) return SPV_SUCCESS;
|
||||
|
||||
if (spvtools::utils::CountSetBits(
|
||||
mask & (SpvImageOperandsOffsetMask | SpvImageOperandsConstOffsetMask |
|
||||
SpvImageOperandsConstOffsetsMask)) > 1) {
|
||||
if (spvtools::utils::CountSetBits(mask & (SpvImageOperandsOffsetMask |
|
||||
SpvImageOperandsConstOffsetMask |
|
||||
SpvImageOperandsConstOffsetsMask |
|
||||
SpvImageOperandsOffsetsMask)) > 1) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(4662)
|
||||
<< "Image Operands Offset, ConstOffset, ConstOffsets cannot be used "
|
||||
<< "together";
|
||||
<< "Image Operands Offset, ConstOffset, ConstOffsets, Offsets "
|
||||
"cannot be used together";
|
||||
}
|
||||
|
||||
const bool is_implicit_lod = IsImplicitLod(opcode);
|
||||
@@ -620,6 +626,10 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
|
||||
// setup.
|
||||
}
|
||||
|
||||
if (mask & SpvImageOperandsOffsetsMask) {
|
||||
// TODO: add validation
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -2058,11 +2068,13 @@ spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst) {
|
||||
std::string* message) {
|
||||
const auto* models = state.GetExecutionModels(entry_point->id());
|
||||
const auto* modes = state.GetExecutionModes(entry_point->id());
|
||||
if (models->find(SpvExecutionModelGLCompute) != models->end() &&
|
||||
modes->find(SpvExecutionModeDerivativeGroupLinearNV) ==
|
||||
modes->end() &&
|
||||
modes->find(SpvExecutionModeDerivativeGroupQuadsNV) ==
|
||||
modes->end()) {
|
||||
if (models &&
|
||||
models->find(SpvExecutionModelGLCompute) != models->end() &&
|
||||
(!modes ||
|
||||
(modes->find(SpvExecutionModeDerivativeGroupLinearNV) ==
|
||||
modes->end() &&
|
||||
modes->find(SpvExecutionModeDerivativeGroupQuadsNV) ==
|
||||
modes->end()))) {
|
||||
if (message) {
|
||||
*message =
|
||||
std::string(
|
||||
|
||||
@@ -318,10 +318,9 @@ spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) {
|
||||
|
||||
if (module_version < min_version) {
|
||||
return _.diag(SPV_ERROR_WRONG_VERSION, inst)
|
||||
<< spvOpcodeString(opcode) << " requires "
|
||||
<< spvTargetEnvDescription(
|
||||
static_cast<spv_target_env>(min_version))
|
||||
<< " at minimum.";
|
||||
<< spvOpcodeString(opcode) << " requires SPIR-V version "
|
||||
<< SPV_SPIRV_VERSION_MAJOR_PART(min_version) << "."
|
||||
<< SPV_SPIRV_VERSION_MINOR_PART(min_version) << " at minimum.";
|
||||
}
|
||||
} else if (!_.HasAnyOfExtensions(exts)) {
|
||||
// Otherwise, we only error out when no enabling extensions are
|
||||
|
||||
@@ -199,6 +199,10 @@ uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) {
|
||||
NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1)));
|
||||
num_components *= type->GetOperandAs<uint32_t>(2);
|
||||
break;
|
||||
case SpvOpTypeArray:
|
||||
// Skip the array.
|
||||
return NumConsumedComponents(_,
|
||||
_.FindDef(type->GetOperandAs<uint32_t>(1)));
|
||||
default:
|
||||
// This is an error that is validated elsewhere.
|
||||
break;
|
||||
@@ -430,17 +434,36 @@ spv_result_t GetLocationsForVariable(
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t end = (location + num_locations) * 4;
|
||||
if (num_components != 0) {
|
||||
start += component;
|
||||
end = location * 4 + component + num_components;
|
||||
}
|
||||
for (uint32_t l = start; l < end; ++l) {
|
||||
if (!locations->insert(l).second) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
|
||||
<< "Entry-point has conflicting " << storage_class
|
||||
<< " location assignment at location " << l / 4
|
||||
<< ", component " << l % 4;
|
||||
if (member->opcode() == SpvOpTypeArray && num_components >= 1 &&
|
||||
num_components < 4) {
|
||||
// When an array has an element that takes less than a location in
|
||||
// size, calculate the used locations in a strided manner.
|
||||
for (uint32_t l = location; l < num_locations + location; ++l) {
|
||||
for (uint32_t c = component; c < component + num_components; ++c) {
|
||||
uint32_t check = 4 * l + c;
|
||||
if (!locations->insert(check).second) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
|
||||
<< "Entry-point has conflicting " << storage_class
|
||||
<< " location assignment at location " << l
|
||||
<< ", component " << c;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: There is a hole here is the member is an array of 3- or
|
||||
// 4-element vectors of 64-bit types.
|
||||
uint32_t end = (location + num_locations) * 4;
|
||||
if (num_components != 0) {
|
||||
start += component;
|
||||
end = location * 4 + component + num_components;
|
||||
}
|
||||
for (uint32_t l = start; l < end; ++l) {
|
||||
if (!locations->insert(l).second) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
|
||||
<< "Entry-point has conflicting " << storage_class
|
||||
<< " location assignment at location " << l / 4
|
||||
<< ", component " << l % 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,11 +235,11 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
|
||||
}
|
||||
if (!ok) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(4683)
|
||||
<< _.VkErrorID(6426)
|
||||
<< "In the Vulkan environment, GLCompute execution model "
|
||||
"entry points require either the LocalSize execution "
|
||||
"mode or an object decorated with WorkgroupSize must be "
|
||||
"specified.";
|
||||
"entry points require either the LocalSize or "
|
||||
"LocalSizeId execution mode or an object decorated with "
|
||||
"WorkgroupSize must be specified.";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1845,8 +1845,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677);
|
||||
case 4682:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682);
|
||||
case 4683:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-04683);
|
||||
case 6426:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-06426); // formally 04683
|
||||
case 4685:
|
||||
return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685);
|
||||
case 4686:
|
||||
|
||||
Reference in New Issue
Block a user