aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2023-08-14 15:37:18 -0700
committerXin Li <delphij@google.com>2023-08-14 15:37:18 -0700
commitfac5ddce1de06e255218e92087f4b22d3877ce3d (patch)
tree2d7dd0b86c11918ce2100af431094a7a82a0bd06
parentbd7c191baf030e9e6a4ffcf0de8c3b634ef736b3 (diff)
parent9a1f3fd6689f35e0e10fef7761eb20b46213bda1 (diff)
downloadSPIRV-Tools-tmp_amf_298295554.tar.gz
Merge Android U (ab/10368041)tmp_amf_298295554
Bug: 291102124 Merged-In: I2cc412177a3f9cf4683531576786d46bcbb54430 Change-Id: I22c5032044980dd467ffca53d0427374cd8923d3
-rw-r--r--.gitignore3
-rw-r--r--Android.bp3
-rw-r--r--BUILD.bazel8
-rw-r--r--BUILD.gn184
-rw-r--r--CHANGES69
-rw-r--r--CMakeLists.txt4
-rw-r--r--DEPS6
-rw-r--r--README.md29
-rw-r--r--build_defs.bzl49
-rw-r--r--include/spirv-tools/optimizer.hpp21
-rw-r--r--kokoro/macos-clang-release-bazel/build.sh10
-rwxr-xr-xkokoro/scripts/linux/build-docker.sh2
-rw-r--r--kokoro/scripts/windows/build.bat2
-rw-r--r--kokoro/windows-msvc-2015-release-bazel/build.bat5
-rw-r--r--source/CMakeLists.txt8
-rw-r--r--source/assembly_grammar.cpp6
-rw-r--r--source/cfa.h65
-rw-r--r--source/diff/CMakeLists.txt54
-rw-r--r--source/diff/diff.cpp2869
-rw-r--r--source/diff/diff.h48
-rw-r--r--source/diff/lcs.h224
-rw-r--r--source/disassemble.h7
-rw-r--r--source/libspirv.cpp4
-rw-r--r--source/link/linker.cpp10
-rw-r--r--source/opcode.cpp10
-rw-r--r--source/opcode.h10
-rw-r--r--source/operand.cpp14
-rw-r--r--source/opt/CMakeLists.txt8
-rw-r--r--source/opt/aggressive_dead_code_elim_pass.cpp50
-rw-r--r--source/opt/aggressive_dead_code_elim_pass.h3
-rw-r--r--source/opt/ccp_pass.cpp3
-rw-r--r--source/opt/cfg.cpp22
-rw-r--r--source/opt/cfg.h8
-rw-r--r--source/opt/compact_ids_pass.cpp8
-rw-r--r--source/opt/const_folding_rules.cpp189
-rw-r--r--source/opt/constants.cpp1
-rw-r--r--source/opt/copy_prop_arrays.cpp170
-rw-r--r--source/opt/copy_prop_arrays.h38
-rw-r--r--source/opt/dead_branch_elim_pass.cpp15
-rw-r--r--source/opt/debug_info_manager.cpp35
-rw-r--r--source/opt/debug_info_manager.h22
-rw-r--r--source/opt/dominator_tree.cpp30
-rw-r--r--source/opt/eliminate_dead_input_components_pass.cpp189
-rw-r--r--source/opt/eliminate_dead_input_components_pass.h65
-rw-r--r--source/opt/fix_func_call_arguments.cpp90
-rw-r--r--source/opt/fix_func_call_arguments.h47
-rw-r--r--source/opt/fold_spec_constant_op_and_composite_pass.cpp89
-rw-r--r--source/opt/fold_spec_constant_op_and_composite_pass.h11
-rw-r--r--source/opt/folding_rules.cpp361
-rw-r--r--source/opt/function.cpp8
-rw-r--r--source/opt/function.h41
-rw-r--r--source/opt/if_conversion.cpp7
-rw-r--r--source/opt/inline_pass.cpp58
-rw-r--r--source/opt/inline_pass.h12
-rw-r--r--source/opt/inst_bindless_check_pass.cpp7
-rw-r--r--source/opt/inst_buff_addr_check_pass.cpp2
-rw-r--r--source/opt/inst_buff_addr_check_pass.h2
-rw-r--r--source/opt/instruction.cpp38
-rw-r--r--source/opt/instruction.h21
-rw-r--r--source/opt/instrument_pass.cpp70
-rw-r--r--source/opt/instrument_pass.h13
-rw-r--r--source/opt/interface_var_sroa.cpp968
-rw-r--r--source/opt/interface_var_sroa.h401
-rw-r--r--source/opt/interp_fixup_pass.cpp7
-rw-r--r--source/opt/ir_builder.h9
-rw-r--r--source/opt/ir_context.cpp15
-rw-r--r--source/opt/ir_context.h10
-rw-r--r--source/opt/ir_loader.cpp5
-rw-r--r--source/opt/local_access_chain_convert_pass.cpp79
-rw-r--r--source/opt/local_access_chain_convert_pass.h16
-rw-r--r--source/opt/local_single_block_elim_pass.cpp2
-rw-r--r--source/opt/local_single_store_elim_pass.cpp26
-rw-r--r--source/opt/loop_descriptor.cpp7
-rw-r--r--source/opt/loop_descriptor.h3
-rw-r--r--source/opt/loop_unroller.cpp15
-rw-r--r--source/opt/merge_return_pass.cpp1
-rw-r--r--source/opt/merge_return_pass.h2
-rw-r--r--source/opt/module.cpp5
-rw-r--r--source/opt/module.h19
-rw-r--r--source/opt/optimizer.cpp39
-rw-r--r--source/opt/pass.h7
-rw-r--r--source/opt/pass_manager.cpp2
-rw-r--r--source/opt/passes.h4
-rw-r--r--source/opt/private_to_local_pass.cpp2
-rw-r--r--source/opt/reduce_load_size.cpp11
-rw-r--r--source/opt/remove_dontinline_pass.cpp49
-rw-r--r--source/opt/remove_dontinline_pass.h42
-rw-r--r--source/opt/replace_desc_array_access_using_var_index.cpp26
-rw-r--r--source/opt/replace_desc_array_access_using_var_index.h11
-rw-r--r--source/opt/scalar_replacement_pass.cpp25
-rw-r--r--source/opt/scalar_replacement_pass.h33
-rw-r--r--source/opt/spread_volatile_semantics.cpp70
-rw-r--r--source/opt/spread_volatile_semantics.h20
-rw-r--r--source/opt/ssa_rewrite_pass.cpp71
-rw-r--r--source/opt/ssa_rewrite_pass.h9
-rw-r--r--source/opt/type_manager.cpp2
-rw-r--r--source/opt/types.cpp192
-rw-r--r--source/opt/types.h111
-rw-r--r--source/parsed_operand.cpp4
-rw-r--r--source/print.cpp3
-rw-r--r--source/text.cpp19
-rw-r--r--source/text_handler.cpp43
-rw-r--r--source/util/hash_combine.h53
-rw-r--r--source/util/hex_float.h90
-rw-r--r--source/util/ilist.h1
-rw-r--r--source/util/small_vector.h14
-rw-r--r--source/val/basic_block.cpp83
-rw-r--r--source/val/basic_block.h97
-rw-r--r--source/val/construct.cpp86
-rw-r--r--source/val/decoration.h9
-rw-r--r--source/val/function.cpp37
-rw-r--r--source/val/function.h8
-rw-r--r--source/val/validate.cpp7
-rw-r--r--source/val/validate.h6
-rw-r--r--source/val/validate_annotation.cpp170
-rw-r--r--source/val/validate_atomics.cpp10
-rw-r--r--source/val/validate_barriers.cpp3
-rw-r--r--source/val/validate_bitwise.cpp83
-rw-r--r--source/val/validate_builtins.cpp203
-rw-r--r--source/val/validate_cfg.cpp223
-rw-r--r--source/val/validate_conversion.cpp18
-rw-r--r--source/val/validate_decorations.cpp305
-rw-r--r--source/val/validate_extensions.cpp121
-rw-r--r--source/val/validate_function.cpp37
-rw-r--r--source/val/validate_image.cpp263
-rw-r--r--source/val/validate_instruction.cpp16
-rw-r--r--source/val/validate_interfaces.cpp20
-rw-r--r--source/val/validate_layout.cpp1
-rw-r--r--source/val/validate_logicals.cpp13
-rw-r--r--source/val/validate_memory.cpp223
-rw-r--r--source/val/validate_misc.cpp5
-rw-r--r--source/val/validate_mode_setting.cpp75
-rw-r--r--source/val/validate_non_uniform.cpp55
-rw-r--r--source/val/validate_ray_query.cpp273
-rw-r--r--source/val/validate_ray_tracing.cpp206
-rw-r--r--source/val/validate_scopes.cpp69
-rw-r--r--source/val/validate_type.cpp53
-rw-r--r--source/val/validation_state.cpp242
-rw-r--r--source/val/validation_state.h112
-rw-r--r--test/CMakeLists.txt1
-rw-r--r--test/binary_to_text_test.cpp11
-rw-r--r--test/c_interface_test.cpp10
-rw-r--r--test/diff/CMakeLists.txt26
-rw-r--r--test/diff/diff_files/.gitignore3
-rw-r--r--test/diff/diff_files/OpExtInst_in_dst_only_autogen.cpp242
-rw-r--r--test/diff/diff_files/OpExtInst_in_dst_only_dst.spvasm33
-rw-r--r--test/diff/diff_files/OpExtInst_in_dst_only_src.spvasm33
-rw-r--r--test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp242
-rw-r--r--test/diff/diff_files/OpExtInst_in_src_only_dst.spvasm30
-rw-r--r--test/diff/diff_files/OpExtInst_in_src_only_src.spvasm36
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_basic_autogen.cpp136
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_basic_dst.spvasm15
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_basic_src.spvasm13
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_intertwined_autogen.cpp138
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_intertwined_dst.spvasm13
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_intertwined_src.spvasm15
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp116
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_mismatching_class_dst.spvasm9
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_mismatching_class_src.spvasm11
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp111
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_mismatching_type_dst.spvasm8
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_mismatching_type_src.spvasm10
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_nested_autogen.cpp127
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_nested_dst.spvasm11
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_nested_src.spvasm13
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp124
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_onesided_debug_dst.spvasm11
-rw-r--r--test/diff/diff_files/OpTypeForwardPointer_onesided_debug_src.spvasm14
-rw-r--r--test/diff/diff_files/README.md17
-rw-r--r--test/diff/diff_files/basic_autogen.cpp407
-rw-r--r--test/diff/diff_files/basic_dst.spvasm49
-rw-r--r--test/diff/diff_files/basic_src.spvasm50
-rw-r--r--test/diff/diff_files/constant_array_size_autogen.cpp306
-rw-r--r--test/diff/diff_files/constant_array_size_dst.spvasm47
-rw-r--r--test/diff/diff_files/constant_array_size_src.spvasm48
-rw-r--r--test/diff/diff_files/diff_test_files_autogen.cmake47
-rw-r--r--test/diff/diff_files/different_decorations_fragment_autogen.cpp1626
-rw-r--r--test/diff/diff_files/different_decorations_fragment_dst.spvasm199
-rw-r--r--test/diff/diff_files/different_decorations_fragment_src.spvasm198
-rw-r--r--test/diff/diff_files/different_decorations_vertex_autogen.cpp1322
-rw-r--r--test/diff/diff_files/different_decorations_vertex_dst.spvasm159
-rw-r--r--test/diff/diff_files/different_decorations_vertex_src.spvasm155
-rw-r--r--test/diff/diff_files/different_function_parameter_count_autogen.cpp339
-rw-r--r--test/diff/diff_files/different_function_parameter_count_dst.spvasm52
-rw-r--r--test/diff/diff_files/different_function_parameter_count_src.spvasm46
-rw-r--r--test/diff/diff_files/extra_if_block_autogen.cpp867
-rw-r--r--test/diff/diff_files/extra_if_block_dst.spvasm136
-rw-r--r--test/diff/diff_files/extra_if_block_src.spvasm137
-rwxr-xr-xtest/diff/diff_files/generate_tests.py304
-rw-r--r--test/diff/diff_files/index_signedness_autogen.cpp733
-rw-r--r--test/diff/diff_files/index_signedness_dst.spvasm110
-rw-r--r--test/diff/diff_files/index_signedness_src.spvasm111
-rw-r--r--test/diff/diff_files/int_vs_uint_constants_autogen.cpp396
-rw-r--r--test/diff/diff_files/int_vs_uint_constants_dst.spvasm46
-rw-r--r--test/diff/diff_files/int_vs_uint_constants_src.spvasm49
-rw-r--r--test/diff/diff_files/large_functions_large_diffs_autogen.cpp1534
-rw-r--r--test/diff/diff_files/large_functions_large_diffs_dst.spvasm213
-rw-r--r--test/diff/diff_files/large_functions_large_diffs_src.spvasm230
-rw-r--r--test/diff/diff_files/large_functions_small_diffs_autogen.cpp1364
-rw-r--r--test/diff/diff_files/large_functions_small_diffs_dst.spvasm229
-rw-r--r--test/diff/diff_files/large_functions_small_diffs_src.spvasm226
-rw-r--r--test/diff/diff_files/multiple_different_entry_points_autogen.cpp330
-rw-r--r--test/diff/diff_files/multiple_different_entry_points_dst.spvasm49
-rw-r--r--test/diff/diff_files/multiple_different_entry_points_src.spvasm51
-rw-r--r--test/diff/diff_files/multiple_same_entry_points_autogen.cpp375
-rw-r--r--test/diff/diff_files/multiple_same_entry_points_dst.spvasm45
-rw-r--r--test/diff/diff_files/multiple_same_entry_points_src.spvasm46
-rw-r--r--test/diff/diff_files/reordered_if_blocks_autogen.cpp568
-rw-r--r--test/diff/diff_files/reordered_if_blocks_dst.spvasm87
-rw-r--r--test/diff/diff_files/reordered_if_blocks_src.spvasm87
-rw-r--r--test/diff/diff_files/reordered_switch_blocks_autogen.cpp582
-rw-r--r--test/diff/diff_files/reordered_switch_blocks_dst.spvasm91
-rw-r--r--test/diff/diff_files/reordered_switch_blocks_src.spvasm92
-rw-r--r--test/diff/diff_files/small_functions_small_diffs_autogen.cpp747
-rw-r--r--test/diff/diff_files/small_functions_small_diffs_dst.spvasm92
-rw-r--r--test/diff/diff_files/small_functions_small_diffs_src.spvasm93
-rw-r--r--test/diff/diff_files/spec_constant_array_size_autogen.cpp310
-rw-r--r--test/diff/diff_files/spec_constant_array_size_dst.spvasm47
-rw-r--r--test/diff/diff_files/spec_constant_array_size_src.spvasm48
-rw-r--r--test/diff/diff_files/spec_constant_composite_autogen.cpp186
-rw-r--r--test/diff/diff_files/spec_constant_composite_dst.spvasm22
-rw-r--r--test/diff/diff_files/spec_constant_composite_src.spvasm23
-rw-r--r--test/diff/diff_files/spec_constant_op_autogen.cpp162
-rw-r--r--test/diff/diff_files/spec_constant_op_dst.spvasm17
-rw-r--r--test/diff/diff_files/spec_constant_op_src.spvasm18
-rw-r--r--test/diff/diff_files/spec_constant_specid_autogen.cpp141
-rw-r--r--test/diff/diff_files/spec_constant_specid_dst.spvasm13
-rw-r--r--test/diff/diff_files/spec_constant_specid_src.spvasm15
-rw-r--r--test/diff/diff_files/unrelated_shaders_autogen.cpp230
-rw-r--r--test/diff/diff_files/unrelated_shaders_dst.spvasm31
-rw-r--r--test/diff/diff_files/unrelated_shaders_src.spvasm25
-rw-r--r--test/diff/diff_test.cpp228
-rw-r--r--test/diff/diff_test_utils.cpp58
-rw-r--r--test/diff/diff_test_utils.h30
-rw-r--r--test/diff/lcs_test.cpp329
-rw-r--r--test/fuzz/fuzz_test_util.cpp2
-rw-r--r--test/hex_float_test.cpp41
-rw-r--r--test/link/entry_points_test.cpp43
-rw-r--r--test/link/linker_fixture.h4
-rw-r--r--test/operand_capabilities_test.cpp32
-rw-r--r--test/operand_pattern_test.cpp10
-rw-r--r--test/opt/CMakeLists.txt8
-rw-r--r--test/opt/aggressive_dead_code_elim_test.cpp153
-rw-r--r--test/opt/ccp_test.cpp29
-rw-r--r--test/opt/cfg_test.cpp119
-rw-r--r--test/opt/compact_ids_test.cpp35
-rw-r--r--test/opt/copy_prop_array_test.cpp104
-rw-r--r--test/opt/dead_branch_elim_test.cpp155
-rw-r--r--test/opt/eliminate_dead_input_components_test.cpp468
-rw-r--r--test/opt/fix_func_call_arguments_test.cpp152
-rw-r--r--test/opt/fold_test.cpp572
-rw-r--r--test/opt/freeze_spec_const_test.cpp45
-rw-r--r--test/opt/function_test.cpp59
-rw-r--r--test/opt/if_conversion_test.cpp61
-rw-r--r--test/opt/inline_test.cpp97
-rw-r--r--test/opt/inst_bindless_check_test.cpp8139
-rw-r--r--test/opt/inst_buff_addr_check_test.cpp816
-rw-r--r--test/opt/inst_debug_printf_test.cpp62
-rw-r--r--test/opt/instruction_test.cpp39
-rw-r--r--test/opt/interface_var_sroa_test.cpp410
-rw-r--r--test/opt/ir_context_test.cpp49
-rw-r--r--test/opt/local_access_chain_convert_test.cpp191
-rw-r--r--test/opt/local_single_store_elim_test.cpp231
-rw-r--r--test/opt/local_ssa_elim_test.cpp1205
-rw-r--r--test/opt/loop_optimizations/unroll_simple.cpp34
-rw-r--r--test/opt/pass_merge_return_test.cpp4
-rw-r--r--test/opt/reduce_load_size_test.cpp39
-rw-r--r--test/opt/remove_dontinline_test.cpp127
-rw-r--r--test/opt/replace_desc_array_access_using_var_index_test.cpp165
-rw-r--r--test/opt/scalar_analysis.cpp1
-rw-r--r--test/opt/scalar_replacement_test.cpp77
-rw-r--r--test/opt/spread_volatile_semantics_test.cpp161
-rw-r--r--test/opt/type_manager_test.cpp16
-rw-r--r--test/opt/types_test.cpp61
-rw-r--r--test/reduce/structured_loop_to_selection_test.cpp18
-rw-r--r--test/text_advance_test.cpp9
-rw-r--r--test/text_to_binary.annotation_test.cpp9
-rw-r--r--test/text_to_binary.barrier_test.cpp21
-rw-r--r--test/text_to_binary.control_flow_test.cpp9
-rw-r--r--test/text_to_binary.device_side_enqueue_test.cpp3
-rw-r--r--test/text_to_binary.extension_test.cpp63
-rw-r--r--test/text_to_binary.image_test.cpp9
-rw-r--r--test/text_to_binary.memory_test.cpp6
-rw-r--r--test/text_to_binary.mode_setting_test.cpp3
-rw-r--r--test/text_to_binary.pipe_storage_test.cpp12
-rw-r--r--test/text_to_binary.subgroup_dispatch_test.cpp12
-rw-r--r--test/text_to_binary.type_declaration_test.cpp9
-rw-r--r--test/util/CMakeLists.txt1
-rw-r--r--test/util/hash_combine_test.cpp43
-rw-r--r--test/util/small_vector_test.cpp74
-rw-r--r--test/val/CMakeLists.txt20
-rw-r--r--test/val/val_annotation_test.cpp4
-rw-r--r--test/val/val_atomics_test.cpp4
-rw-r--r--test/val/val_barriers_test.cpp84
-rw-r--r--test/val/val_bitwise_test.cpp117
-rw-r--r--test/val/val_builtins_test.cpp251
-rw-r--r--test/val/val_capability_test.cpp4
-rw-r--r--test/val/val_cfg_test.cpp191
-rw-r--r--test/val/val_conversion_test.cpp129
-rw-r--r--test/val/val_data_test.cpp2
-rw-r--r--test/val/val_decoration_test.cpp960
-rw-r--r--test/val/val_entry_point_test.cpp (renamed from test/val/val_entry_point.cpp)0
-rw-r--r--test/val/val_ext_inst_debug_test.cpp181
-rw-r--r--test/val/val_extension_spv_khr_bit_instructions_test.cpp (renamed from test/val/val_extension_spv_khr_bit_instructions.cpp)0
-rw-r--r--test/val/val_extension_spv_khr_expect_assume_test.cpp (renamed from test/val/val_extension_spv_khr_expect_assume.cpp)0
-rw-r--r--test/val/val_extension_spv_khr_integer_dot_product_test.cpp (renamed from test/val/val_extension_spv_khr_integer_dot_product.cpp)0
-rw-r--r--test/val/val_extension_spv_khr_linkonce_odr_test.cpp (renamed from test/val/val_extension_spv_khr_linkonce_odr.cpp)0
-rw-r--r--test/val/val_extension_spv_khr_subgroup_rotate_test.cpp352
-rw-r--r--test/val/val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp (renamed from test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp)0
-rw-r--r--test/val/val_extension_spv_khr_terminate_invocation_test.cpp (renamed from test/val/val_extension_spv_khr_terminate_invocation.cpp)0
-rw-r--r--test/val/val_fixtures.h18
-rw-r--r--test/val/val_id_test.cpp113
-rw-r--r--test/val/val_image_test.cpp255
-rw-r--r--test/val/val_interfaces_test.cpp2
-rw-r--r--test/val/val_layout_test.cpp92
-rw-r--r--test/val/val_logicals_test.cpp63
-rw-r--r--test/val/val_memory_test.cpp365
-rw-r--r--test/val/val_mesh_shading_test.cpp95
-rw-r--r--test/val/val_modes_test.cpp83
-rw-r--r--test/val/val_ray_query_test.cpp631
-rw-r--r--test/val/val_ray_tracing_test.cpp583
-rw-r--r--test/val/val_storage_test.cpp287
-rw-r--r--tools/CMakeLists.txt1
-rw-r--r--tools/diff/diff.cpp201
-rw-r--r--tools/fuzz/fuzz.cpp22
-rw-r--r--tools/io.h65
-rw-r--r--tools/opt/opt.cpp17
-rw-r--r--tools/sva/yarn.lock6
-rwxr-xr-xutils/check_copyright.py28
-rwxr-xr-xutils/roll_deps.sh2
-rwxr-xr-xutils/update_build_version.py23
331 files changed, 40230 insertions, 9052 deletions
diff --git a/.gitignore b/.gitignore
index b2af56e9..ec709ba7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,7 @@
.ycm_extra_conf.py*
*.pyc
compile_commands.json
-/build/
+/build*/
/buildtools/
/external/googletest
/external/SPIRV-Headers
@@ -20,6 +20,7 @@ bazel-bin
bazel-genfiles
bazel-out
bazel-spirv-tools
+bazel-SPIRV-Tools
bazel-testlogs
# Vim
diff --git a/Android.bp b/Android.bp
index ca6b9f59..3d98bfb9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -113,8 +113,7 @@ genrule { // FIXME this relies on `git` which is no good on build machines
out: ["build-version.inc"],
srcs: ["CHANGES"],
tool_files: ["utils/update_build_version.py"],
- cmd: "$(location) $$(dirname $(location CHANGES)) " +
- "$(location build-version.inc)",
+ cmd: "$(location) $(location CHANGES) $(location build-version.inc)",
}
cc_library {
diff --git a/BUILD.bazel b/BUILD.bazel
index b2031ded..35dfd665 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -81,7 +81,8 @@ genrule(
srcs = ["@spirv_headers//:spirv_xml_registry"],
outs = ["generators.inc"],
cmd = "$(location generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)",
- tools = [":generate_registry_tables"],
+ cmd_bat = "$(location //:generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)",
+ exec_tools = [":generate_registry_tables"],
)
py_binary(
@@ -93,8 +94,9 @@ genrule(
name = "gen_build_version",
srcs = ["CHANGES"],
outs = ["build-version.inc"],
- cmd = "SOURCE_DATE_EPOCH=0 $(location update_build_version) $$(dirname $(location CHANGES)) $(location build-version.inc)",
- tools = [":update_build_version"],
+ cmd = "SOURCE_DATE_EPOCH=0 $(location update_build_version) $(location CHANGES) $(location build-version.inc)",
+ cmd_bat = "set SOURCE_DATE_EPOCH=0 && $(location //:update_build_version) $(location CHANGES) $(location build-version.inc)",
+ exec_tools = [":update_build_version"],
)
# Libraries
diff --git a/BUILD.gn b/BUILD.gn
index 4d154caf..ac75cbaa 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -19,6 +19,17 @@ if (build_with_chromium) {
import("//third_party/protobuf/proto_library.gni")
}
+# SPIRV-Tools may be part of multiple projects in the Chromium tree.
+# Only enable building executables if this is the main copy.
+abspath = get_path_info(".", "abspath")
+spvtools_chromium_third_party = (abspath == "//third_party/vulkan-deps/spirv-tools/src/")
+spvtools_build_executables = build_with_chromium && spvtools_chromium_third_party
+# Fuchsia also requires building the executables.
+# TODO(b/158002593): Avoid the use of dependent-specific variables.
+if (defined(is_fuchsia_tree) && is_fuchsia_tree) {
+ spvtools_build_executables = true
+}
+
spirv_headers = spirv_tools_spirv_headers_dir
spirv_is_winuwp = is_win && target_os == "winuwp"
@@ -245,12 +256,12 @@ action("spvtools_generators_inc") {
action("spvtools_build_version") {
script = "utils/update_build_version.py"
- src_dir = "."
+ changes_file = "CHANGES"
inc_file = "${target_gen_dir}/build-version.inc"
outputs = [ inc_file ]
args = [
- rebase_path(src_dir, root_build_dir),
+ rebase_path(changes_file, root_build_dir),
rebase_path(inc_file, root_build_dir),
]
}
@@ -448,6 +459,7 @@ static_library("spvtools") {
"source/util/bit_vector.cpp",
"source/util/bit_vector.h",
"source/util/bitutils.h",
+ "source/util/hash_combine.h",
"source/util/hex_float.h",
"source/util/ilist.h",
"source/util/ilist_node.h",
@@ -518,6 +530,8 @@ static_library("spvtools_val") {
"source/val/validate_mode_setting.cpp",
"source/val/validate_non_uniform.cpp",
"source/val/validate_primitives.cpp",
+ "source/val/validate_ray_query.cpp",
+ "source/val/validate_ray_tracing.cpp",
"source/val/validate_scopes.cpp",
"source/val/validate_scopes.h",
"source/val/validate_small_type_uses.cpp",
@@ -609,11 +623,15 @@ static_library("spvtools_opt") {
"source/opt/eliminate_dead_functions_pass.h",
"source/opt/eliminate_dead_functions_util.cpp",
"source/opt/eliminate_dead_functions_util.h",
+ "source/opt/eliminate_dead_input_components_pass.cpp",
+ "source/opt/eliminate_dead_input_components_pass.h",
"source/opt/eliminate_dead_members_pass.cpp",
"source/opt/eliminate_dead_members_pass.h",
"source/opt/empty_pass.h",
"source/opt/feature_manager.cpp",
"source/opt/feature_manager.h",
+ "source/opt/fix_func_call_arguments.cpp",
+ "source/opt/fix_func_call_arguments.h",
"source/opt/fix_storage_class.cpp",
"source/opt/fix_storage_class.h",
"source/opt/flatten_decoration_pass.cpp",
@@ -650,6 +668,8 @@ static_library("spvtools_opt") {
"source/opt/instruction_list.h",
"source/opt/instrument_pass.cpp",
"source/opt/instrument_pass.h",
+ "source/opt/interface_var_sroa.cpp",
+ "source/opt/interface_var_sroa.h",
"source/opt/interp_fixup_pass.cpp",
"source/opt/interp_fixup_pass.h",
"source/opt/ir_builder.h",
@@ -714,6 +734,8 @@ static_library("spvtools_opt") {
"source/opt/register_pressure.h",
"source/opt/relax_float_ops_pass.cpp",
"source/opt/relax_float_ops_pass.h",
+ "source/opt/remove_dontinline_pass.cpp",
+ "source/opt/remove_dontinline_pass.h",
"source/opt/remove_duplicates_pass.cpp",
"source/opt/remove_duplicates_pass.h",
"source/opt/remove_unused_interface_variables_pass.cpp",
@@ -871,7 +893,7 @@ static_library("spvtools_reduce") {
configs += [ ":spvtools_internal_config" ]
}
-if (build_with_chromium) {
+if (build_with_chromium && spvtools_build_executables) {
# The spirv-fuzz library is only built when in a Chromium checkout
# due to its dependency on protobuf.
@@ -1300,7 +1322,7 @@ group("SPIRV-Tools") {
# The tests are scoped to Chromium to avoid needing to write gtest integration.
# See Chromium's third_party/googletest/BUILD.gn for a complete integration.
-if (build_with_chromium) {
+if (build_with_chromium && spvtools_build_executables) {
test("spvtools_test") {
sources = [
"test/assembly_context_test.cpp",
@@ -1409,73 +1431,75 @@ source_set("spvtools_software_version") {
configs += [ ":spvtools_internal_config" ]
}
-executable("spirv-as") {
- sources = [ "tools/as/as.cpp" ]
- deps = [
- ":spvtools",
- ":spvtools_software_version",
- ]
- configs += [ ":spvtools_internal_config" ]
-}
+if (spvtools_build_executables) {
+ executable("spirv-as") {
+ sources = [ "tools/as/as.cpp" ]
+ deps = [
+ ":spvtools",
+ ":spvtools_software_version",
+ ]
+ configs += [ ":spvtools_internal_config" ]
+ }
-executable("spirv-dis") {
- sources = [ "tools/dis/dis.cpp" ]
- deps = [
- ":spvtools",
- ":spvtools_software_version",
- ]
- configs += [ ":spvtools_internal_config" ]
-}
+ executable("spirv-dis") {
+ sources = [ "tools/dis/dis.cpp" ]
+ deps = [
+ ":spvtools",
+ ":spvtools_software_version",
+ ]
+ configs += [ ":spvtools_internal_config" ]
+ }
-executable("spirv-val") {
- sources = [ "tools/val/val.cpp" ]
- deps = [
- ":spvtools",
- ":spvtools_software_version",
- ":spvtools_util_cli_consumer",
- ":spvtools_val",
- ]
- configs += [ ":spvtools_internal_config" ]
-}
+ executable("spirv-val") {
+ sources = [ "tools/val/val.cpp" ]
+ deps = [
+ ":spvtools",
+ ":spvtools_software_version",
+ ":spvtools_util_cli_consumer",
+ ":spvtools_val",
+ ]
+ configs += [ ":spvtools_internal_config" ]
+ }
-executable("spirv-cfg") {
- sources = [
- "tools/cfg/bin_to_dot.cpp",
- "tools/cfg/bin_to_dot.h",
- "tools/cfg/cfg.cpp",
- ]
- deps = [
- ":spvtools",
- ":spvtools_software_version",
- ]
- configs += [ ":spvtools_internal_config" ]
-}
+ executable("spirv-cfg") {
+ sources = [
+ "tools/cfg/bin_to_dot.cpp",
+ "tools/cfg/bin_to_dot.h",
+ "tools/cfg/cfg.cpp",
+ ]
+ deps = [
+ ":spvtools",
+ ":spvtools_software_version",
+ ]
+ configs += [ ":spvtools_internal_config" ]
+ }
-executable("spirv-opt") {
- sources = [ "tools/opt/opt.cpp" ]
- deps = [
- ":spvtools",
- ":spvtools_opt",
- ":spvtools_software_version",
- ":spvtools_util_cli_consumer",
- ":spvtools_val",
- ]
- configs += [ ":spvtools_internal_config" ]
-}
+ executable("spirv-opt") {
+ sources = [ "tools/opt/opt.cpp" ]
+ deps = [
+ ":spvtools",
+ ":spvtools_opt",
+ ":spvtools_software_version",
+ ":spvtools_util_cli_consumer",
+ ":spvtools_val",
+ ]
+ configs += [ ":spvtools_internal_config" ]
+ }
-executable("spirv-link") {
- sources = [ "tools/link/linker.cpp" ]
- deps = [
- ":spvtools",
- ":spvtools_link",
- ":spvtools_opt",
- ":spvtools_software_version",
- ":spvtools_val",
- ]
- configs += [ ":spvtools_internal_config" ]
+ executable("spirv-link") {
+ sources = [ "tools/link/linker.cpp" ]
+ deps = [
+ ":spvtools",
+ ":spvtools_link",
+ ":spvtools_opt",
+ ":spvtools_software_version",
+ ":spvtools_val",
+ ]
+ configs += [ ":spvtools_internal_config" ]
+ }
}
-if (!is_ios && !spirv_is_winuwp && build_with_chromium) {
+if (!is_ios && !spirv_is_winuwp && build_with_chromium && spvtools_build_executables) {
# iOS and UWP do not allow std::system calls which spirv-fuzz
# requires. Additionally, spirv-fuzz is only built when in a
# Chromium checkout due to its dependency on protobuf.
@@ -1496,7 +1520,7 @@ if (!is_ios && !spirv_is_winuwp && build_with_chromium) {
}
}
-if (!is_ios && !spirv_is_winuwp) {
+if (!is_ios && !spirv_is_winuwp && spvtools_build_executables) {
# iOS and UWP do not allow std::system calls which spirv-reduce
# requires.
@@ -1514,19 +1538,21 @@ if (!is_ios && !spirv_is_winuwp) {
}
}
-group("all_spirv_tools") {
- deps = [
- ":spirv-as",
- ":spirv-cfg",
- ":spirv-dis",
- ":spirv-link",
- ":spirv-opt",
- ":spirv-val",
- ]
- if (!is_ios && !spirv_is_winuwp && build_with_chromium) {
- deps += [ ":spirv-fuzz" ]
- }
- if (!is_ios && !spirv_is_winuwp) {
- deps += [ ":spirv-reduce" ]
+if (spvtools_build_executables){
+ group("all_spirv_tools") {
+ deps = [
+ ":spirv-as",
+ ":spirv-cfg",
+ ":spirv-dis",
+ ":spirv-link",
+ ":spirv-opt",
+ ":spirv-val",
+ ]
+ if (!is_ios && !spirv_is_winuwp && build_with_chromium) {
+ deps += [ ":spirv-fuzz" ]
+ }
+ if (!is_ios && !spirv_is_winuwp) {
+ deps += [ ":spirv-reduce" ]
+ }
}
}
diff --git a/CHANGES b/CHANGES
index 246e4839..b59b74fa 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,7 +1,72 @@
Revision history for SPIRV-Tools
-v2022.2-dev 2022-01-26
- - Start v2022.2-dev
+v2022.4-dev 2022-08-11
+ - Start v2022.4-dev release.
+
+v2022.3 2022-08-08
+ - General
+ - Add SPV_KHR_fragment_shader_barycentric support (#4805)
+ - Add support for SPV_KHR_subgroup_rotate (#4786)
+ - use exec_tools instead of tools for better RBE compatibility (#4837)
+ - Write binary files to stdout in binary on windows. (#4834)
+ - Allow spirv-opt print-all to show pretty IDs (#4888)
+ - Validator
+ - spirv-val: Add PerVertexKHR (#4807)
+ - spirv-opt : Add FixFuncCallArgumentsPass (#4775)
+ - spirv-val: Add CullMaskKHR support (#4792)
+ - Require ColMajor or RowMajor for matrices (#4878)
+ - spirv-val: Add SPV_KHR_ray_query (#4848)
+ - spirv-val: Add SPV_KHR_ray_tracing instructions (#4871)
+ - Implement SPV_NV_bindless_texture related changes (#4847)
+ - spirv-val: Add OpConvertUToAccelerationStructureKHR (#4838)
+ - spirv-val: Add support for SPV_AMD_shader_early_and_late_fragment_tests (#4812)
+ - Optimizer
+ - Fold multiply and subtraction into FMA with negation (#4808)
+ - Add more folding for composite instructions (#4802)
+ - spirv-opt: add pass for interface variable scalar replacement (#4779)
+ - Don't try to unroll loop with step count 0. (#4769)
+ - spirv-opt: SPV_NV_bindless_texture related changes (#4870)
+ - Linker
+ - linker: Recalculate interface variables (#4784)
+
+v2022.2 2022-04-07
+ - General
+ - Add OpModuleProcessed to debug opcode (#4694)
+ - Optimizer
+ - Complete handling of RayQueryKHR type (#4690)
+ - Have scalar replacement use undef instead of null (#4691)
+ - Optimize Instruction::Instruction (#4705)
+ - Handle propagation of arrays with decorations (#4717)
+ - spirv-opt: Add OpExecutionModeId support (#4719)
+ - Optimize Type::HashValue (#4707)
+ - Optimize DefUseManager allocations (#4709)
+ - Add pass to remove DontInline function control (#4747)
+ - Better handling of 0xFFFFFFFF when folding vector shuffle (#4743)
+ - Reset the id bound on the module in compact ids (#4744)
+ - spirv-opt: (WIP) Eliminate Dead Input Component Pass (#4720)
+ - Support SPV_KHR_uniform_group_instructions (#4734)
+ - Handle shaders without execution model in spread-volatile-semantics (#4766)
+ - Validator
+ - Fix handling of Nontemporal image operand (#4692)
+ - [spirv-val] Allow 0 Component Count for DebugTypeArray for Shader (#4706)
+ - spirv-val: Validate DebugTypeMatrix (#4732)
+ - spirv-val: Label Vulkan VUID 04734 (#4739)
+ - spirv-val: Label VUID 06491 (#4745)
+ - spirv-val: Disallow array of push constants (#4742)
+ - spirv-val: Label Vulkan RuntimeArray VUID (#4749)
+ - spirv-val: Add Vulkan Image VUID 06214 (#4750)
+ - spirv-val: Add Vulkan Dref not allowed 3D dim VUID (#4751)
+ - spirv-val: Label and add test for PSB Aligned (#4756)
+ - spirv-val: Add Vulkan 32-bit bit op Base (#4758)
+ - spirv-val: Add more Vulkan VUID labels (#4764)
+ - Diff
+ - Introduce spirv-diff (#4611)
+ - Stabilize the output of spirv-diff (#4698)
+ - spirv-diff: Handle OpSpecConstant array sizes (#4700)
+ - spirv-diff: Match OpSpecConstantComposite correctly (#4704)
+ - spirv-diff: Use GetSingleWord*Operand (#4768)
+ - spirv-diff: Basic support for OpTypeForwardPointer (#4761)
+ - spirv-diff: Fix OpTypeFunction matching w.r.t operand count (#4771)
v2022.1 2022-01-26
- General
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 76b87d8c..1b8fe92f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -56,8 +56,12 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Android")
set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS})
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD")
add_definitions(-DSPIRV_FREEBSD)
+elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "OpenBSD")
+ add_definitions(-DSPIRV_OPENBSD)
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia")
add_definitions(-DSPIRV_FUCHSIA)
+elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "GNU")
+ add_definitions(-DSPIRV_GNU)
else()
message(FATAL_ERROR "Your platform '${CMAKE_SYSTEM_NAME}' is not supported!")
endif()
diff --git a/DEPS b/DEPS
index 81e1b5d6..e0b4c554 100644
--- a/DEPS
+++ b/DEPS
@@ -4,9 +4,9 @@ vars = {
'github': 'https://github.com',
'effcee_revision': 'ddf5e2bb92957dc8a12c5392f8495333d6844133',
- 'googletest_revision': 'f45d5865ed0b2b8912244627cdf508a24cc6ccb4',
- 're2_revision': '611baecbcedc9cec1f46e38616b6d8880b676c03',
- 'spirv_headers_revision': 'b42ba6d92faf6b4938e6f22ddd186dbdacc98d78',
+ 'googletest_revision': '548b13dc3c02b93f60eeff9a0cc6e11c1ea722ca',
+ 're2_revision': '5723bb8950318135ed9cf4fc76bed988a087f536',
+ 'spirv_headers_revision': '87d5b782bec60822aa878941e6b13c0a9a954c9b',
}
deps = {
diff --git a/README.md b/README.md
index 14db1e70..e951e37f 100644
--- a/README.md
+++ b/README.md
@@ -212,6 +212,24 @@ issue](https://github.com/KhronosGroup/SPIRV-Tools/issues]) with
"Fuzzer:" as the start of its title.
+### Diff
+
+*Note:* The diff tool is still under development.
+
+The diff tool takes two SPIR-V files, either in binary or text format and
+produces a diff-style comparison between the two. The instructions between the
+src and dst modules are matched as best as the tool can, and output is produced
+(in src id-space) that shows which instructions are removed in src, added in dst
+or modified between them. The order of instructions are not retained.
+
+Matching instructions between two SPIR-V modules is not trivial, and thus a
+number of heuristics are applied in this tool. In particular, without debug
+information, match functions is nontrivial as they can be reordered. As such,
+this tool is primarily useful to produce the diff of two SPIR-V modules derived
+from the same source, for example before and after a modification to the shader,
+before and after a transformation, or SPIR-V produced from different tools.
+
+
### Extras
* [Utility filters](#utility-filters)
@@ -399,7 +417,7 @@ targets, you need to install CMake Version 2.8.12 or later.
- [Python 3](http://www.python.org/): for utility scripts and running the test
suite.
- [Bazel](https://bazel.build/) (optional): if building the source with Bazel,
-you need to install Bazel Version 0.29.1 on your machine. Other versions may
+you need to install Bazel Version 5.0.0 on your machine. Other versions may
also work, but are not verified.
- [Emscripten SDK](https://emscripten.org) (optional): if building the
WebAssembly module.
@@ -624,6 +642,15 @@ This is experimental.
* `spirv-cfg` - the control flow graph dumper
* `<spirv-dir>/tools/cfg`
+### Diff tool
+
+*Warning:* This functionality is under development, and is incomplete.
+
+The diff tool produces a diff-style comparison between two SPIR-V modules.
+
+* `spirv-diff` - the standalone diff tool
+ * `<spirv-dir>`/tools/diff`
+
### Utility filters
* `spirv-lesspipe.sh` - Automatically disassembles `.spv` binary files for the
diff --git a/build_defs.bzl b/build_defs.bzl
index b2cd41b9..71891372 100644
--- a/build_defs.bzl
+++ b/build_defs.bzl
@@ -68,7 +68,15 @@ def generate_core_tables(version = None):
"--core-insts-output=$(location {3}) " +
"--operand-kinds-output=$(location {4})"
).format(*fmtargs),
- tools = [":generate_grammar_tables"],
+ cmd_bat = (
+ "$(location :generate_grammar_tables) " +
+ "--spirv-core-grammar=$(location {0}) " +
+ "--extinst-debuginfo-grammar=$(location {1}) " +
+ "--extinst-cldebuginfo100-grammar=$(location {2}) " +
+ "--core-insts-output=$(location {3}) " +
+ "--operand-kinds-output=$(location {4})"
+ ).format(*fmtargs),
+ exec_tools = [":generate_grammar_tables"],
visibility = ["//visibility:private"],
)
@@ -97,7 +105,15 @@ def generate_enum_string_mapping(version = None):
"--extension-enum-output=$(location {3}) " +
"--enum-string-mapping-output=$(location {4})"
).format(*fmtargs),
- tools = [":generate_grammar_tables"],
+ cmd_bat = (
+ "$(location :generate_grammar_tables) " +
+ "--spirv-core-grammar=$(location {0}) " +
+ "--extinst-debuginfo-grammar=$(location {1}) " +
+ "--extinst-cldebuginfo100-grammar=$(location {2}) " +
+ "--extension-enum-output=$(location {3}) " +
+ "--enum-string-mapping-output=$(location {4})"
+ ).format(*fmtargs),
+ exec_tools = [":generate_grammar_tables"],
visibility = ["//visibility:private"],
)
@@ -118,7 +134,12 @@ def generate_opencl_tables(version = None):
"--extinst-opencl-grammar=$(location {0}) " +
"--opencl-insts-output=$(location {1})"
).format(*fmtargs),
- tools = [":generate_grammar_tables"],
+ cmd_bat = (
+ "$(location :generate_grammar_tables) " +
+ "--extinst-opencl-grammar=$(location {0}) " +
+ "--opencl-insts-output=$(location {1})"
+ ).format(*fmtargs),
+ exec_tools = [":generate_grammar_tables"],
visibility = ["//visibility:private"],
)
@@ -139,7 +160,12 @@ def generate_glsl_tables(version = None):
"--extinst-glsl-grammar=$(location {0}) " +
"--glsl-insts-output=$(location {1})"
).format(*fmtargs),
- tools = [":generate_grammar_tables"],
+ cmd_bat = (
+ "$(location :generate_grammar_tables) " +
+ "--extinst-glsl-grammar=$(location {0}) " +
+ "--glsl-insts-output=$(location {1})"
+ ).format(*fmtargs),
+ exec_tools = [":generate_grammar_tables"],
visibility = ["//visibility:private"],
)
@@ -161,7 +187,13 @@ def generate_vendor_tables(extension, operand_kind_prefix = ""):
"--vendor-insts-output=$(location {1}) " +
"--vendor-operand-kind-prefix={2}"
).format(*fmtargs),
- tools = [":generate_grammar_tables"],
+ cmd_bat = (
+ "$(location :generate_grammar_tables) " +
+ "--extinst-vendor-grammar=$(location {0}) " +
+ "--vendor-insts-output=$(location {1}) " +
+ "--vendor-operand-kind-prefix={2}"
+ ).format(*fmtargs),
+ exec_tools = [":generate_grammar_tables"],
visibility = ["//visibility:private"],
)
@@ -179,7 +211,12 @@ def generate_extinst_lang_headers(name, grammar = None):
"--extinst-grammar=$< " +
"--extinst-output-path=$(location {0})"
).format(*fmtargs),
- tools = [":generate_language_headers"],
+ cmd_bat = (
+ "$(location :generate_language_headers) " +
+ "--extinst-grammar=$< " +
+ "--extinst-output-path=$(location {0})"
+ ).format(*fmtargs),
+ exec_tools = [":generate_language_headers"],
visibility = ["//visibility:private"],
)
diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp
index fdb2e648..94973560 100644
--- a/include/spirv-tools/optimizer.hpp
+++ b/include/spirv-tools/optimizer.hpp
@@ -886,6 +886,13 @@ Optimizer::PassToken CreateAmdExtToKhrPass();
// propagated into their final positions.
Optimizer::PassToken CreateInterpolateFixupPass();
+// Removes unused components from composite input variables. Current
+// implementation just removes trailing unused components from input arrays.
+// The pass performs best after maximizing dead code removal. A subsequent dead
+// code elimination pass would be beneficial in removing newly unused component
+// types.
+Optimizer::PassToken CreateEliminateDeadInputComponentsPass();
+
// Creates a convert-to-sampled-image pass to convert images and/or
// samplers with given pairs of descriptor set and binding to sampled image.
// If a pair of an image and a sampler have the same pair of descriptor set and
@@ -896,6 +903,20 @@ Optimizer::PassToken CreateConvertToSampledImagePass(
const std::vector<opt::DescriptorSetAndBinding>&
descriptor_set_binding_pairs);
+// Create an interface-variable-scalar-replacement pass that replaces array or
+// matrix interface variables with a series of scalar or vector interface
+// variables. For example, it replaces `float3 foo[2]` with `float3 foo0, foo1`.
+Optimizer::PassToken CreateInterfaceVariableScalarReplacementPass();
+
+// Creates a remove-dont-inline pass to remove the |DontInline| function control
+// from every function in the module. This is useful if you want the inliner to
+// inline these functions some reason.
+Optimizer::PassToken CreateRemoveDontInlinePass();
+// Create a fix-func-call-param pass to fix non memory argument for the function
+// call, as spirv-validation requires function parameters to be an memory
+// object, currently the pass would remove accesschain pointer argument passed
+// to the function
+Optimizer::PassToken CreateFixFuncCallArgumentsPass();
} // namespace spvtools
#endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_
diff --git a/kokoro/macos-clang-release-bazel/build.sh b/kokoro/macos-clang-release-bazel/build.sh
index d2a516f5..c62611ab 100644
--- a/kokoro/macos-clang-release-bazel/build.sh
+++ b/kokoro/macos-clang-release-bazel/build.sh
@@ -31,14 +31,14 @@ cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31
git clone --depth=1 https://github.com/google/effcee external/effcee
git clone --depth=1 https://github.com/google/re2 external/re2
-# Get bazel 0.29.1.
-gsutil cp gs://bazel/0.29.1/release/bazel-0.29.1-darwin-x86_64 .
-chmod +x bazel-0.29.1-darwin-x86_64
+# Get bazel 5.0.0
+gsutil cp gs://bazel/5.0.0/release/bazel-5.0.0-darwin-x86_64 .
+chmod +x bazel-5.0.0-darwin-x86_64
echo $(date): Build everything...
-./bazel-0.29.1-darwin-x86_64 build :all
+./bazel-5.0.0-darwin-x86_64 build :all
echo $(date): Build completed.
echo $(date): Starting bazel test...
-./bazel-0.29.1-darwin-x86_64 test :all
+./bazel-5.0.0-darwin-x86_64 test :all
echo $(date): Bazel test completed.
diff --git a/kokoro/scripts/linux/build-docker.sh b/kokoro/scripts/linux/build-docker.sh
index 8f76803c..80043b8a 100755
--- a/kokoro/scripts/linux/build-docker.sh
+++ b/kokoro/scripts/linux/build-docker.sh
@@ -195,7 +195,7 @@ elif [ $TOOL = "android-ndk-build" ]; then
echo $(date): ndk-build completed.
elif [ $TOOL = "bazel" ]; then
- using bazel-3.1.0
+ using bazel-5.0.0
echo $(date): Build everything...
bazel build :all
diff --git a/kokoro/scripts/windows/build.bat b/kokoro/scripts/windows/build.bat
index 24e29ccf..8c9d6892 100644
--- a/kokoro/scripts/windows/build.bat
+++ b/kokoro/scripts/windows/build.bat
@@ -22,7 +22,7 @@ set BUILD_TYPE=%1
set VS_VERSION=%2
:: Force usage of python 3.6
-set PATH=C:\python36;"C:\Program Files\CMake\bin";%PATH%
+set PATH=C:\python36;"C:\Program Files\cmake-3.23.1-windows-x86_64\bin";%PATH%
cd %SRC%
git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers
diff --git a/kokoro/windows-msvc-2015-release-bazel/build.bat b/kokoro/windows-msvc-2015-release-bazel/build.bat
index 2f721af4..de20b0aa 100644
--- a/kokoro/windows-msvc-2015-release-bazel/build.bat
+++ b/kokoro/windows-msvc-2015-release-bazel/build.bat
@@ -30,14 +30,13 @@ git clone --depth=1 https://github.com/google/effcee external/effce
git clone --depth=1 https://github.com/google/re2 external/re2
:: REM Install Bazel.
-wget -q https://github.com/bazelbuild/bazel/releases/download/0.29.1/bazel-0.29.1-windows-x86_64.zip
-unzip -q bazel-0.29.1-windows-x86_64.zip
+wget -q https://github.com/bazelbuild/bazel/releases/download/5.0.0/bazel-5.0.0-windows-x86_64.zip
+unzip -q bazel-5.0.0-windows-x86_64.zip
:: Set up MSVC
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
set BAZEL_VS=C:\Program Files (x86)\Microsoft Visual Studio 14.0
set BAZEL_VC=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC
-set BAZEL_SH=c:\tools\msys64\usr\bin\bash.exe
set BAZEL_PYTHON=c:\tools\python2\python.exe
:: #########################################
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 331ff675..ab4578b9 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -197,7 +197,7 @@ set(SPIRV_TOOLS_CHANGES_FILE
add_custom_command(OUTPUT ${SPIRV_TOOLS_BUILD_VERSION_INC}
COMMAND ${PYTHON_EXECUTABLE}
${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR}
- ${spirv-tools_SOURCE_DIR} ${SPIRV_TOOLS_BUILD_VERSION_INC}
+ ${SPIRV_TOOLS_CHANGES_FILE} ${SPIRV_TOOLS_BUILD_VERSION_INC}
DEPENDS ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR}
${SPIRV_TOOLS_CHANGES_FILE}
COMMENT "Update build-version.inc in the SPIRV-Tools build directory (if necessary).")
@@ -217,12 +217,14 @@ add_subdirectory(reduce)
add_subdirectory(fuzz)
add_subdirectory(link)
add_subdirectory(lint)
+add_subdirectory(diff)
set(SPIRV_SOURCES
${spirv-tools_SOURCE_DIR}/include/spirv-tools/libspirv.h
${CMAKE_CURRENT_SOURCE_DIR}/util/bitutils.h
${CMAKE_CURRENT_SOURCE_DIR}/util/bit_vector.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/util/hash_combine.h
${CMAKE_CURRENT_SOURCE_DIR}/util/hex_float.h
${CMAKE_CURRENT_SOURCE_DIR}/util/make_unique.h
${CMAKE_CURRENT_SOURCE_DIR}/util/parse_number.h
@@ -320,6 +322,8 @@ set(SPIRV_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_mode_setting.cpp
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_non_uniform.cpp
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_primitives.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_ray_query.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_ray_tracing.cpp
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_scopes.cpp
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_small_type_uses.cpp
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_type.cpp
@@ -404,7 +408,7 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
find_library(LIBRT rt)
if(LIBRT)
foreach(target ${SPIRV_TOOLS_TARGETS})
- target_link_libraries(${target} ${LIBRT})
+ target_link_libraries(${target} rt)
endforeach()
endif()
endif()
diff --git a/source/assembly_grammar.cpp b/source/assembly_grammar.cpp
index 79f18eee..4f5942ab 100644
--- a/source/assembly_grammar.cpp
+++ b/source/assembly_grammar.cpp
@@ -62,9 +62,9 @@ spv_result_t spvTextParseMaskOperand(spv_target_env env,
end = std::find(begin, text_end, separator);
spv_operand_desc entry = nullptr;
- if (spvOperandTableNameLookup(env, operandTable, type, begin, end - begin,
- &entry)) {
- return SPV_ERROR_INVALID_TEXT;
+ if (auto error = spvOperandTableNameLookup(env, operandTable, type, begin,
+ end - begin, &entry)) {
+ return error;
}
value |= entry->value;
diff --git a/source/cfa.h b/source/cfa.h
index 7cadf55f..2743ab40 100644
--- a/source/cfa.h
+++ b/source/cfa.h
@@ -56,8 +56,33 @@ class CFA {
///
/// This function performs a depth first traversal from the \p entry
/// BasicBlock and calls the pre/postorder functions when it needs to process
+ /// the node in pre order, post order.
+ ///
+ /// @param[in] entry The root BasicBlock of a CFG
+ /// @param[in] successor_func A function which will return a pointer to the
+ /// successor nodes
+ /// @param[in] preorder A function that will be called for every block in a
+ /// CFG following preorder traversal semantics
+ /// @param[in] postorder A function that will be called for every block in a
+ /// CFG following postorder traversal semantics
+ /// @param[in] terminal A function that will be called to determine if the
+ /// search should stop at the given node.
+ /// NOTE: The @p successor_func and predecessor_func each return a pointer to
+ /// a collection such that iterators to that collection remain valid for the
+ /// lifetime of the algorithm.
+ static void DepthFirstTraversal(const BB* entry,
+ get_blocks_func successor_func,
+ std::function<void(cbb_ptr)> preorder,
+ std::function<void(cbb_ptr)> postorder,
+ std::function<bool(cbb_ptr)> terminal);
+
+ /// @brief Depth first traversal starting from the \p entry BasicBlock
+ ///
+ /// This function performs a depth first traversal from the \p entry
+ /// BasicBlock and calls the pre/postorder functions when it needs to process
/// the node in pre order, post order. It also calls the backedge function
- /// when a back edge is encountered.
+ /// when a back edge is encountered. The backedge function can be empty. The
+ /// runtime of the algorithm is improved if backedge is empty.
///
/// @param[in] entry The root BasicBlock of a CFG
/// @param[in] successor_func A function which will return a pointer to the
@@ -67,16 +92,18 @@ class CFA {
/// @param[in] postorder A function that will be called for every block in a
/// CFG following postorder traversal semantics
/// @param[in] backedge A function that will be called when a backedge is
- /// encountered during a traversal
+ /// encountered during a traversal.
+ /// @param[in] terminal A function that will be called to determine if the
+ /// search should stop at the given node.
/// NOTE: The @p successor_func and predecessor_func each return a pointer to
- /// a
- /// collection such that iterators to that collection remain valid for the
+ /// a collection such that iterators to that collection remain valid for the
/// lifetime of the algorithm.
static void DepthFirstTraversal(
const BB* entry, get_blocks_func successor_func,
std::function<void(cbb_ptr)> preorder,
std::function<void(cbb_ptr)> postorder,
- std::function<void(cbb_ptr, cbb_ptr)> backedge);
+ std::function<void(cbb_ptr, cbb_ptr)> backedge,
+ std::function<bool(cbb_ptr)> terminal);
/// @brief Calculates dominator edges for a set of blocks
///
@@ -134,11 +161,27 @@ bool CFA<BB>::FindInWorkList(const std::vector<block_info>& work_list,
}
template <class BB>
+void CFA<BB>::DepthFirstTraversal(const BB* entry,
+ get_blocks_func successor_func,
+ std::function<void(cbb_ptr)> preorder,
+ std::function<void(cbb_ptr)> postorder,
+ std::function<bool(cbb_ptr)> terminal) {
+ DepthFirstTraversal(entry, successor_func, preorder, postorder,
+ /* backedge = */ {}, terminal);
+}
+
+template <class BB>
void CFA<BB>::DepthFirstTraversal(
const BB* entry, get_blocks_func successor_func,
std::function<void(cbb_ptr)> preorder,
std::function<void(cbb_ptr)> postorder,
- std::function<void(cbb_ptr, cbb_ptr)> backedge) {
+ std::function<void(cbb_ptr, cbb_ptr)> backedge,
+ std::function<bool(cbb_ptr)> terminal) {
+ assert(successor_func && "The successor function cannot be empty.");
+ assert(preorder && "The preorder function cannot be empty.");
+ assert(postorder && "The postorder function cannot be empty.");
+ assert(terminal && "The terminal function cannot be empty.");
+
std::unordered_set<uint32_t> processed;
/// NOTE: work_list is the sequence of nodes from the root node to the node
@@ -152,13 +195,13 @@ void CFA<BB>::DepthFirstTraversal(
while (!work_list.empty()) {
block_info& top = work_list.back();
- if (top.iter == end(*successor_func(top.block))) {
+ if (terminal(top.block) || top.iter == end(*successor_func(top.block))) {
postorder(top.block);
work_list.pop_back();
} else {
BB* child = *top.iter;
top.iter++;
- if (FindInWorkList(work_list, child->id())) {
+ if (backedge && FindInWorkList(work_list, child->id())) {
backedge(top.block, child);
}
if (processed.count(child->id()) == 0) {
@@ -265,12 +308,12 @@ std::vector<BB*> CFA<BB>::TraversalRoots(const std::vector<BB*>& blocks,
auto mark_visited = [&visited](const BB* b) { visited.insert(b); };
auto ignore_block = [](const BB*) {};
- auto ignore_blocks = [](const BB*, const BB*) {};
+ auto no_terminal_blocks = [](const BB*) { return false; };
auto traverse_from_root = [&mark_visited, &succ_func, &ignore_block,
- &ignore_blocks](const BB* entry) {
+ &no_terminal_blocks](const BB* entry) {
DepthFirstTraversal(entry, succ_func, mark_visited, ignore_block,
- ignore_blocks);
+ no_terminal_blocks);
};
std::vector<BB*> result;
diff --git a/source/diff/CMakeLists.txt b/source/diff/CMakeLists.txt
new file mode 100644
index 00000000..1328699a
--- /dev/null
+++ b/source/diff/CMakeLists.txt
@@ -0,0 +1,54 @@
+# Copyright (c) 2022 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.
+set(SPIRV_TOOLS_DIFF_SOURCES
+ diff.h
+ lcs.h
+
+ diff.cpp
+)
+
+add_library(SPIRV-Tools-diff ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_DIFF_SOURCES})
+
+spvtools_default_compile_options(SPIRV-Tools-diff)
+target_include_directories(SPIRV-Tools-diff
+ PUBLIC
+ $<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
+ $<BUILD_INTERFACE:${SPIRV_HEADER_INCLUDE_DIR}>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+ PRIVATE ${spirv-tools_BINARY_DIR}
+)
+# We need the assembling and disassembling functionalities in the main library.
+target_link_libraries(SPIRV-Tools-diff
+ PUBLIC ${SPIRV_TOOLS_FULL_VISIBILITY})
+# We need the internals of spirv-opt.
+target_link_libraries(SPIRV-Tools-diff
+ PUBLIC SPIRV-Tools-opt)
+
+set_property(TARGET SPIRV-Tools-diff PROPERTY FOLDER "SPIRV-Tools libraries")
+spvtools_check_symbol_exports(SPIRV-Tools-diff)
+
+if(ENABLE_SPIRV_TOOLS_INSTALL)
+ install(TARGETS SPIRV-Tools-diff EXPORT SPIRV-Tools-diffTargets
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+ export(EXPORT SPIRV-Tools-diffTargets FILE SPIRV-Tools-diffTargets.cmake)
+
+ spvtools_config_package_dir(SPIRV-Tools-diff PACKAGE_DIR)
+ install(EXPORT SPIRV-Tools-diffTargets FILE SPIRV-Tools-diffTargets.cmake
+ DESTINATION ${PACKAGE_DIR})
+
+ spvtools_generate_config_file(SPIRV-Tools-diff)
+ install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-diffConfig.cmake DESTINATION ${PACKAGE_DIR})
+endif(ENABLE_SPIRV_TOOLS_INSTALL)
diff --git a/source/diff/diff.cpp b/source/diff/diff.cpp
new file mode 100644
index 00000000..7ed41de5
--- /dev/null
+++ b/source/diff/diff.cpp
@@ -0,0 +1,2869 @@
+// Copyright (c) 2022 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/diff/diff.h"
+
+#include "source/diff/lcs.h"
+#include "source/disassemble.h"
+#include "source/ext_inst.h"
+#include "source/latest_version_spirv_header.h"
+#include "source/print.h"
+#include "spirv-tools/libspirv.hpp"
+
+namespace spvtools {
+namespace diff {
+
+namespace {
+
+// A map from an id to the instruction that defines it.
+using IdToInstructionMap = std::vector<const opt::Instruction*>;
+// A map from an id to the instructions that decorate it, or name it, etc.
+using IdToInfoMap = std::vector<std::vector<const opt::Instruction*>>;
+// A map from an instruction to another, used for instructions without id.
+using InstructionToInstructionMap =
+ std::unordered_map<const opt::Instruction*, const opt::Instruction*>;
+// A flat list of instructions in a function for easier iteration.
+using InstructionList = std::vector<const opt::Instruction*>;
+// A map from a function to its list of instructions.
+using FunctionInstMap = std::map<uint32_t, InstructionList>;
+// A list of ids with some similar property, for example functions with the same
+// name.
+using IdGroup = std::vector<uint32_t>;
+// A map of names to ids with the same name. This is an ordered map so
+// different implementations produce identical results.
+using IdGroupMapByName = std::map<std::string, IdGroup>;
+using IdGroupMapByTypeId = std::map<uint32_t, IdGroup>;
+using IdGroupMapByOp = std::map<SpvOp, IdGroup>;
+using IdGroupMapByStorageClass = std::map<SpvStorageClass, IdGroup>;
+
+// A set of potential id mappings that haven't been resolved yet. Any id in src
+// may map in any id in dst. Note that ids are added in the same order as they
+// appear in src and dst to facilitate matching dependent instructions. For
+// example, this guarantees that when matching OpTypeVector, the basic type of
+// the vector is already (potentially) matched.
+struct PotentialIdMap {
+ std::vector<uint32_t> src_ids;
+ std::vector<uint32_t> dst_ids;
+};
+
+void CompactIds(std::vector<uint32_t>& ids) {
+ size_t write_index = 0;
+ for (size_t i = 0; i < ids.size(); ++i) {
+ if (ids[i] != 0) {
+ ids[write_index++] = ids[i];
+ }
+ }
+ ids.resize(write_index);
+}
+
+// A mapping between src and dst ids.
+class IdMap {
+ public:
+ IdMap(size_t id_bound) { id_map_.resize(id_bound, 0); }
+
+ void MapIds(uint32_t from, uint32_t to) {
+ assert(from != 0);
+ assert(to != 0);
+ assert(from < id_map_.size());
+ assert(id_map_[from] == 0);
+
+ id_map_[from] = to;
+ }
+
+ uint32_t MappedId(uint32_t from) const {
+ assert(from != 0);
+ return from < id_map_.size() ? id_map_[from] : 0;
+ }
+ const opt::Instruction* MappedInst(const opt::Instruction* from_inst) const {
+ assert(from_inst != nullptr);
+ assert(!from_inst->HasResultId());
+
+ auto mapped = inst_map_.find(from_inst);
+ if (mapped == inst_map_.end()) {
+ return nullptr;
+ }
+ return mapped->second;
+ }
+
+ bool IsMapped(uint32_t from) const {
+ assert(from != 0);
+ return from < id_map_.size() && id_map_[from] != 0;
+ }
+
+ // Map any ids in src and dst that have not been mapped to new ids in dst and
+ // src respectively.
+ void MapUnmatchedIds(IdMap& other_way);
+
+ // Some instructions don't have result ids. Those are mapped by pointer.
+ void MapInsts(const opt::Instruction* from_inst,
+ const opt::Instruction* to_inst) {
+ assert(from_inst != nullptr);
+ assert(to_inst != nullptr);
+ assert(inst_map_.find(from_inst) == inst_map_.end());
+
+ inst_map_[from_inst] = to_inst;
+ }
+
+ uint32_t IdBound() const { return static_cast<uint32_t>(id_map_.size()); }
+
+ private:
+ // Given an id, returns the corresponding id in the other module, or 0 if not
+ // matched yet.
+ std::vector<uint32_t> id_map_;
+
+ // Same for instructions that don't have an id.
+ InstructionToInstructionMap inst_map_;
+};
+
+// Two way mapping of ids.
+class SrcDstIdMap {
+ public:
+ SrcDstIdMap(size_t src_id_bound, size_t dst_id_bound)
+ : src_to_dst_(src_id_bound), dst_to_src_(dst_id_bound) {}
+
+ void MapIds(uint32_t src, uint32_t dst) {
+ src_to_dst_.MapIds(src, dst);
+ dst_to_src_.MapIds(dst, src);
+ }
+
+ uint32_t MappedDstId(uint32_t src) {
+ uint32_t dst = src_to_dst_.MappedId(src);
+ assert(dst == 0 || dst_to_src_.MappedId(dst) == src);
+ return dst;
+ }
+ uint32_t MappedSrcId(uint32_t dst) {
+ uint32_t src = dst_to_src_.MappedId(dst);
+ assert(src == 0 || src_to_dst_.MappedId(src) == dst);
+ return src;
+ }
+
+ bool IsSrcMapped(uint32_t src) { return src_to_dst_.IsMapped(src); }
+ bool IsDstMapped(uint32_t dst) { return dst_to_src_.IsMapped(dst); }
+
+ // Map any ids in src and dst that have not been mapped to new ids in dst and
+ // src respectively.
+ void MapUnmatchedIds();
+
+ // Some instructions don't have result ids. Those are mapped by pointer.
+ void MapInsts(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst) {
+ assert(src_inst->HasResultId() == dst_inst->HasResultId());
+ if (src_inst->HasResultId()) {
+ MapIds(src_inst->result_id(), dst_inst->result_id());
+ } else {
+ src_to_dst_.MapInsts(src_inst, dst_inst);
+ dst_to_src_.MapInsts(dst_inst, src_inst);
+ }
+ }
+
+ const IdMap& SrcToDstMap() const { return src_to_dst_; }
+ const IdMap& DstToSrcMap() const { return dst_to_src_; }
+
+ private:
+ IdMap src_to_dst_;
+ IdMap dst_to_src_;
+};
+
+struct IdInstructions {
+ IdInstructions(const opt::Module* module)
+ : inst_map_(module->IdBound(), nullptr),
+ name_map_(module->IdBound()),
+ decoration_map_(module->IdBound()),
+ forward_pointer_map_(module->IdBound()) {
+ // Map ids from all sections to instructions that define them.
+ MapIdsToInstruction(module->ext_inst_imports());
+ MapIdsToInstruction(module->types_values());
+ for (const opt::Function& function : *module) {
+ function.ForEachInst(
+ [this](const opt::Instruction* inst) {
+ if (inst->HasResultId()) {
+ MapIdToInstruction(inst->result_id(), inst);
+ }
+ },
+ true, true);
+ }
+
+ // Gather decorations applied to ids that could be useful in matching them
+ // between src and dst modules.
+ MapIdsToInfos(module->debugs2());
+ MapIdsToInfos(module->annotations());
+ MapIdsToInfos(module->types_values());
+ }
+
+ void MapIdToInstruction(uint32_t id, const opt::Instruction* inst);
+
+ void MapIdsToInstruction(
+ opt::IteratorRange<opt::Module::const_inst_iterator> section);
+ void MapIdsToInfos(
+ opt::IteratorRange<opt::Module::const_inst_iterator> section);
+
+ IdToInstructionMap inst_map_;
+ IdToInfoMap name_map_;
+ IdToInfoMap decoration_map_;
+ IdToInstructionMap forward_pointer_map_;
+};
+
+class Differ {
+ public:
+ Differ(opt::IRContext* src, opt::IRContext* dst, std::ostream& out,
+ Options options)
+ : src_context_(src),
+ dst_context_(dst),
+ src_(src->module()),
+ dst_(dst->module()),
+ options_(options),
+ out_(out),
+ src_id_to_(src_),
+ dst_id_to_(dst_),
+ id_map_(src_->IdBound(), dst_->IdBound()) {
+ // Cache function bodies in canonicalization order.
+ GetFunctionBodies(src_context_, &src_funcs_, &src_func_insts_);
+ GetFunctionBodies(dst_context_, &dst_funcs_, &dst_func_insts_);
+ }
+
+ // Match ids or instructions of different sections.
+ void MatchCapabilities();
+ void MatchExtensions();
+ void MatchExtInstImportIds();
+ void MatchMemoryModel();
+ void MatchEntryPointIds();
+ void MatchExecutionModes();
+ void MatchTypeForwardPointers();
+ void MatchTypeIds();
+ void MatchConstants();
+ void MatchVariableIds();
+ void MatchFunctions();
+
+ // Debug info and annotations are matched only after ids are matched.
+ void MatchDebugs1();
+ void MatchDebugs2();
+ void MatchDebugs3();
+ void MatchExtInstDebugInfo();
+ void MatchAnnotations();
+
+ // Output the diff.
+ spv_result_t Output();
+
+ void DumpIdMap() {
+ if (!options_.dump_id_map) {
+ return;
+ }
+
+ out_ << " Src -> Dst\n";
+ for (uint32_t src_id = 1; src_id < src_->IdBound(); ++src_id) {
+ uint32_t dst_id = id_map_.MappedDstId(src_id);
+ if (src_id_to_.inst_map_[src_id] != nullptr && dst_id != 0)
+ out_ << std::setw(4) << src_id << " -> " << std::setw(4) << dst_id
+ << " [" << spvOpcodeString(src_id_to_.inst_map_[src_id]->opcode())
+ << "]\n";
+ }
+ }
+
+ private:
+ // Helper functions that match ids between src and dst
+ void PoolPotentialIds(
+ opt::IteratorRange<opt::Module::const_inst_iterator> section,
+ std::vector<uint32_t>& ids, bool is_src,
+ std::function<bool(const opt::Instruction&)> filter,
+ std::function<uint32_t(const opt::Instruction&)> get_id);
+ void MatchIds(
+ PotentialIdMap& potential,
+ std::function<bool(const opt::Instruction*, const opt::Instruction*)>
+ match);
+ // Helper functions that match id-less instructions between src and dst.
+ void MatchPreambleInstructions(
+ opt::IteratorRange<opt::Module::const_inst_iterator> src_insts,
+ opt::IteratorRange<opt::Module::const_inst_iterator> dst_insts);
+ InstructionList SortPreambleInstructions(
+ const opt::Module* module,
+ opt::IteratorRange<opt::Module::const_inst_iterator> insts);
+ int ComparePreambleInstructions(const opt::Instruction* a,
+ const opt::Instruction* b,
+ const opt::Module* src_inst_module,
+ const opt::Module* dst_inst_module);
+ // Helper functions that match debug and annotation instructions of already
+ // matched ids.
+ void MatchDebugAndAnnotationInstructions(
+ opt::IteratorRange<opt::Module::const_inst_iterator> src_insts,
+ opt::IteratorRange<opt::Module::const_inst_iterator> dst_insts);
+
+ // Get various properties from an id. These Helper functions are passed to
+ // `GroupIds` and `GroupIdsAndMatch` below (as the `get_group` argument).
+ uint32_t GroupIdsHelperGetTypeId(const IdInstructions& id_to, uint32_t id);
+ SpvStorageClass GroupIdsHelperGetTypePointerStorageClass(
+ const IdInstructions& id_to, uint32_t id);
+ SpvOp GroupIdsHelperGetTypePointerTypeOp(const IdInstructions& id_to,
+ uint32_t id);
+
+ // Given a list of ids, groups them based on some value. The `get_group`
+ // function extracts a piece of information corresponding to each id, and the
+ // ids are bucketed based on that (and output in `groups`). This is useful to
+ // attempt to match ids between src and dst only when said property is
+ // identical.
+ template <typename T>
+ void GroupIds(const IdGroup& ids, bool is_src, std::map<T, IdGroup>* groups,
+ T (Differ::*get_group)(const IdInstructions&, uint32_t));
+
+ // Calls GroupIds to bucket ids in src and dst based on a property returned by
+ // `get_group`. This function then calls `match_group` for each bucket (i.e.
+ // "group") with identical values for said property.
+ //
+ // For example, say src and dst ids have the following properties
+ // correspondingly:
+ //
+ // - src ids' properties: {id0: A, id1: A, id2: B, id3: C, id4: B}
+ // - dst ids' properties: {id0': B, id1': C, id2': B, id3': D, id4': B}
+ //
+ // Then `match_group` is called 2 times:
+ //
+ // - Once with: ([id2, id4], [id0', id2', id4']) corresponding to B
+ // - Once with: ([id3], [id2']) corresponding to C
+ //
+ // Ids corresponding to A and D cannot match based on this property.
+ template <typename T>
+ void GroupIdsAndMatch(
+ const IdGroup& src_ids, const IdGroup& dst_ids, T invalid_group_key,
+ T (Differ::*get_group)(const IdInstructions&, uint32_t),
+ std::function<void(const IdGroup& src_group, const IdGroup& dst_group)>
+ match_group);
+
+ // Helper functions that determine if two instructions match
+ bool DoIdsMatch(uint32_t src_id, uint32_t dst_id);
+ bool DoesOperandMatch(const opt::Operand& src_operand,
+ const opt::Operand& dst_operand);
+ bool DoOperandsMatch(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst,
+ uint32_t in_operand_index_start,
+ uint32_t in_operand_count);
+ bool DoInstructionsMatch(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst);
+ bool DoIdsMatchFuzzy(uint32_t src_id, uint32_t dst_id);
+ bool DoesOperandMatchFuzzy(const opt::Operand& src_operand,
+ const opt::Operand& dst_operand);
+ bool DoInstructionsMatchFuzzy(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst);
+ bool AreIdenticalUintConstants(uint32_t src_id, uint32_t dst_id);
+ bool DoDebugAndAnnotationInstructionsMatch(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst);
+ bool AreVariablesMatchable(uint32_t src_id, uint32_t dst_id,
+ uint32_t flexibility);
+ bool MatchOpTypeStruct(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst,
+ uint32_t flexibility);
+ bool MatchOpConstant(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst, uint32_t flexibility);
+ bool MatchOpSpecConstant(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst);
+ bool MatchOpVariable(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst, uint32_t flexibility);
+ bool MatchPerVertexType(uint32_t src_type_id, uint32_t dst_type_id);
+ bool MatchPerVertexVariable(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst);
+
+ // Helper functions for matching OpTypeForwardPointer
+ void MatchTypeForwardPointersByName(const IdGroup& src, const IdGroup& dst);
+ void MatchTypeForwardPointersByTypeOp(const IdGroup& src, const IdGroup& dst);
+
+ // Helper functions for function matching.
+ using FunctionMap = std::map<uint32_t, const opt::Function*>;
+
+ InstructionList GetFunctionBody(opt::IRContext* context,
+ opt::Function& function);
+ InstructionList GetFunctionHeader(const opt::Function& function);
+ void GetFunctionBodies(opt::IRContext* context, FunctionMap* functions,
+ FunctionInstMap* function_insts);
+ void GetFunctionHeaderInstructions(const opt::Module* module,
+ FunctionInstMap* function_insts);
+ void BestEffortMatchFunctions(const IdGroup& src_func_ids,
+ const IdGroup& dst_func_ids,
+ const FunctionInstMap& src_func_insts,
+ const FunctionInstMap& dst_func_insts);
+
+ // Calculates the diff of two function bodies. Note that the matched
+ // instructions themselves may not be identical; output of exact matches
+ // should produce the exact instruction while inexact matches should produce a
+ // diff as well.
+ //
+ // Returns the similarity of the two bodies = 2*N_match / (N_src + N_dst)
+ void MatchFunctionParamIds(const opt::Function* src_func,
+ const opt::Function* dst_func);
+ float MatchFunctionBodies(const InstructionList& src_body,
+ const InstructionList& dst_body,
+ DiffMatch* src_match_result,
+ DiffMatch* dst_match_result);
+ void MatchIdsInFunctionBodies(const InstructionList& src_body,
+ const InstructionList& dst_body,
+ const DiffMatch& src_match_result,
+ const DiffMatch& dst_match_result,
+ uint32_t flexibility);
+ void MatchVariablesUsedByMatchedInstructions(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst,
+ uint32_t flexibility);
+
+ // Helper functions to retrieve information pertaining to an id
+ const opt::Instruction* GetInst(const IdInstructions& id_to, uint32_t id);
+ uint32_t GetConstantUint(const IdInstructions& id_to, uint32_t constant_id);
+ SpvExecutionModel GetExecutionModel(const opt::Module* module,
+ uint32_t entry_point_id);
+ bool HasName(const IdInstructions& id_to, uint32_t id);
+ // Get the OpName associated with an id
+ std::string GetName(const IdInstructions& id_to, uint32_t id, bool* has_name);
+ // Get the OpName associated with an id, with argument types stripped for
+ // functions. Some tools don't encode function argument types in the OpName
+ // string, and this improves diff between SPIR-V from those tools and others.
+ std::string GetSanitizedName(const IdInstructions& id_to, uint32_t id);
+ uint32_t GetVarTypeId(const IdInstructions& id_to, uint32_t var_id,
+ SpvStorageClass* storage_class);
+ bool GetDecorationValue(const IdInstructions& id_to, uint32_t id,
+ SpvDecoration decoration, uint32_t* decoration_value);
+ const opt::Instruction* GetForwardPointerInst(const IdInstructions& id_to,
+ uint32_t id);
+ bool IsIntType(const IdInstructions& id_to, uint32_t type_id);
+ bool IsFloatType(const IdInstructions& id_to, uint32_t type_id);
+ bool IsConstantUint(const IdInstructions& id_to, uint32_t id);
+ bool IsVariable(const IdInstructions& id_to, uint32_t pointer_id);
+ bool IsOp(const IdInstructions& id_to, uint32_t id, SpvOp opcode);
+ bool IsPerVertexType(const IdInstructions& id_to, uint32_t type_id);
+ bool IsPerVertexVariable(const IdInstructions& id_to, uint32_t type_id);
+ SpvStorageClass GetPerVertexStorageClass(const opt::Module* module,
+ uint32_t type_id);
+ spv_ext_inst_type_t GetExtInstType(const IdInstructions& id_to,
+ uint32_t set_id);
+ spv_number_kind_t GetNumberKind(const IdInstructions& id_to,
+ const opt::Instruction& inst,
+ uint32_t operand_index,
+ uint32_t* number_bit_width);
+ spv_number_kind_t GetTypeNumberKind(const IdInstructions& id_to, uint32_t id,
+ uint32_t* number_bit_width);
+
+ // Helper functions to output a diff line
+ const opt::Instruction* MappedDstInst(const opt::Instruction* src_inst);
+ const opt::Instruction* MappedSrcInst(const opt::Instruction* dst_inst);
+ const opt::Instruction* MappedInstImpl(const opt::Instruction* inst,
+ const IdMap& to_other,
+ const IdInstructions& other_id_to);
+ void OutputLine(std::function<bool()> are_lines_identical,
+ std::function<void()> output_src_line,
+ std::function<void()> output_dst_line);
+ template <typename InstList>
+ void OutputSection(
+ const InstList& src_insts, const InstList& dst_insts,
+ std::function<void(const opt::Instruction&, const IdInstructions&,
+ const opt::Instruction&)>
+ write_inst);
+ void ToParsedInstruction(const opt::Instruction& inst,
+ const IdInstructions& id_to,
+ const opt::Instruction& original_inst,
+ spv_parsed_instruction_t* parsed_inst,
+ std::vector<spv_parsed_operand_t>& parsed_operands,
+ std::vector<uint32_t>& inst_binary);
+ opt::Instruction ToMappedSrcIds(const opt::Instruction& dst_inst);
+
+ void OutputRed() {
+ if (options_.color_output) out_ << spvtools::clr::red{true};
+ }
+ void OutputGreen() {
+ if (options_.color_output) out_ << spvtools::clr::green{true};
+ }
+ void OutputResetColor() {
+ if (options_.color_output) out_ << spvtools::clr::reset{true};
+ }
+
+ opt::IRContext* src_context_;
+ opt::IRContext* dst_context_;
+ const opt::Module* src_;
+ const opt::Module* dst_;
+ Options options_;
+ std::ostream& out_;
+
+ // Helpers to look up instructions based on id.
+ IdInstructions src_id_to_;
+ IdInstructions dst_id_to_;
+
+ // The ids that have been matched between src and dst so far.
+ SrcDstIdMap id_map_;
+
+ // List of instructions in function bodies after canonicalization. Cached
+ // here to avoid duplicate work. More importantly, some maps use
+ // opt::Instruction pointers so they need to be unique.
+ FunctionInstMap src_func_insts_;
+ FunctionInstMap dst_func_insts_;
+ FunctionMap src_funcs_;
+ FunctionMap dst_funcs_;
+};
+
+void IdMap::MapUnmatchedIds(IdMap& other_way) {
+ const uint32_t src_id_bound = static_cast<uint32_t>(id_map_.size());
+ const uint32_t dst_id_bound = static_cast<uint32_t>(other_way.id_map_.size());
+
+ uint32_t next_src_id = src_id_bound;
+ uint32_t next_dst_id = dst_id_bound;
+
+ for (uint32_t src_id = 1; src_id < src_id_bound; ++src_id) {
+ if (!IsMapped(src_id)) {
+ MapIds(src_id, next_dst_id);
+
+ other_way.id_map_.push_back(0);
+ other_way.MapIds(next_dst_id++, src_id);
+ }
+ }
+
+ for (uint32_t dst_id = 1; dst_id < dst_id_bound; ++dst_id) {
+ if (!other_way.IsMapped(dst_id)) {
+ id_map_.push_back(0);
+ MapIds(next_src_id, dst_id);
+
+ other_way.MapIds(dst_id, next_src_id++);
+ }
+ }
+}
+
+void SrcDstIdMap::MapUnmatchedIds() {
+ src_to_dst_.MapUnmatchedIds(dst_to_src_);
+}
+
+void IdInstructions::MapIdToInstruction(uint32_t id,
+ const opt::Instruction* inst) {
+ assert(id != 0);
+ assert(id < inst_map_.size());
+ assert(inst_map_[id] == nullptr);
+
+ inst_map_[id] = inst;
+}
+
+void IdInstructions::MapIdsToInstruction(
+ opt::IteratorRange<opt::Module::const_inst_iterator> section) {
+ for (const opt::Instruction& inst : section) {
+ uint32_t result_id = inst.result_id();
+ if (result_id == 0) {
+ continue;
+ }
+
+ MapIdToInstruction(result_id, &inst);
+ }
+}
+
+void IdInstructions::MapIdsToInfos(
+ opt::IteratorRange<opt::Module::const_inst_iterator> section) {
+ for (const opt::Instruction& inst : section) {
+ IdToInfoMap* info_map = nullptr;
+ uint32_t id_operand = 0;
+
+ switch (inst.opcode()) {
+ case SpvOpName:
+ info_map = &name_map_;
+ break;
+ case SpvOpMemberName:
+ info_map = &name_map_;
+ break;
+ case SpvOpDecorate:
+ info_map = &decoration_map_;
+ break;
+ case SpvOpMemberDecorate:
+ info_map = &decoration_map_;
+ break;
+ case SpvOpTypeForwardPointer: {
+ uint32_t id = inst.GetSingleWordOperand(0);
+ assert(id != 0);
+
+ assert(id < forward_pointer_map_.size());
+ forward_pointer_map_[id] = &inst;
+ continue;
+ }
+ default:
+ // Currently unsupported instruction, don't attempt to use it for
+ // matching.
+ break;
+ }
+
+ if (info_map == nullptr) {
+ continue;
+ }
+
+ uint32_t id = inst.GetOperand(id_operand).AsId();
+ assert(id != 0);
+
+ assert(id < info_map->size());
+ assert(std::find((*info_map)[id].begin(), (*info_map)[id].end(), &inst) ==
+ (*info_map)[id].end());
+
+ (*info_map)[id].push_back(&inst);
+ }
+}
+
+void Differ::PoolPotentialIds(
+ opt::IteratorRange<opt::Module::const_inst_iterator> section,
+ std::vector<uint32_t>& ids, bool is_src,
+ std::function<bool(const opt::Instruction&)> filter,
+ std::function<uint32_t(const opt::Instruction&)> get_id) {
+ for (const opt::Instruction& inst : section) {
+ if (!filter(inst)) {
+ continue;
+ }
+
+ uint32_t result_id = get_id(inst);
+ assert(result_id != 0);
+
+ assert(std::find(ids.begin(), ids.end(), result_id) == ids.end());
+
+ // Don't include ids that are already matched, for example through
+ // OpTypeForwardPointer.
+ const bool is_matched = is_src ? id_map_.IsSrcMapped(result_id)
+ : id_map_.IsDstMapped(result_id);
+ if (is_matched) {
+ continue;
+ }
+
+ ids.push_back(result_id);
+ }
+}
+
+void Differ::MatchIds(
+ PotentialIdMap& potential,
+ std::function<bool(const opt::Instruction*, const opt::Instruction*)>
+ match) {
+ for (size_t src_index = 0; src_index < potential.src_ids.size();
+ ++src_index) {
+ for (size_t dst_index = 0; dst_index < potential.dst_ids.size();
+ ++dst_index) {
+ const uint32_t src_id = potential.src_ids[src_index];
+ const uint32_t dst_id = potential.dst_ids[dst_index];
+
+ if (dst_id == 0) {
+ // Already matched.
+ continue;
+ }
+
+ const opt::Instruction* src_inst = src_id_to_.inst_map_[src_id];
+ const opt::Instruction* dst_inst = dst_id_to_.inst_map_[dst_id];
+
+ if (match(src_inst, dst_inst)) {
+ id_map_.MapIds(src_id, dst_id);
+
+ // Remove the ids from the potential list.
+ potential.src_ids[src_index] = 0;
+ potential.dst_ids[dst_index] = 0;
+
+ // Find a match for the next src id.
+ break;
+ }
+ }
+ }
+
+ // Remove matched ids to make the next iteration faster.
+ CompactIds(potential.src_ids);
+ CompactIds(potential.dst_ids);
+}
+
+void Differ::MatchPreambleInstructions(
+ opt::IteratorRange<opt::Module::const_inst_iterator> src_insts,
+ opt::IteratorRange<opt::Module::const_inst_iterator> dst_insts) {
+ // First, pool all instructions from each section and sort them.
+ InstructionList sorted_src_insts = SortPreambleInstructions(src_, src_insts);
+ InstructionList sorted_dst_insts = SortPreambleInstructions(dst_, dst_insts);
+
+ // Then walk and match them.
+ size_t src_cur = 0;
+ size_t dst_cur = 0;
+
+ while (src_cur < sorted_src_insts.size() &&
+ dst_cur < sorted_dst_insts.size()) {
+ const opt::Instruction* src_inst = sorted_src_insts[src_cur];
+ const opt::Instruction* dst_inst = sorted_dst_insts[dst_cur];
+
+ int compare = ComparePreambleInstructions(src_inst, dst_inst, src_, dst_);
+ if (compare == 0) {
+ id_map_.MapInsts(src_inst, dst_inst);
+ }
+ if (compare <= 0) {
+ ++src_cur;
+ }
+ if (compare >= 0) {
+ ++dst_cur;
+ }
+ }
+}
+
+InstructionList Differ::SortPreambleInstructions(
+ const opt::Module* module,
+ opt::IteratorRange<opt::Module::const_inst_iterator> insts) {
+ InstructionList sorted;
+ for (const opt::Instruction& inst : insts) {
+ sorted.push_back(&inst);
+ }
+ std::sort(
+ sorted.begin(), sorted.end(),
+ [this, module](const opt::Instruction* a, const opt::Instruction* b) {
+ return ComparePreambleInstructions(a, b, module, module) < 0;
+ });
+ return sorted;
+}
+
+int Differ::ComparePreambleInstructions(const opt::Instruction* a,
+ const opt::Instruction* b,
+ const opt::Module* src_inst_module,
+ const opt::Module* dst_inst_module) {
+ assert(a->opcode() == b->opcode());
+ assert(!a->HasResultId());
+ assert(!a->HasResultType());
+
+ const uint32_t a_operand_count = a->NumOperands();
+ const uint32_t b_operand_count = b->NumOperands();
+
+ if (a_operand_count < b_operand_count) {
+ return -1;
+ }
+ if (a_operand_count > b_operand_count) {
+ return 1;
+ }
+
+ // Instead of comparing OpExecutionMode entry point ids as ids, compare them
+ // through their corresponding execution model. This simplifies traversing
+ // the sorted list of instructions between src and dst modules.
+ if (a->opcode() == SpvOpExecutionMode) {
+ const SpvExecutionModel src_model =
+ GetExecutionModel(src_inst_module, a->GetSingleWordOperand(0));
+ const SpvExecutionModel dst_model =
+ GetExecutionModel(dst_inst_module, b->GetSingleWordOperand(0));
+
+ if (src_model < dst_model) {
+ return -1;
+ }
+ if (src_model > dst_model) {
+ return 1;
+ }
+ }
+
+ // Match every operand of the instruction.
+ for (uint32_t operand_index = 0; operand_index < a_operand_count;
+ ++operand_index) {
+ const opt::Operand& a_operand = a->GetOperand(operand_index);
+ const opt::Operand& b_operand = b->GetOperand(operand_index);
+
+ if (a_operand.type < b_operand.type) {
+ return -1;
+ }
+ if (a_operand.type > b_operand.type) {
+ return 1;
+ }
+
+ switch (a_operand.type) {
+ case SPV_OPERAND_TYPE_ID:
+ // Don't compare ids, there can't be multiple instances of the
+ // OpExecutionMode with different ids of the same execution model.
+ break;
+ case SPV_OPERAND_TYPE_TYPE_ID:
+ case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
+ case SPV_OPERAND_TYPE_SCOPE_ID:
+ assert(false && "Unreachable");
+ break;
+ case SPV_OPERAND_TYPE_LITERAL_STRING: {
+ int str_compare =
+ strcmp(a_operand.AsString().c_str(), b_operand.AsString().c_str());
+ if (str_compare != 0) {
+ return str_compare;
+ }
+ break;
+ }
+ default:
+ // Expect literal values to match.
+ assert(a_operand.words.size() == 1);
+ assert(b_operand.words.size() == 1);
+
+ if (a_operand.words[0] < b_operand.words[0]) {
+ return -1;
+ }
+ if (a_operand.words[0] > b_operand.words[0]) {
+ return 1;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void Differ::MatchDebugAndAnnotationInstructions(
+ opt::IteratorRange<opt::Module::const_inst_iterator> src_insts,
+ opt::IteratorRange<opt::Module::const_inst_iterator> dst_insts) {
+ for (const opt::Instruction& src_inst : src_insts) {
+ for (const opt::Instruction& dst_inst : dst_insts) {
+ if (MappedSrcInst(&dst_inst) != nullptr) {
+ continue;
+ }
+
+ // Map instructions as soon as they match. Debug and annotation
+ // instructions are matched such that there can't be multiple matches.
+ if (DoDebugAndAnnotationInstructionsMatch(&src_inst, &dst_inst)) {
+ id_map_.MapInsts(&src_inst, &dst_inst);
+ break;
+ }
+ }
+ }
+}
+
+uint32_t Differ::GroupIdsHelperGetTypeId(const IdInstructions& id_to,
+ uint32_t id) {
+ return GetInst(id_to, id)->type_id();
+}
+
+SpvStorageClass Differ::GroupIdsHelperGetTypePointerStorageClass(
+ const IdInstructions& id_to, uint32_t id) {
+ const opt::Instruction* inst = GetInst(id_to, id);
+ assert(inst && inst->opcode() == SpvOpTypePointer);
+ return SpvStorageClass(inst->GetSingleWordInOperand(0));
+}
+
+SpvOp Differ::GroupIdsHelperGetTypePointerTypeOp(const IdInstructions& id_to,
+ uint32_t id) {
+ const opt::Instruction* inst = GetInst(id_to, id);
+ assert(inst && inst->opcode() == SpvOpTypePointer);
+
+ const uint32_t type_id = inst->GetSingleWordInOperand(1);
+ const opt::Instruction* type_inst = GetInst(id_to, type_id);
+ assert(type_inst);
+
+ return type_inst->opcode();
+}
+
+template <typename T>
+void Differ::GroupIds(const IdGroup& ids, bool is_src,
+ std::map<T, IdGroup>* groups,
+ T (Differ::*get_group)(const IdInstructions&, uint32_t)) {
+ assert(groups->empty());
+
+ const IdInstructions& id_to = is_src ? src_id_to_ : dst_id_to_;
+
+ for (const uint32_t id : ids) {
+ // Don't include ids that are already matched, for example through
+ // OpEntryPoint.
+ const bool is_matched =
+ is_src ? id_map_.IsSrcMapped(id) : id_map_.IsDstMapped(id);
+ if (is_matched) {
+ continue;
+ }
+
+ T group = (this->*get_group)(id_to, id);
+ (*groups)[group].push_back(id);
+ }
+}
+
+template <typename T>
+void Differ::GroupIdsAndMatch(
+ const IdGroup& src_ids, const IdGroup& dst_ids, T invalid_group_key,
+ T (Differ::*get_group)(const IdInstructions&, uint32_t),
+ std::function<void(const IdGroup& src_group, const IdGroup& dst_group)>
+ match_group) {
+ // Group the ids based on a key (get_group)
+ std::map<T, IdGroup> src_groups;
+ std::map<T, IdGroup> dst_groups;
+
+ GroupIds<T>(src_ids, true, &src_groups, get_group);
+ GroupIds<T>(dst_ids, false, &dst_groups, get_group);
+
+ // Iterate over the groups, and match those with identical keys
+ for (const auto& iter : src_groups) {
+ const T& key = iter.first;
+ const IdGroup& src_group = iter.second;
+
+ if (key == invalid_group_key) {
+ continue;
+ }
+
+ const IdGroup& dst_group = dst_groups[key];
+
+ // Let the caller match the groups as appropriate.
+ match_group(src_group, dst_group);
+ }
+}
+
+bool Differ::DoIdsMatch(uint32_t src_id, uint32_t dst_id) {
+ assert(dst_id != 0);
+ return id_map_.MappedDstId(src_id) == dst_id;
+}
+
+bool Differ::DoesOperandMatch(const opt::Operand& src_operand,
+ const opt::Operand& dst_operand) {
+ assert(src_operand.type == dst_operand.type);
+
+ switch (src_operand.type) {
+ case SPV_OPERAND_TYPE_ID:
+ case SPV_OPERAND_TYPE_TYPE_ID:
+ case SPV_OPERAND_TYPE_RESULT_ID:
+ case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
+ case SPV_OPERAND_TYPE_SCOPE_ID:
+ // Match ids only if they are already matched in the id map.
+ return DoIdsMatch(src_operand.AsId(), dst_operand.AsId());
+ case SPV_OPERAND_TYPE_LITERAL_STRING:
+ return src_operand.AsString() == dst_operand.AsString();
+ default:
+ // Otherwise expect them to match exactly.
+ assert(src_operand.type != SPV_OPERAND_TYPE_LITERAL_STRING);
+ if (src_operand.words.size() != dst_operand.words.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < src_operand.words.size(); ++i) {
+ if (src_operand.words[i] != dst_operand.words[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+bool Differ::DoOperandsMatch(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst,
+ uint32_t in_operand_index_start,
+ uint32_t in_operand_count) {
+ // Caller should have returned early for instructions with different opcode.
+ assert(src_inst->opcode() == dst_inst->opcode());
+
+ bool match = true;
+ for (uint32_t i = 0; i < in_operand_count; ++i) {
+ const uint32_t in_operand_index = in_operand_index_start + i;
+
+ const opt::Operand& src_operand = src_inst->GetInOperand(in_operand_index);
+ const opt::Operand& dst_operand = dst_inst->GetInOperand(in_operand_index);
+
+ match = match && DoesOperandMatch(src_operand, dst_operand);
+ }
+
+ return match;
+}
+
+bool Differ::DoInstructionsMatch(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst) {
+ // Check whether the two instructions are identical, that is the instructions
+ // themselves are matched, every id is matched, and every other value is
+ // identical.
+ if (MappedDstInst(src_inst) != dst_inst) {
+ return false;
+ }
+
+ assert(src_inst->opcode() == dst_inst->opcode());
+ if (src_inst->NumOperands() != dst_inst->NumOperands()) {
+ return false;
+ }
+
+ for (uint32_t operand_index = 0; operand_index < src_inst->NumOperands();
+ ++operand_index) {
+ const opt::Operand& src_operand = src_inst->GetOperand(operand_index);
+ const opt::Operand& dst_operand = dst_inst->GetOperand(operand_index);
+
+ if (!DoesOperandMatch(src_operand, dst_operand)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Differ::DoIdsMatchFuzzy(uint32_t src_id, uint32_t dst_id) {
+ assert(dst_id != 0);
+ const uint32_t mapped_dst_id = id_map_.MappedDstId(src_id);
+
+ // Consider unmatched ids as a match. In function bodies, no result id is
+ // matched yet and thus they are excluded from instruction matching when used
+ // as parameters in subsequent instructions.
+ if (mapped_dst_id == 0 || mapped_dst_id == dst_id) {
+ return true;
+ }
+
+ // Int and Uint constants are interchangeable, match them in that case.
+ if (AreIdenticalUintConstants(src_id, dst_id)) {
+ return true;
+ }
+
+ return false;
+}
+
+bool Differ::DoesOperandMatchFuzzy(const opt::Operand& src_operand,
+ const opt::Operand& dst_operand) {
+ if (src_operand.type != dst_operand.type) {
+ return false;
+ }
+
+ assert(src_operand.type != SPV_OPERAND_TYPE_RESULT_ID);
+ assert(dst_operand.type != SPV_OPERAND_TYPE_RESULT_ID);
+
+ switch (src_operand.type) {
+ case SPV_OPERAND_TYPE_ID:
+ case SPV_OPERAND_TYPE_TYPE_ID:
+ case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
+ case SPV_OPERAND_TYPE_SCOPE_ID:
+ // Match id operands only if they are already matched in the id map.
+ return DoIdsMatchFuzzy(src_operand.AsId(), dst_operand.AsId());
+ default:
+ // Otherwise allow everything to match.
+ return true;
+ }
+}
+
+bool Differ::DoInstructionsMatchFuzzy(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst) {
+ // Similar to DoOperandsMatch, but only checks that ids that have already been
+ // matched are identical. Ids that are unknown are allowed to match, as well
+ // as any non-id operand.
+ if (src_inst->opcode() != dst_inst->opcode()) {
+ return false;
+ }
+ // For external instructions, make sure the set and opcode of the external
+ // instruction matches too.
+ if (src_inst->opcode() == SpvOpExtInst) {
+ if (!DoOperandsMatch(src_inst, dst_inst, 0, 2)) {
+ return false;
+ }
+ }
+
+ assert(src_inst->HasResultType() == dst_inst->HasResultType());
+ if (src_inst->HasResultType() &&
+ !DoIdsMatchFuzzy(src_inst->type_id(), dst_inst->type_id())) {
+ return false;
+ }
+
+ // TODO: allow some instructions to match with different instruction lengths,
+ // for example OpImage* with additional operands.
+ if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) {
+ return false;
+ }
+
+ bool match = true;
+ for (uint32_t in_operand_index = 0;
+ in_operand_index < src_inst->NumInOperandWords(); ++in_operand_index) {
+ const opt::Operand& src_operand = src_inst->GetInOperand(in_operand_index);
+ const opt::Operand& dst_operand = dst_inst->GetInOperand(in_operand_index);
+
+ match = match && DoesOperandMatchFuzzy(src_operand, dst_operand);
+ }
+
+ return match;
+}
+
+bool Differ::AreIdenticalUintConstants(uint32_t src_id, uint32_t dst_id) {
+ return IsConstantUint(src_id_to_, src_id) &&
+ IsConstantUint(dst_id_to_, dst_id) &&
+ GetConstantUint(src_id_to_, src_id) ==
+ GetConstantUint(dst_id_to_, dst_id);
+}
+
+bool Differ::DoDebugAndAnnotationInstructionsMatch(
+ const opt::Instruction* src_inst, const opt::Instruction* dst_inst) {
+ if (src_inst->opcode() != dst_inst->opcode()) {
+ return false;
+ }
+
+ switch (src_inst->opcode()) {
+ case SpvOpString:
+ case SpvOpSourceExtension:
+ case SpvOpModuleProcessed:
+ return DoesOperandMatch(src_inst->GetOperand(0), dst_inst->GetOperand(0));
+ case SpvOpSource:
+ return DoOperandsMatch(src_inst, dst_inst, 0, 2);
+ case SpvOpSourceContinued:
+ return true;
+ case SpvOpName:
+ return DoOperandsMatch(src_inst, dst_inst, 0, 1);
+ case SpvOpMemberName:
+ return DoOperandsMatch(src_inst, dst_inst, 0, 2);
+ case SpvOpDecorate:
+ return DoOperandsMatch(src_inst, dst_inst, 0, 2);
+ case SpvOpMemberDecorate:
+ return DoOperandsMatch(src_inst, dst_inst, 0, 3);
+ case SpvOpExtInst:
+ case SpvOpDecorationGroup:
+ case SpvOpGroupDecorate:
+ case SpvOpGroupMemberDecorate:
+ return false;
+ default:
+ return false;
+ }
+}
+
+bool Differ::AreVariablesMatchable(uint32_t src_id, uint32_t dst_id,
+ uint32_t flexibility) {
+ // Variables must match by their built-in decorations.
+ uint32_t src_built_in_decoration = 0, dst_built_in_decoration = 0;
+ const bool src_is_built_in = GetDecorationValue(
+ src_id_to_, src_id, SpvDecorationBuiltIn, &src_built_in_decoration);
+ const bool dst_is_built_in = GetDecorationValue(
+ dst_id_to_, dst_id, SpvDecorationBuiltIn, &dst_built_in_decoration);
+
+ if (src_is_built_in != dst_is_built_in) {
+ return false;
+ }
+ if (src_is_built_in && src_built_in_decoration != dst_built_in_decoration) {
+ return false;
+ }
+
+ // Check their types and storage classes.
+ SpvStorageClass src_storage_class, dst_storage_class;
+ const uint32_t src_type_id =
+ GetVarTypeId(src_id_to_, src_id, &src_storage_class);
+ const uint32_t dst_type_id =
+ GetVarTypeId(dst_id_to_, dst_id, &dst_storage_class);
+
+ if (!DoIdsMatch(src_type_id, dst_type_id)) {
+ return false;
+ }
+ switch (flexibility) {
+ case 0:
+ if (src_storage_class != dst_storage_class) {
+ return false;
+ }
+ break;
+ case 1:
+ if (src_storage_class != dst_storage_class) {
+ // Allow one of the two to be Private while the other is Input or
+ // Output, this allows matching in/out variables that have been turned
+ // global as part of linking two stages (as done in ANGLE).
+ const bool src_is_io = src_storage_class == SpvStorageClassInput ||
+ src_storage_class == SpvStorageClassOutput;
+ const bool dst_is_io = dst_storage_class == SpvStorageClassInput ||
+ dst_storage_class == SpvStorageClassOutput;
+ const bool src_is_private = src_storage_class == SpvStorageClassPrivate;
+ const bool dst_is_private = dst_storage_class == SpvStorageClassPrivate;
+
+ if (!((src_is_io && dst_is_private) || (src_is_private && dst_is_io))) {
+ return false;
+ }
+ }
+ break;
+ default:
+ assert(false && "Unreachable");
+ return false;
+ }
+
+ // TODO: Is there any other way to check compatiblity of the variables? It's
+ // easy to tell when the variables definitely don't match, but there's little
+ // information that can be used for a definite match.
+ return true;
+}
+
+bool Differ::MatchOpTypeStruct(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst,
+ uint32_t flexibility) {
+ const uint32_t src_type_id = src_inst->result_id();
+ const uint32_t dst_type_id = dst_inst->result_id();
+
+ bool src_has_name = false, dst_has_name = false;
+ std::string src_name = GetName(src_id_to_, src_type_id, &src_has_name);
+ std::string dst_name = GetName(dst_id_to_, dst_type_id, &dst_has_name);
+
+ // If debug info is present, always match the structs by name.
+ if (src_has_name && dst_has_name) {
+ if (src_name != dst_name) {
+ return false;
+ }
+
+ // For gl_PerVertex, find the type pointer of this type (array) and make
+ // sure the storage classes of src and dst match; geometry and tessellation
+ // shaders have two instances of gl_PerVertex.
+ if (src_name == "gl_PerVertex") {
+ return MatchPerVertexType(src_type_id, dst_type_id);
+ }
+
+ return true;
+ }
+
+ // If debug info is not present, match the structs by their type.
+
+ // For gl_PerVertex, find the type pointer of this type (array) and match by
+ // storage class. The gl_PerVertex struct is itself found by the BuiltIn
+ // decorations applied to its members.
+ const bool src_is_per_vertex = IsPerVertexType(src_id_to_, src_type_id);
+ const bool dst_is_per_vertex = IsPerVertexType(dst_id_to_, dst_type_id);
+ if (src_is_per_vertex != dst_is_per_vertex) {
+ return false;
+ }
+
+ if (src_is_per_vertex) {
+ return MatchPerVertexType(src_type_id, dst_type_id);
+ }
+
+ switch (flexibility) {
+ case 0:
+ if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) {
+ return false;
+ }
+ return DoOperandsMatch(src_inst, dst_inst, 0,
+ src_inst->NumInOperandWords());
+ case 1:
+ // TODO: match by taking a diff of the fields, and see if there's a >75%
+ // match. Need to then make sure OpMemberName, OpMemberDecorate,
+ // OpAccessChain etc are aware of the struct field matching.
+ return false;
+ default:
+ assert(false && "Unreachable");
+ return false;
+ }
+}
+
+bool Differ::MatchOpConstant(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst,
+ uint32_t flexibility) {
+ // The constants' type must match. In flexibility == 1, match constants of
+ // int and uint, as they are generally interchangeable.
+ switch (flexibility) {
+ case 0:
+ if (!DoesOperandMatch(src_inst->GetOperand(0), dst_inst->GetOperand(0))) {
+ return false;
+ }
+ break;
+ case 1:
+ if (!IsIntType(src_id_to_, src_inst->type_id()) ||
+ !IsIntType(dst_id_to_, dst_inst->type_id())) {
+ return false;
+ }
+ break;
+ default:
+ assert(false && "Unreachable");
+ return false;
+ }
+
+ const opt::Operand& src_value_operand = src_inst->GetOperand(2);
+ const opt::Operand& dst_value_operand = dst_inst->GetOperand(2);
+
+ const uint64_t src_value = src_value_operand.AsLiteralUint64();
+ const uint64_t dst_value = dst_value_operand.AsLiteralUint64();
+
+ // If values are identical, it's a match.
+ if (src_value == dst_value) {
+ return true;
+ }
+
+ // Otherwise, only allow flexibility for float types.
+ if (IsFloatType(src_id_to_, src_inst->type_id()) && flexibility == 1) {
+ // Tolerance is:
+ //
+ // - For float: allow 4 bits of mantissa as error
+ // - For double: allow 6 bits of mantissa as error
+ //
+ // TODO: the above values are arbitrary and a placeholder; investigate the
+ // amount of error resulting from using `printf("%f", f)` and `printf("%lf",
+ // d)` and having glslang parse them.
+ const uint64_t tolerance = src_value_operand.words.size() == 1 ? 16 : 64;
+ return src_value - dst_value < tolerance ||
+ dst_value - src_value < tolerance;
+ }
+
+ return false;
+}
+
+bool Differ::MatchOpSpecConstant(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst) {
+ const uint32_t src_id = src_inst->result_id();
+ const uint32_t dst_id = dst_inst->result_id();
+
+ bool src_has_name = false, dst_has_name = false;
+ std::string src_name = GetName(src_id_to_, src_id, &src_has_name);
+ std::string dst_name = GetName(dst_id_to_, dst_id, &dst_has_name);
+
+ // If debug info is present, always match the spec consts by name.
+ if (src_has_name && dst_has_name) {
+ return src_name == dst_name;
+ }
+
+ // Otherwise, match them by SpecId.
+ uint32_t src_spec_id, dst_spec_id;
+
+ if (GetDecorationValue(src_id_to_, src_id, SpvDecorationSpecId,
+ &src_spec_id) &&
+ GetDecorationValue(dst_id_to_, dst_id, SpvDecorationSpecId,
+ &dst_spec_id)) {
+ return src_spec_id == dst_spec_id;
+ }
+
+ // There is no SpecId decoration, while not practical, still valid.
+ // SpecConstantOp don't have SpecId and can be matched by operands
+ if (src_inst->opcode() == SpvOpSpecConstantOp) {
+ if (src_inst->NumInOperandWords() == dst_inst->NumInOperandWords()) {
+ return DoOperandsMatch(src_inst, dst_inst, 0,
+ src_inst->NumInOperandWords());
+ }
+ }
+
+ return false;
+}
+
+bool Differ::MatchOpVariable(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst,
+ uint32_t flexibility) {
+ const uint32_t src_id = src_inst->result_id();
+ const uint32_t dst_id = dst_inst->result_id();
+
+ const bool src_is_pervertex = IsPerVertexVariable(src_id_to_, src_id);
+ const bool dst_is_pervertex = IsPerVertexVariable(dst_id_to_, dst_id);
+
+ // For gl_PerVertex, make sure the input and output instances are matched
+ // correctly.
+ if (src_is_pervertex != dst_is_pervertex) {
+ return false;
+ }
+ if (src_is_pervertex) {
+ return MatchPerVertexVariable(src_inst, dst_inst);
+ }
+
+ bool src_has_name = false, dst_has_name = false;
+ std::string src_name = GetName(src_id_to_, src_id, &src_has_name);
+ std::string dst_name = GetName(dst_id_to_, dst_id, &dst_has_name);
+
+ // If debug info is present, always match the variables by name.
+ if (src_has_name && dst_has_name) {
+ return src_name == dst_name;
+ }
+
+ // If debug info is not present, see if the variables can be matched by their
+ // built-in decorations.
+ uint32_t src_built_in_decoration;
+ const bool src_is_built_in = GetDecorationValue(
+ src_id_to_, src_id, SpvDecorationBuiltIn, &src_built_in_decoration);
+
+ if (src_is_built_in && AreVariablesMatchable(src_id, dst_id, flexibility)) {
+ return true;
+ }
+
+ SpvStorageClass src_storage_class, dst_storage_class;
+ GetVarTypeId(src_id_to_, src_id, &src_storage_class);
+ GetVarTypeId(dst_id_to_, dst_id, &dst_storage_class);
+
+ if (src_storage_class != dst_storage_class) {
+ return false;
+ }
+
+ // If variables are decorated with set/binding, match by the value of those
+ // decorations.
+ if (!options_.ignore_set_binding) {
+ uint32_t src_set = 0, dst_set = 0;
+ uint32_t src_binding = 0, dst_binding = 0;
+
+ const bool src_has_set = GetDecorationValue(
+ src_id_to_, src_id, SpvDecorationDescriptorSet, &src_set);
+ const bool dst_has_set = GetDecorationValue(
+ dst_id_to_, dst_id, SpvDecorationDescriptorSet, &dst_set);
+ const bool src_has_binding =
+ GetDecorationValue(src_id_to_, src_id, SpvDecorationBinding, &src_set);
+ const bool dst_has_binding =
+ GetDecorationValue(dst_id_to_, dst_id, SpvDecorationBinding, &dst_set);
+
+ if (src_has_set && dst_has_set && src_has_binding && dst_has_binding) {
+ return src_set == dst_set && src_binding == dst_binding;
+ }
+ }
+
+ // If variables are decorated with location, match by the value of that
+ // decoration.
+ if (!options_.ignore_location) {
+ uint32_t src_location, dst_location;
+
+ const bool src_has_location = GetDecorationValue(
+ src_id_to_, src_id, SpvDecorationLocation, &src_location);
+ const bool dst_has_location = GetDecorationValue(
+ dst_id_to_, dst_id, SpvDecorationLocation, &dst_location);
+
+ if (src_has_location && dst_has_location) {
+ return src_location == dst_location;
+ }
+ }
+
+ // Currently, there's no other way to match variables.
+ return false;
+}
+
+bool Differ::MatchPerVertexType(uint32_t src_type_id, uint32_t dst_type_id) {
+ // For gl_PerVertex, find the type pointer of this type (array) and make sure
+ // the storage classes of src and dst match; geometry and tessellation shaders
+ // have two instances of gl_PerVertex.
+ SpvStorageClass src_storage_class =
+ GetPerVertexStorageClass(src_, src_type_id);
+ SpvStorageClass dst_storage_class =
+ GetPerVertexStorageClass(dst_, dst_type_id);
+
+ assert(src_storage_class == SpvStorageClassInput ||
+ src_storage_class == SpvStorageClassOutput);
+ assert(dst_storage_class == SpvStorageClassInput ||
+ dst_storage_class == SpvStorageClassOutput);
+
+ return src_storage_class == dst_storage_class;
+}
+
+bool Differ::MatchPerVertexVariable(const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst) {
+ SpvStorageClass src_storage_class =
+ SpvStorageClass(src_inst->GetSingleWordInOperand(0));
+ SpvStorageClass dst_storage_class =
+ SpvStorageClass(dst_inst->GetSingleWordInOperand(0));
+
+ return src_storage_class == dst_storage_class;
+}
+
+void Differ::MatchTypeForwardPointersByName(const IdGroup& src,
+ const IdGroup& dst) {
+ // Given two sets of compatible groups of OpTypeForwardPointer instructions,
+ // attempts to match them by name.
+
+ // Group them by debug info and loop over them.
+ GroupIdsAndMatch<std::string>(
+ src, dst, "", &Differ::GetSanitizedName,
+ [this](const IdGroup& src_group, const IdGroup& dst_group) {
+
+ // Match only if there's a unique forward declaration with this debug
+ // name.
+ if (src_group.size() == 1 && dst_group.size() == 1) {
+ id_map_.MapIds(src_group[0], dst_group[0]);
+ }
+ });
+}
+
+void Differ::MatchTypeForwardPointersByTypeOp(const IdGroup& src,
+ const IdGroup& dst) {
+ // Given two sets of compatible groups of OpTypeForwardPointer instructions,
+ // attempts to match them by type op. Must be called after
+ // MatchTypeForwardPointersByName to match as many as possible by debug info.
+
+ // Remove ids that are matched with debug info in
+ // MatchTypeForwardPointersByName.
+ IdGroup src_unmatched_ids;
+ IdGroup dst_unmatched_ids;
+
+ std::copy_if(src.begin(), src.end(), std::back_inserter(src_unmatched_ids),
+ [this](uint32_t id) { return !id_map_.IsSrcMapped(id); });
+ std::copy_if(dst.begin(), dst.end(), std::back_inserter(dst_unmatched_ids),
+ [this](uint32_t id) { return !id_map_.IsDstMapped(id); });
+
+ // Match only if there's a unique forward declaration with this
+ // storage class and type opcode. If both have debug info, they
+ // must not have been matchable.
+ if (src_unmatched_ids.size() == 1 && dst_unmatched_ids.size() == 1) {
+ uint32_t src_id = src_unmatched_ids[0];
+ uint32_t dst_id = dst_unmatched_ids[0];
+ if (!HasName(src_id_to_, src_id) || !HasName(dst_id_to_, dst_id)) {
+ id_map_.MapIds(src_id, dst_id);
+ }
+ }
+}
+
+InstructionList Differ::GetFunctionBody(opt::IRContext* context,
+ opt::Function& function) {
+ // Canonicalize the blocks of the function to produce better diff, for example
+ // to not produce any diff if the src and dst have the same switch/case blocks
+ // but with the cases simply reordered.
+ std::list<opt::BasicBlock*> order;
+ context->cfg()->ComputeStructuredOrder(&function, &*function.begin(), &order);
+
+ // Go over the instructions of the function and add the instructions to a flat
+ // list to simplify future iterations.
+ InstructionList body;
+ for (opt::BasicBlock* block : order) {
+ block->ForEachInst(
+ [&body](const opt::Instruction* inst) { body.push_back(inst); }, true);
+ }
+ body.push_back(function.EndInst());
+
+ return body;
+}
+
+InstructionList Differ::GetFunctionHeader(const opt::Function& function) {
+ // Go over the instructions of the function and add the header instructions to
+ // a flat list to simplify diff generation.
+ InstructionList body;
+ function.WhileEachInst(
+ [&body](const opt::Instruction* inst) {
+ if (inst->opcode() == SpvOpLabel) {
+ return false;
+ }
+ body.push_back(inst);
+ return true;
+ },
+ true, true);
+
+ return body;
+}
+
+void Differ::GetFunctionBodies(opt::IRContext* context, FunctionMap* functions,
+ FunctionInstMap* function_insts) {
+ for (opt::Function& function : *context->module()) {
+ uint32_t id = function.result_id();
+ assert(functions->find(id) == functions->end());
+ assert(function_insts->find(id) == function_insts->end());
+
+ (*functions)[id] = &function;
+
+ InstructionList body = GetFunctionBody(context, function);
+ (*function_insts)[id] = std::move(body);
+ }
+}
+
+void Differ::GetFunctionHeaderInstructions(const opt::Module* module,
+ FunctionInstMap* function_insts) {
+ for (opt::Function& function : *module) {
+ InstructionList body = GetFunctionHeader(function);
+ (*function_insts)[function.result_id()] = std::move(body);
+ }
+}
+
+void Differ::BestEffortMatchFunctions(const IdGroup& src_func_ids,
+ const IdGroup& dst_func_ids,
+ const FunctionInstMap& src_func_insts,
+ const FunctionInstMap& dst_func_insts) {
+ struct MatchResult {
+ uint32_t src_id;
+ uint32_t dst_id;
+ DiffMatch src_match;
+ DiffMatch dst_match;
+ float match_rate;
+ bool operator<(const MatchResult& other) const {
+ return match_rate > other.match_rate;
+ }
+ };
+ std::vector<MatchResult> all_match_results;
+
+ for (const uint32_t src_func_id : src_func_ids) {
+ if (id_map_.IsSrcMapped(src_func_id)) {
+ continue;
+ }
+ const std::string src_name = GetSanitizedName(src_id_to_, src_func_id);
+
+ for (const uint32_t dst_func_id : dst_func_ids) {
+ if (id_map_.IsDstMapped(dst_func_id)) {
+ continue;
+ }
+
+ // Don't match functions that are named, but the names are different.
+ const std::string dst_name = GetSanitizedName(dst_id_to_, dst_func_id);
+ if (src_name != "" && dst_name != "" && src_name != dst_name) {
+ continue;
+ }
+
+ DiffMatch src_match_result, dst_match_result;
+ float match_rate = MatchFunctionBodies(
+ src_func_insts.at(src_func_id), dst_func_insts.at(dst_func_id),
+ &src_match_result, &dst_match_result);
+
+ // Only consider the functions a match if there's at least 60% match.
+ // This is an arbitrary limit that should be tuned.
+ constexpr float pass_match_rate = 0.6f;
+ if (match_rate >= pass_match_rate) {
+ all_match_results.emplace_back(
+ MatchResult{src_func_id, dst_func_id, std::move(src_match_result),
+ std::move(dst_match_result), match_rate});
+ }
+ }
+ }
+
+ std::sort(all_match_results.begin(), all_match_results.end());
+
+ for (const MatchResult& match_result : all_match_results) {
+ if (id_map_.IsSrcMapped(match_result.src_id) ||
+ id_map_.IsDstMapped(match_result.dst_id)) {
+ continue;
+ }
+
+ id_map_.MapIds(match_result.src_id, match_result.dst_id);
+
+ MatchIdsInFunctionBodies(src_func_insts.at(match_result.src_id),
+ dst_func_insts.at(match_result.dst_id),
+ match_result.src_match, match_result.dst_match, 0);
+ }
+}
+
+void Differ::MatchFunctionParamIds(const opt::Function* src_func,
+ const opt::Function* dst_func) {
+ IdGroup src_params;
+ IdGroup dst_params;
+ src_func->ForEachParam(
+ [&src_params](const opt::Instruction* param) {
+ src_params.push_back(param->result_id());
+ },
+ false);
+ dst_func->ForEachParam(
+ [&dst_params](const opt::Instruction* param) {
+ dst_params.push_back(param->result_id());
+ },
+ false);
+
+ GroupIdsAndMatch<std::string>(
+ src_params, dst_params, "", &Differ::GetSanitizedName,
+ [this](const IdGroup& src_group, const IdGroup& dst_group) {
+
+ // There shouldn't be two parameters with the same name, so the ids
+ // should match. There is nothing restricting the SPIR-V however to have
+ // two parameters with the same name, so be resilient against that.
+ if (src_group.size() == 1 && dst_group.size() == 1) {
+ id_map_.MapIds(src_group[0], dst_group[0]);
+ }
+ });
+
+ // Then match the parameters by their type. If there are multiple of them,
+ // match them by their order.
+ GroupIdsAndMatch<uint32_t>(
+ src_params, dst_params, 0, &Differ::GroupIdsHelperGetTypeId,
+ [this](const IdGroup& src_group_by_type_id,
+ const IdGroup& dst_group_by_type_id) {
+
+ const size_t shared_param_count =
+ std::min(src_group_by_type_id.size(), dst_group_by_type_id.size());
+
+ for (size_t param_index = 0; param_index < shared_param_count;
+ ++param_index) {
+ id_map_.MapIds(src_group_by_type_id[0], dst_group_by_type_id[0]);
+ }
+ });
+}
+
+float Differ::MatchFunctionBodies(const InstructionList& src_body,
+ const InstructionList& dst_body,
+ DiffMatch* src_match_result,
+ DiffMatch* dst_match_result) {
+ LongestCommonSubsequence<std::vector<const opt::Instruction*>> lcs(src_body,
+ dst_body);
+
+ uint32_t best_match_length = lcs.Get<const opt::Instruction*>(
+ [this](const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst) {
+ return DoInstructionsMatchFuzzy(src_inst, dst_inst);
+ },
+ src_match_result, dst_match_result);
+
+ // TODO: take the gaps in between matches and match those again with a relaxed
+ // instruction-and-type-only comparison. This can produce a better diff for
+ // example if an array index is changed, causing the OpAccessChain id to not
+ // match and subsequently every operation that's derived from that id.
+ // Usually this mismatch cascades until the next OpStore which doesn't produce
+ // an id.
+
+ return static_cast<float>(best_match_length) * 2.0f /
+ static_cast<float>(src_body.size() + dst_body.size());
+}
+
+void Differ::MatchIdsInFunctionBodies(const InstructionList& src_body,
+ const InstructionList& dst_body,
+ const DiffMatch& src_match_result,
+ const DiffMatch& dst_match_result,
+ uint32_t flexibility) {
+ size_t src_cur = 0;
+ size_t dst_cur = 0;
+
+ while (src_cur < src_body.size() && dst_cur < dst_body.size()) {
+ if (src_match_result[src_cur] && dst_match_result[dst_cur]) {
+ // Match instructions the src and dst instructions.
+ //
+ // TODO: count the matchings between variables discovered this way and
+ // choose the "best match" after all functions have been diffed and all
+ // instructions analyzed.
+ const opt::Instruction* src_inst = src_body[src_cur++];
+ const opt::Instruction* dst_inst = dst_body[dst_cur++];
+
+ // Record the matching between the instructions. This is done only once
+ // (hence flexibility == 0). Calls with non-zero flexibility values will
+ // only deal with matching other ids based on the operands.
+ if (flexibility == 0) {
+ id_map_.MapInsts(src_inst, dst_inst);
+ }
+
+ // Match any unmatched variables referenced by the instructions.
+ MatchVariablesUsedByMatchedInstructions(src_inst, dst_inst, flexibility);
+ continue;
+ }
+ if (!src_match_result[src_cur]) {
+ ++src_cur;
+ }
+ if (!dst_match_result[dst_cur]) {
+ ++dst_cur;
+ }
+ }
+}
+
+void Differ::MatchVariablesUsedByMatchedInstructions(
+ const opt::Instruction* src_inst, const opt::Instruction* dst_inst,
+ uint32_t flexibility) {
+ // For OpAccessChain, OpLoad and OpStore instructions that reference unmatched
+ // variables, match them as a best effort.
+ assert(src_inst->opcode() == dst_inst->opcode());
+ switch (src_inst->opcode()) {
+ default:
+ // TODO: match functions based on OpFunctionCall?
+ break;
+ case SpvOpAccessChain:
+ case SpvOpInBoundsAccessChain:
+ case SpvOpPtrAccessChain:
+ case SpvOpInBoundsPtrAccessChain:
+ case SpvOpLoad:
+ case SpvOpStore:
+ const uint32_t src_pointer_id = src_inst->GetSingleWordInOperand(0);
+ const uint32_t dst_pointer_id = dst_inst->GetSingleWordInOperand(0);
+ if (IsVariable(src_id_to_, src_pointer_id) &&
+ IsVariable(dst_id_to_, dst_pointer_id) &&
+ !id_map_.IsSrcMapped(src_pointer_id) &&
+ !id_map_.IsDstMapped(dst_pointer_id) &&
+ AreVariablesMatchable(src_pointer_id, dst_pointer_id, flexibility)) {
+ id_map_.MapIds(src_pointer_id, dst_pointer_id);
+ }
+ break;
+ }
+}
+
+const opt::Instruction* Differ::GetInst(const IdInstructions& id_to,
+ uint32_t id) {
+ assert(id != 0);
+ assert(id < id_to.inst_map_.size());
+
+ const opt::Instruction* inst = id_to.inst_map_[id];
+ assert(inst != nullptr);
+
+ return inst;
+}
+
+uint32_t Differ::GetConstantUint(const IdInstructions& id_to,
+ uint32_t constant_id) {
+ const opt::Instruction* constant_inst = GetInst(id_to, constant_id);
+ assert(constant_inst->opcode() == SpvOpConstant);
+ assert(GetInst(id_to, constant_inst->type_id())->opcode() == SpvOpTypeInt);
+
+ return constant_inst->GetSingleWordInOperand(0);
+}
+
+SpvExecutionModel Differ::GetExecutionModel(const opt::Module* module,
+ uint32_t entry_point_id) {
+ for (const opt::Instruction& inst : module->entry_points()) {
+ assert(inst.opcode() == SpvOpEntryPoint);
+ if (inst.GetSingleWordOperand(1) == entry_point_id) {
+ return SpvExecutionModel(inst.GetSingleWordOperand(0));
+ }
+ }
+
+ assert(false && "Unreachable");
+ return SpvExecutionModel(0xFFF);
+}
+
+bool Differ::HasName(const IdInstructions& id_to, uint32_t id) {
+ assert(id != 0);
+ assert(id < id_to.name_map_.size());
+
+ for (const opt::Instruction* inst : id_to.name_map_[id]) {
+ if (inst->opcode() == SpvOpName) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+std::string Differ::GetName(const IdInstructions& id_to, uint32_t id,
+ bool* has_name) {
+ assert(id != 0);
+ assert(id < id_to.name_map_.size());
+
+ for (const opt::Instruction* inst : id_to.name_map_[id]) {
+ if (inst->opcode() == SpvOpName) {
+ *has_name = true;
+ return inst->GetOperand(1).AsString();
+ }
+ }
+
+ *has_name = false;
+ return "";
+}
+
+std::string Differ::GetSanitizedName(const IdInstructions& id_to, uint32_t id) {
+ bool has_name = false;
+ std::string name = GetName(id_to, id, &has_name);
+
+ if (!has_name) {
+ return "";
+ }
+
+ // Remove args from the name, in case this is a function name
+ return name.substr(0, name.find('('));
+}
+
+uint32_t Differ::GetVarTypeId(const IdInstructions& id_to, uint32_t var_id,
+ SpvStorageClass* storage_class) {
+ const opt::Instruction* var_inst = GetInst(id_to, var_id);
+ assert(var_inst->opcode() == SpvOpVariable);
+
+ *storage_class = SpvStorageClass(var_inst->GetSingleWordInOperand(0));
+
+ // Get the type pointer from the variable.
+ const uint32_t type_pointer_id = var_inst->type_id();
+ const opt::Instruction* type_pointer_inst = GetInst(id_to, type_pointer_id);
+
+ // Get the type from the type pointer.
+ return type_pointer_inst->GetSingleWordInOperand(1);
+}
+
+bool Differ::GetDecorationValue(const IdInstructions& id_to, uint32_t id,
+ SpvDecoration decoration,
+ uint32_t* decoration_value) {
+ assert(id != 0);
+ assert(id < id_to.decoration_map_.size());
+
+ for (const opt::Instruction* inst : id_to.decoration_map_[id]) {
+ if (inst->opcode() == SpvOpDecorate &&
+ inst->GetSingleWordOperand(0) == id &&
+ inst->GetSingleWordOperand(1) == decoration) {
+ *decoration_value = inst->GetSingleWordOperand(2);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+const opt::Instruction* Differ::GetForwardPointerInst(
+ const IdInstructions& id_to, uint32_t id) {
+ assert(id != 0);
+ assert(id < id_to.forward_pointer_map_.size());
+ return id_to.forward_pointer_map_[id];
+}
+
+bool Differ::IsIntType(const IdInstructions& id_to, uint32_t type_id) {
+ return IsOp(id_to, type_id, SpvOpTypeInt);
+}
+
+bool Differ::IsFloatType(const IdInstructions& id_to, uint32_t type_id) {
+ return IsOp(id_to, type_id, SpvOpTypeFloat);
+}
+
+bool Differ::IsConstantUint(const IdInstructions& id_to, uint32_t id) {
+ const opt::Instruction* constant_inst = GetInst(id_to, id);
+ if (constant_inst->opcode() != SpvOpConstant) {
+ return false;
+ }
+
+ const opt::Instruction* type_inst = GetInst(id_to, constant_inst->type_id());
+ return type_inst->opcode() == SpvOpTypeInt;
+}
+
+bool Differ::IsVariable(const IdInstructions& id_to, uint32_t pointer_id) {
+ return IsOp(id_to, pointer_id, SpvOpVariable);
+}
+
+bool Differ::IsOp(const IdInstructions& id_to, uint32_t id, SpvOp op) {
+ return GetInst(id_to, id)->opcode() == op;
+}
+
+bool Differ::IsPerVertexType(const IdInstructions& id_to, uint32_t type_id) {
+ assert(type_id != 0);
+ assert(type_id < id_to.decoration_map_.size());
+
+ for (const opt::Instruction* inst : id_to.decoration_map_[type_id]) {
+ if (inst->opcode() == SpvOpMemberDecorate &&
+ inst->GetSingleWordOperand(0) == type_id &&
+ inst->GetSingleWordOperand(2) == SpvDecorationBuiltIn) {
+ SpvBuiltIn built_in = SpvBuiltIn(inst->GetSingleWordOperand(3));
+
+ // Only gl_PerVertex can have, and it can only have, the following
+ // built-in decorations.
+ return built_in == SpvBuiltInPosition ||
+ built_in == SpvBuiltInPointSize ||
+ built_in == SpvBuiltInClipDistance ||
+ built_in == SpvBuiltInCullDistance;
+ }
+ }
+
+ return false;
+}
+
+bool Differ::IsPerVertexVariable(const IdInstructions& id_to, uint32_t var_id) {
+ // Get the type from the type pointer.
+ SpvStorageClass storage_class;
+ uint32_t type_id = GetVarTypeId(id_to, var_id, &storage_class);
+ const opt::Instruction* type_inst = GetInst(id_to, type_id);
+
+ // If array, get the element type.
+ if (type_inst->opcode() == SpvOpTypeArray) {
+ type_id = type_inst->GetSingleWordInOperand(0);
+ }
+
+ // Now check if the type is gl_PerVertex.
+ return IsPerVertexType(id_to, type_id);
+}
+
+SpvStorageClass Differ::GetPerVertexStorageClass(const opt::Module* module,
+ uint32_t type_id) {
+ for (const opt::Instruction& inst : module->types_values()) {
+ switch (inst.opcode()) {
+ case SpvOpTypeArray:
+ // The gl_PerVertex instance could be an array, look for a variable of
+ // the array type instead.
+ if (inst.GetSingleWordInOperand(0) == type_id) {
+ type_id = inst.result_id();
+ }
+ break;
+ case SpvOpTypePointer:
+ // Find the storage class of the pointer to this type.
+ if (inst.GetSingleWordInOperand(1) == type_id) {
+ return SpvStorageClass(inst.GetSingleWordInOperand(0));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // gl_PerVertex is declared, but is unused. Return either of Input or Output
+ // classes just so it matches one in the other module. This should be highly
+ // unlikely, perhaps except for ancient GS-used-to-emulate-CS scenarios.
+ return SpvStorageClassOutput;
+}
+
+spv_ext_inst_type_t Differ::GetExtInstType(const IdInstructions& id_to,
+ uint32_t set_id) {
+ const opt::Instruction* set_inst = GetInst(id_to, set_id);
+ return spvExtInstImportTypeGet(set_inst->GetInOperand(0).AsString().c_str());
+}
+
+spv_number_kind_t Differ::GetNumberKind(const IdInstructions& id_to,
+ const opt::Instruction& inst,
+ uint32_t operand_index,
+ uint32_t* number_bit_width) {
+ const opt::Operand& operand = inst.GetOperand(operand_index);
+ *number_bit_width = 0;
+
+ // A very limited version of Parser::parseOperand.
+ switch (operand.type) {
+ case SPV_OPERAND_TYPE_LITERAL_INTEGER:
+ case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER:
+ // Always unsigned integers.
+ *number_bit_width = 32;
+ return SPV_NUMBER_UNSIGNED_INT;
+ case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER:
+ case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER:
+ switch (inst.opcode()) {
+ case SpvOpSwitch:
+ case SpvOpConstant:
+ case SpvOpSpecConstant:
+ // Same kind of number as the selector (OpSwitch) or the type
+ // (Op*Constant).
+ return GetTypeNumberKind(id_to, inst.GetSingleWordOperand(0),
+ number_bit_width);
+ default:
+ assert(false && "Unreachable");
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return SPV_NUMBER_NONE;
+}
+
+spv_number_kind_t Differ::GetTypeNumberKind(const IdInstructions& id_to,
+ uint32_t id,
+ uint32_t* number_bit_width) {
+ const opt::Instruction* type_inst = GetInst(id_to, id);
+ if (!spvOpcodeIsScalarType(type_inst->opcode())) {
+ type_inst = GetInst(id_to, type_inst->type_id());
+ }
+
+ switch (type_inst->opcode()) {
+ case SpvOpTypeInt:
+ *number_bit_width = type_inst->GetSingleWordOperand(1);
+ return type_inst->GetSingleWordOperand(2) == 0 ? SPV_NUMBER_UNSIGNED_INT
+ : SPV_NUMBER_SIGNED_INT;
+ break;
+ case SpvOpTypeFloat:
+ *number_bit_width = type_inst->GetSingleWordOperand(1);
+ return SPV_NUMBER_FLOATING;
+ default:
+ assert(false && "Unreachable");
+ return SPV_NUMBER_NONE;
+ }
+}
+
+void Differ::MatchCapabilities() {
+ MatchPreambleInstructions(src_->capabilities(), dst_->capabilities());
+}
+
+void Differ::MatchExtensions() {
+ MatchPreambleInstructions(src_->extensions(), dst_->extensions());
+}
+
+void Differ::MatchExtInstImportIds() {
+ // Bunch all of this section's ids as potential matches.
+ PotentialIdMap potential_id_map;
+ auto get_result_id = [](const opt::Instruction& inst) {
+ return inst.result_id();
+ };
+ auto accept_all = [](const opt::Instruction&) { return true; };
+
+ PoolPotentialIds(src_->ext_inst_imports(), potential_id_map.src_ids, true,
+ accept_all, get_result_id);
+ PoolPotentialIds(dst_->ext_inst_imports(), potential_id_map.dst_ids, false,
+ accept_all, get_result_id);
+
+ // Then match the ids.
+ MatchIds(potential_id_map, [](const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst) {
+ // Match OpExtInstImport by exact name, which is operand 1
+ const opt::Operand& src_name = src_inst->GetOperand(1);
+ const opt::Operand& dst_name = dst_inst->GetOperand(1);
+
+ return src_name.AsString() == dst_name.AsString();
+ });
+}
+void Differ::MatchMemoryModel() {
+ // Always match the memory model instructions, there is always a single one of
+ // it.
+ id_map_.MapInsts(src_->GetMemoryModel(), dst_->GetMemoryModel());
+}
+
+void Differ::MatchEntryPointIds() {
+ // Match OpEntryPoint ids (at index 1) by ExecutionModel (at index 0) and
+ // possibly name (at index 2). OpEntryPoint doesn't produce a result id, so
+ // this function doesn't use the helpers the other functions use.
+
+ // Map from execution model to OpEntryPoint instructions of that model.
+ using ExecutionModelMap =
+ std::unordered_map<uint32_t, std::vector<const opt::Instruction*>>;
+ ExecutionModelMap src_entry_points_map;
+ ExecutionModelMap dst_entry_points_map;
+ std::set<uint32_t> all_execution_models;
+
+ for (const opt::Instruction& src_inst : src_->entry_points()) {
+ uint32_t execution_model = src_inst.GetSingleWordOperand(0);
+ src_entry_points_map[execution_model].push_back(&src_inst);
+ all_execution_models.insert(execution_model);
+ }
+ for (const opt::Instruction& dst_inst : dst_->entry_points()) {
+ uint32_t execution_model = dst_inst.GetSingleWordOperand(0);
+ dst_entry_points_map[execution_model].push_back(&dst_inst);
+ all_execution_models.insert(execution_model);
+ }
+
+ // Go through each model and match the ids.
+ for (const uint32_t execution_model : all_execution_models) {
+ auto& src_insts = src_entry_points_map[execution_model];
+ auto& dst_insts = dst_entry_points_map[execution_model];
+
+ // If there is only one entry point in src and dst with that model, match
+ // them unconditionally.
+ if (src_insts.size() == 1 && dst_insts.size() == 1) {
+ uint32_t src_id = src_insts[0]->GetSingleWordOperand(1);
+ uint32_t dst_id = dst_insts[0]->GetSingleWordOperand(1);
+ id_map_.MapIds(src_id, dst_id);
+ id_map_.MapInsts(src_insts[0], dst_insts[0]);
+ continue;
+ }
+
+ // Otherwise match them by name.
+ bool matched = false;
+ for (const opt::Instruction* src_inst : src_insts) {
+ for (const opt::Instruction* dst_inst : dst_insts) {
+ const opt::Operand& src_name = src_inst->GetOperand(2);
+ const opt::Operand& dst_name = dst_inst->GetOperand(2);
+
+ if (src_name.AsString() == dst_name.AsString()) {
+ uint32_t src_id = src_inst->GetSingleWordOperand(1);
+ uint32_t dst_id = dst_inst->GetSingleWordOperand(1);
+ id_map_.MapIds(src_id, dst_id);
+ id_map_.MapInsts(src_inst, dst_inst);
+ matched = true;
+ break;
+ }
+ }
+ if (matched) {
+ break;
+ }
+ }
+ }
+}
+
+void Differ::MatchExecutionModes() {
+ MatchPreambleInstructions(src_->execution_modes(), dst_->execution_modes());
+}
+
+void Differ::MatchTypeForwardPointers() {
+ // Bunch all of type forward pointers as potential matches.
+ PotentialIdMap potential_id_map;
+ auto get_pointer_type_id = [](const opt::Instruction& inst) {
+ return inst.GetSingleWordOperand(0);
+ };
+ auto accept_type_forward_pointer_ops = [](const opt::Instruction& inst) {
+ return inst.opcode() == SpvOpTypeForwardPointer;
+ };
+
+ PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true,
+ accept_type_forward_pointer_ops, get_pointer_type_id);
+ PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false,
+ accept_type_forward_pointer_ops, get_pointer_type_id);
+
+ // Matching types with cyclical references (i.e. in the style of linked lists)
+ // can get very complex. Currently, the diff tool matches types bottom up, so
+ // on every instruction it expects to know if its operands are already matched
+ // or not. With cyclical references, it cannot know that. Type matching may
+ // need significant modifications to be able to support this use case.
+ //
+ // Currently, forwarded types are only matched by storage class and debug
+ // info, with minimal matching of the type being forwarded:
+ //
+ // - Group by class
+ // - Group by OpType being pointed to
+ // - Group by debug info
+ // - If same name and unique, match
+ // - If leftover is unique, match
+
+ // Group forwarded pointers by storage class first and loop over them.
+ GroupIdsAndMatch<SpvStorageClass>(
+ potential_id_map.src_ids, potential_id_map.dst_ids, SpvStorageClassMax,
+ &Differ::GroupIdsHelperGetTypePointerStorageClass,
+ [this](const IdGroup& src_group_by_storage_class,
+ const IdGroup& dst_group_by_storage_class) {
+
+ // Group them further by the type they are pointing to and loop over
+ // them.
+ GroupIdsAndMatch<SpvOp>(
+ src_group_by_storage_class, dst_group_by_storage_class, SpvOpMax,
+ &Differ::GroupIdsHelperGetTypePointerTypeOp,
+ [this](const IdGroup& src_group_by_type_op,
+ const IdGroup& dst_group_by_type_op) {
+
+ // Group them even further by debug info, if possible and match by
+ // debug name.
+ MatchTypeForwardPointersByName(src_group_by_type_op,
+ dst_group_by_type_op);
+
+ // Match the leftovers only if they lack debug info and there is
+ // only one instance of them.
+ MatchTypeForwardPointersByTypeOp(src_group_by_type_op,
+ dst_group_by_type_op);
+ });
+ });
+
+ // Match the instructions that forward declare the same type themselves
+ for (uint32_t src_id : potential_id_map.src_ids) {
+ uint32_t dst_id = id_map_.MappedDstId(src_id);
+ if (dst_id == 0) continue;
+
+ const opt::Instruction* src_forward_inst =
+ GetForwardPointerInst(src_id_to_, src_id);
+ const opt::Instruction* dst_forward_inst =
+ GetForwardPointerInst(dst_id_to_, dst_id);
+
+ assert(src_forward_inst);
+ assert(dst_forward_inst);
+
+ id_map_.MapInsts(src_forward_inst, dst_forward_inst);
+ }
+}
+
+void Differ::MatchTypeIds() {
+ // Bunch all of type ids as potential matches.
+ PotentialIdMap potential_id_map;
+ auto get_result_id = [](const opt::Instruction& inst) {
+ return inst.result_id();
+ };
+ auto accept_type_ops = [](const opt::Instruction& inst) {
+ return spvOpcodeGeneratesType(inst.opcode());
+ };
+
+ PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true,
+ accept_type_ops, get_result_id);
+ PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false,
+ accept_type_ops, get_result_id);
+
+ // Then match the ids. Start with exact matches, then match the leftover with
+ // gradually loosening degrees of strictness. For example, in the absence of
+ // debug info, two block types will be matched if they differ only in a few of
+ // the fields.
+ for (uint32_t flexibility = 0; flexibility < 2; ++flexibility) {
+ MatchIds(potential_id_map, [this, flexibility](
+ const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst) {
+ const SpvOp src_op = src_inst->opcode();
+ const SpvOp dst_op = dst_inst->opcode();
+
+ // Don't match if the opcode is not the same.
+ if (src_op != dst_op) {
+ return false;
+ }
+
+ switch (src_op) {
+ case SpvOpTypeVoid:
+ case SpvOpTypeBool:
+ case SpvOpTypeSampler:
+ // void, bool and sampler are unique, match them.
+ return true;
+ case SpvOpTypeInt:
+ case SpvOpTypeFloat:
+ case SpvOpTypeVector:
+ case SpvOpTypeMatrix:
+ case SpvOpTypeSampledImage:
+ case SpvOpTypeRuntimeArray:
+ case SpvOpTypePointer:
+ // Match these instructions when all operands match.
+ assert(src_inst->NumInOperandWords() ==
+ dst_inst->NumInOperandWords());
+ return DoOperandsMatch(src_inst, dst_inst, 0,
+ src_inst->NumInOperandWords());
+
+ case SpvOpTypeFunction:
+ case SpvOpTypeImage:
+ // Match function types only if they have the same number of operands,
+ // and they all match.
+ // Match image types similarly, expecting the optional final parameter
+ // to match (if provided in both)
+ if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) {
+ return false;
+ }
+ return DoOperandsMatch(src_inst, dst_inst, 0,
+ src_inst->NumInOperandWords());
+
+ case SpvOpTypeArray:
+ // Match arrays only if the element type and length match. The length
+ // is an id of a constant, so the actual constant it's defining is
+ // compared instead.
+ if (!DoOperandsMatch(src_inst, dst_inst, 0, 1)) {
+ return false;
+ }
+
+ if (AreIdenticalUintConstants(src_inst->GetSingleWordInOperand(1),
+ dst_inst->GetSingleWordInOperand(1))) {
+ return true;
+ }
+
+ // If size is not OpConstant, expect the ids to match exactly (for
+ // example if a spec contant is used).
+ return DoOperandsMatch(src_inst, dst_inst, 1, 1);
+
+ case SpvOpTypeStruct:
+ return MatchOpTypeStruct(src_inst, dst_inst, flexibility);
+
+ default:
+ return false;
+ }
+ });
+ }
+}
+
+void Differ::MatchConstants() {
+ // Bunch all of constant ids as potential matches.
+ PotentialIdMap potential_id_map;
+ auto get_result_id = [](const opt::Instruction& inst) {
+ return inst.result_id();
+ };
+ auto accept_type_ops = [](const opt::Instruction& inst) {
+ return spvOpcodeIsConstant(inst.opcode());
+ };
+
+ PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true,
+ accept_type_ops, get_result_id);
+ PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false,
+ accept_type_ops, get_result_id);
+
+ // Then match the ids. Constants are matched exactly, except for float types
+ // that are first matched exactly, then leftovers are matched with a small
+ // error.
+ for (uint32_t flexibility = 0; flexibility < 2; ++flexibility) {
+ MatchIds(potential_id_map, [this, flexibility](
+ const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst) {
+ const SpvOp src_op = src_inst->opcode();
+ const SpvOp dst_op = dst_inst->opcode();
+
+ // Don't match if the opcode is not the same.
+ if (src_op != dst_op) {
+ return false;
+ }
+
+ switch (src_op) {
+ case SpvOpConstantTrue:
+ case SpvOpConstantFalse:
+ // true and false are unique, match them.
+ return true;
+ case SpvOpConstant:
+ return MatchOpConstant(src_inst, dst_inst, flexibility);
+ case SpvOpConstantComposite:
+ case SpvOpSpecConstantComposite:
+ // Composite constants must match in type and value.
+ //
+ // TODO: match OpConstantNull with OpConstantComposite with all zeros
+ // at flexibility == 1
+ // TODO: match constants from structs that have been flexibly-matched.
+ if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) {
+ return false;
+ }
+ return DoesOperandMatch(src_inst->GetOperand(0),
+ dst_inst->GetOperand(0)) &&
+ DoOperandsMatch(src_inst, dst_inst, 0,
+ src_inst->NumInOperandWords());
+ case SpvOpConstantSampler:
+ // Match sampler constants exactly.
+ // TODO: Allow flexibility in parameters to better diff shaders where
+ // the sampler param has changed.
+ assert(src_inst->NumInOperandWords() ==
+ dst_inst->NumInOperandWords());
+ return DoOperandsMatch(src_inst, dst_inst, 0,
+ src_inst->NumInOperandWords());
+ case SpvOpConstantNull:
+ // Match null constants as long as the type matches.
+ return DoesOperandMatch(src_inst->GetOperand(0),
+ dst_inst->GetOperand(0));
+
+ case SpvOpSpecConstantTrue:
+ case SpvOpSpecConstantFalse:
+ case SpvOpSpecConstant:
+ case SpvOpSpecConstantOp:
+ // Match spec constants by name if available, then by the SpecId
+ // decoration.
+ return MatchOpSpecConstant(src_inst, dst_inst);
+
+ default:
+ return false;
+ }
+ });
+ }
+}
+
+void Differ::MatchVariableIds() {
+ // Bunch all of variable ids as potential matches.
+ PotentialIdMap potential_id_map;
+ auto get_result_id = [](const opt::Instruction& inst) {
+ return inst.result_id();
+ };
+ auto accept_type_ops = [](const opt::Instruction& inst) {
+ return inst.opcode() == SpvOpVariable;
+ };
+
+ PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true,
+ accept_type_ops, get_result_id);
+ PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false,
+ accept_type_ops, get_result_id);
+
+ // Then match the ids. Start with exact matches, then match the leftover with
+ // gradually loosening degrees of strictness. For example, in the absence of
+ // debug info, two otherwise identical variables will be matched if one of
+ // them has a Private storage class and the other doesn't.
+ for (uint32_t flexibility = 0; flexibility < 2; ++flexibility) {
+ MatchIds(potential_id_map,
+ [this, flexibility](const opt::Instruction* src_inst,
+ const opt::Instruction* dst_inst) {
+ assert(src_inst->opcode() == SpvOpVariable);
+ assert(dst_inst->opcode() == SpvOpVariable);
+
+ return MatchOpVariable(src_inst, dst_inst, flexibility);
+ });
+ }
+}
+
+void Differ::MatchFunctions() {
+ IdGroup src_func_ids;
+ IdGroup dst_func_ids;
+
+ for (const auto& func : src_funcs_) {
+ src_func_ids.push_back(func.first);
+ }
+ for (const auto& func : dst_funcs_) {
+ dst_func_ids.push_back(func.first);
+ }
+
+ // Base the matching of functions on debug info when available.
+ GroupIdsAndMatch<std::string>(
+ src_func_ids, dst_func_ids, "", &Differ::GetSanitizedName,
+ [this](const IdGroup& src_group, const IdGroup& dst_group) {
+
+ // If there is a single function with this name in src and dst, it's a
+ // definite match.
+ if (src_group.size() == 1 && dst_group.size() == 1) {
+ id_map_.MapIds(src_group[0], dst_group[0]);
+ return;
+ }
+
+ // If there are multiple functions with the same name, group them by
+ // type, and match only if the types match (and are unique).
+ GroupIdsAndMatch<uint32_t>(src_group, dst_group, 0,
+ &Differ::GroupIdsHelperGetTypeId,
+ [this](const IdGroup& src_group_by_type_id,
+ const IdGroup& dst_group_by_type_id) {
+
+ if (src_group_by_type_id.size() == 1 &&
+ dst_group_by_type_id.size() == 1) {
+ id_map_.MapIds(src_group_by_type_id[0],
+ dst_group_by_type_id[0]);
+ }
+ });
+ });
+
+ // Any functions that are left are pooled together and matched as if unnamed,
+ // with the only exception that two functions with mismatching names are not
+ // matched.
+ //
+ // Before that however, the diff of the functions that are matched are taken
+ // and processed, so that more of the global variables can be matched before
+ // attempting to match the rest of the functions. They can contribute to the
+ // precision of the diff of those functions.
+ for (const uint32_t src_func_id : src_func_ids) {
+ const uint32_t dst_func_id = id_map_.MappedDstId(src_func_id);
+ if (dst_func_id == 0) {
+ continue;
+ }
+
+ // Since these functions are definite matches, match their parameters for a
+ // better diff.
+ MatchFunctionParamIds(src_funcs_[src_func_id], dst_funcs_[dst_func_id]);
+
+ // Take the diff of the two functions.
+ DiffMatch src_match_result, dst_match_result;
+ MatchFunctionBodies(src_func_insts_[src_func_id],
+ dst_func_insts_[dst_func_id], &src_match_result,
+ &dst_match_result);
+
+ // Match ids between the two function bodies; which can also result in
+ // global variables getting matched.
+ MatchIdsInFunctionBodies(src_func_insts_[src_func_id],
+ dst_func_insts_[dst_func_id], src_match_result,
+ dst_match_result, 0);
+ }
+
+ // Best effort match functions with matching type.
+ GroupIdsAndMatch<uint32_t>(
+ src_func_ids, dst_func_ids, 0, &Differ::GroupIdsHelperGetTypeId,
+ [this](const IdGroup& src_group_by_type_id,
+ const IdGroup& dst_group_by_type_id) {
+
+ BestEffortMatchFunctions(src_group_by_type_id, dst_group_by_type_id,
+ src_func_insts_, dst_func_insts_);
+ });
+
+ // Any function that's left, best effort match them.
+ BestEffortMatchFunctions(src_func_ids, dst_func_ids, src_func_insts_,
+ dst_func_insts_);
+}
+
+void Differ::MatchDebugs1() {
+ // This section in cludes: OpString, OpSourceExtension, OpSource,
+ // OpSourceContinued
+ MatchDebugAndAnnotationInstructions(src_->debugs1(), dst_->debugs1());
+}
+
+void Differ::MatchDebugs2() {
+ // This section includes: OpName, OpMemberName
+ MatchDebugAndAnnotationInstructions(src_->debugs2(), dst_->debugs2());
+}
+
+void Differ::MatchDebugs3() {
+ // This section includes: OpModuleProcessed
+ MatchDebugAndAnnotationInstructions(src_->debugs3(), dst_->debugs3());
+}
+
+void Differ::MatchExtInstDebugInfo() {
+ // This section includes OpExtInst for DebugInfo extension
+ MatchDebugAndAnnotationInstructions(src_->ext_inst_debuginfo(),
+ dst_->ext_inst_debuginfo());
+}
+
+void Differ::MatchAnnotations() {
+ // This section includes OpDecorate and family.
+ MatchDebugAndAnnotationInstructions(src_->annotations(), dst_->annotations());
+}
+
+const opt::Instruction* Differ::MappedDstInst(
+ const opt::Instruction* src_inst) {
+ return MappedInstImpl(src_inst, id_map_.SrcToDstMap(), dst_id_to_);
+}
+
+const opt::Instruction* Differ::MappedSrcInst(
+ const opt::Instruction* dst_inst) {
+ return MappedInstImpl(dst_inst, id_map_.DstToSrcMap(), src_id_to_);
+}
+
+const opt::Instruction* Differ::MappedInstImpl(
+ const opt::Instruction* inst, const IdMap& to_other,
+ const IdInstructions& other_id_to) {
+ if (inst->HasResultId()) {
+ if (to_other.IsMapped(inst->result_id())) {
+ const uint32_t other_result_id = to_other.MappedId(inst->result_id());
+
+ assert(other_result_id < other_id_to.inst_map_.size());
+ return other_id_to.inst_map_[other_result_id];
+ }
+
+ return nullptr;
+ }
+
+ return to_other.MappedInst(inst);
+}
+
+void Differ::OutputLine(std::function<bool()> are_lines_identical,
+ std::function<void()> output_src_line,
+ std::function<void()> output_dst_line) {
+ if (are_lines_identical()) {
+ out_ << " ";
+ output_src_line();
+ } else {
+ OutputRed();
+ out_ << "-";
+ output_src_line();
+
+ OutputGreen();
+ out_ << "+";
+ output_dst_line();
+
+ OutputResetColor();
+ }
+}
+
+const opt::Instruction* IterInst(opt::Module::const_inst_iterator& iter) {
+ return &*iter;
+}
+
+const opt::Instruction* IterInst(InstructionList::const_iterator& iter) {
+ return *iter;
+}
+
+template <typename InstList>
+void Differ::OutputSection(
+ const InstList& src_insts, const InstList& dst_insts,
+ std::function<void(const opt::Instruction&, const IdInstructions&,
+ const opt::Instruction&)>
+ write_inst) {
+ auto src_iter = src_insts.begin();
+ auto dst_iter = dst_insts.begin();
+
+ // - While src_inst doesn't have a match, output it with -
+ // - While dst_inst doesn't have a match, output it with +
+ // - Now src_inst and dst_inst both have matches; might not match each other!
+ // * If section is unordered, just process src_inst and its match (dst_inst
+ // or not),
+ // dst_inst will eventually be processed when its match is seen.
+ // * If section is ordered, also just process src_inst and its match. Its
+ // match must
+ // necessarily be dst_inst.
+ while (src_iter != src_insts.end() || dst_iter != dst_insts.end()) {
+ OutputRed();
+ while (src_iter != src_insts.end() &&
+ MappedDstInst(IterInst(src_iter)) == nullptr) {
+ out_ << "-";
+ write_inst(*IterInst(src_iter), src_id_to_, *IterInst(src_iter));
+ ++src_iter;
+ }
+ OutputGreen();
+ while (dst_iter != dst_insts.end() &&
+ MappedSrcInst(IterInst(dst_iter)) == nullptr) {
+ out_ << "+";
+ write_inst(ToMappedSrcIds(*IterInst(dst_iter)), dst_id_to_,
+ *IterInst(dst_iter));
+ ++dst_iter;
+ }
+ OutputResetColor();
+
+ if (src_iter != src_insts.end() && dst_iter != dst_insts.end()) {
+ const opt::Instruction* src_inst = IterInst(src_iter);
+ const opt::Instruction* matched_dst_inst = MappedDstInst(src_inst);
+
+ assert(matched_dst_inst != nullptr);
+ assert(MappedSrcInst(IterInst(dst_iter)) != nullptr);
+
+ OutputLine(
+ [this, src_inst, matched_dst_inst]() {
+ return DoInstructionsMatch(src_inst, matched_dst_inst);
+ },
+ [this, src_inst, &write_inst]() {
+ write_inst(*src_inst, src_id_to_, *src_inst);
+ },
+ [this, matched_dst_inst, &write_inst]() {
+ write_inst(ToMappedSrcIds(*matched_dst_inst), dst_id_to_,
+ *matched_dst_inst);
+ });
+
+ ++src_iter;
+ ++dst_iter;
+ }
+ }
+}
+
+void Differ::ToParsedInstruction(
+ const opt::Instruction& inst, const IdInstructions& id_to,
+ const opt::Instruction& original_inst,
+ spv_parsed_instruction_t* parsed_inst,
+ std::vector<spv_parsed_operand_t>& parsed_operands,
+ std::vector<uint32_t>& inst_binary) {
+ inst.ToBinaryWithoutAttachedDebugInsts(&inst_binary);
+ parsed_operands.resize(inst.NumOperands());
+
+ parsed_inst->words = inst_binary.data();
+ parsed_inst->num_words = static_cast<uint16_t>(inst_binary.size());
+ parsed_inst->opcode = static_cast<uint16_t>(inst.opcode());
+ parsed_inst->ext_inst_type =
+ inst.opcode() == SpvOpExtInst
+ ? GetExtInstType(id_to, original_inst.GetSingleWordInOperand(0))
+ : SPV_EXT_INST_TYPE_NONE;
+ parsed_inst->type_id =
+ inst.HasResultType() ? inst.GetSingleWordOperand(0) : 0;
+ parsed_inst->result_id = inst.HasResultId() ? inst.result_id() : 0;
+ parsed_inst->operands = parsed_operands.data();
+ parsed_inst->num_operands = static_cast<uint16_t>(parsed_operands.size());
+
+ // Word 0 is always op and num_words, so operands start at offset 1.
+ uint32_t offset = 1;
+ for (uint16_t operand_index = 0; operand_index < parsed_inst->num_operands;
+ ++operand_index) {
+ const opt::Operand& operand = inst.GetOperand(operand_index);
+ spv_parsed_operand_t& parsed_operand = parsed_operands[operand_index];
+
+ parsed_operand.offset = static_cast<uint16_t>(offset);
+ parsed_operand.num_words = static_cast<uint16_t>(operand.words.size());
+ parsed_operand.type = operand.type;
+ parsed_operand.number_kind = GetNumberKind(
+ id_to, original_inst, operand_index, &parsed_operand.number_bit_width);
+
+ offset += parsed_operand.num_words;
+ }
+}
+
+opt::Instruction Differ::ToMappedSrcIds(const opt::Instruction& dst_inst) {
+ // Create an identical instruction to dst_inst, except ids are changed to the
+ // mapped one.
+ opt::Instruction mapped_inst = dst_inst;
+
+ for (uint32_t operand_index = 0; operand_index < mapped_inst.NumOperands();
+ ++operand_index) {
+ opt::Operand& operand = mapped_inst.GetOperand(operand_index);
+
+ if (spvIsIdType(operand.type)) {
+ assert(id_map_.IsDstMapped(operand.AsId()));
+ operand.words[0] = id_map_.MappedSrcId(operand.AsId());
+ }
+ }
+
+ return mapped_inst;
+}
+
+spv_result_t Differ::Output() {
+ id_map_.MapUnmatchedIds();
+ src_id_to_.inst_map_.resize(id_map_.SrcToDstMap().IdBound(), nullptr);
+ dst_id_to_.inst_map_.resize(id_map_.DstToSrcMap().IdBound(), nullptr);
+
+ const spv_target_env target_env = SPV_ENV_UNIVERSAL_1_6;
+ spv_opcode_table opcode_table;
+ spv_operand_table operand_table;
+ spv_ext_inst_table ext_inst_table;
+ spv_result_t result;
+
+ result = spvOpcodeTableGet(&opcode_table, target_env);
+ if (result != SPV_SUCCESS) return result;
+
+ result = spvOperandTableGet(&operand_table, target_env);
+ if (result != SPV_SUCCESS) return result;
+
+ result = spvExtInstTableGet(&ext_inst_table, target_env);
+ if (result != SPV_SUCCESS) return result;
+
+ spv_context_t context{
+ target_env,
+ opcode_table,
+ operand_table,
+ ext_inst_table,
+ };
+
+ const AssemblyGrammar grammar(&context);
+ if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE;
+
+ uint32_t disassembly_options = SPV_BINARY_TO_TEXT_OPTION_PRINT;
+ if (options_.indent) {
+ disassembly_options |= SPV_BINARY_TO_TEXT_OPTION_INDENT;
+ }
+
+ NameMapper name_mapper = GetTrivialNameMapper();
+ disassemble::InstructionDisassembler dis(grammar, out_, disassembly_options,
+ name_mapper);
+
+ if (!options_.no_header) {
+ // Output the header
+ // TODO: when using diff with text, the assembler overrides the version and
+ // generator, so these aren't reflected correctly in the output. Could
+ // potentially extract this info from the header comment.
+ OutputLine([]() { return true; }, [&dis]() { dis.EmitHeaderSpirv(); },
+ []() { assert(false && "Unreachable"); });
+ OutputLine([this]() { return src_->version() == dst_->version(); },
+ [this, &dis]() { dis.EmitHeaderVersion(src_->version()); },
+ [this, &dis]() { dis.EmitHeaderVersion(dst_->version()); });
+ OutputLine([this]() { return src_->generator() == dst_->generator(); },
+ [this, &dis]() { dis.EmitHeaderGenerator(src_->generator()); },
+ [this, &dis]() { dis.EmitHeaderGenerator(dst_->generator()); });
+ OutputLine(
+ [this]() { return src_->IdBound() == id_map_.SrcToDstMap().IdBound(); },
+ [this, &dis]() { dis.EmitHeaderIdBound(src_->IdBound()); },
+ [this, &dis]() {
+ dis.EmitHeaderIdBound(id_map_.SrcToDstMap().IdBound());
+ });
+ OutputLine([this]() { return src_->schema() == dst_->schema(); },
+ [this, &dis]() { dis.EmitHeaderSchema(src_->schema()); },
+ [this, &dis]() { dis.EmitHeaderSchema(dst_->schema()); });
+ }
+
+ // For each section, iterate both modules and output the disassembly.
+ auto write_inst = [this, &dis](const opt::Instruction& inst,
+ const IdInstructions& id_to,
+ const opt::Instruction& original_inst) {
+ spv_parsed_instruction_t parsed_inst;
+ std::vector<spv_parsed_operand_t> parsed_operands;
+ std::vector<uint32_t> inst_binary;
+
+ ToParsedInstruction(inst, id_to, original_inst, &parsed_inst,
+ parsed_operands, inst_binary);
+
+ dis.EmitInstruction(parsed_inst, 0);
+ };
+
+ OutputSection(src_->capabilities(), dst_->capabilities(), write_inst);
+ OutputSection(src_->extensions(), dst_->extensions(), write_inst);
+ OutputSection(src_->ext_inst_imports(), dst_->ext_inst_imports(), write_inst);
+
+ // There is only one memory model.
+ OutputLine(
+ [this]() {
+ return DoInstructionsMatch(src_->GetMemoryModel(),
+ dst_->GetMemoryModel());
+ },
+ [this, &write_inst]() {
+ write_inst(*src_->GetMemoryModel(), src_id_to_,
+ *src_->GetMemoryModel());
+ },
+ [this, &write_inst]() {
+ write_inst(*dst_->GetMemoryModel(), dst_id_to_,
+ *dst_->GetMemoryModel());
+ });
+
+ OutputSection(src_->entry_points(), dst_->entry_points(), write_inst);
+ OutputSection(src_->execution_modes(), dst_->execution_modes(), write_inst);
+ OutputSection(src_->debugs1(), dst_->debugs1(), write_inst);
+ OutputSection(src_->debugs2(), dst_->debugs2(), write_inst);
+ OutputSection(src_->debugs3(), dst_->debugs3(), write_inst);
+ OutputSection(src_->ext_inst_debuginfo(), dst_->ext_inst_debuginfo(),
+ write_inst);
+ OutputSection(src_->annotations(), dst_->annotations(), write_inst);
+ OutputSection(src_->types_values(), dst_->types_values(), write_inst);
+
+ // Get the body of all the functions.
+ FunctionInstMap src_func_header_insts;
+ FunctionInstMap dst_func_header_insts;
+
+ GetFunctionHeaderInstructions(src_, &src_func_header_insts);
+ GetFunctionHeaderInstructions(dst_, &dst_func_header_insts);
+
+ for (const auto& src_func : src_func_insts_) {
+ const uint32_t src_func_id = src_func.first;
+ const InstructionList& src_insts = src_func.second;
+ const InstructionList& src_header_insts =
+ src_func_header_insts[src_func_id];
+
+ const uint32_t dst_func_id = id_map_.MappedDstId(src_func_id);
+ if (dst_func_insts_.find(dst_func_id) == dst_func_insts_.end()) {
+ OutputSection(src_header_insts, InstructionList(), write_inst);
+ OutputSection(src_insts, InstructionList(), write_inst);
+ continue;
+ }
+
+ const InstructionList& dst_insts = dst_func_insts_[dst_func_id];
+ const InstructionList& dst_header_insts =
+ dst_func_header_insts[dst_func_id];
+ OutputSection(src_header_insts, dst_header_insts, write_inst);
+ OutputSection(src_insts, dst_insts, write_inst);
+ }
+
+ for (const auto& dst_func : dst_func_insts_) {
+ const uint32_t dst_func_id = dst_func.first;
+ const InstructionList& dst_insts = dst_func.second;
+ const InstructionList& dst_header_insts =
+ dst_func_header_insts[dst_func_id];
+
+ const uint32_t src_func_id = id_map_.MappedSrcId(dst_func_id);
+ if (src_func_insts_.find(src_func_id) == src_func_insts_.end()) {
+ OutputSection(InstructionList(), dst_header_insts, write_inst);
+ OutputSection(InstructionList(), dst_insts, write_inst);
+ }
+ }
+
+ out_ << std::flush;
+
+ return SPV_SUCCESS;
+}
+
+} // anonymous namespace
+
+spv_result_t Diff(opt::IRContext* src, opt::IRContext* dst, std::ostream& out,
+ Options options) {
+ // High level algorithm:
+ //
+ // - Some sections of SPIR-V don't deal with ids; instructions in those
+ // sections are matched identically. For example OpCapability instructions.
+ // - Some sections produce ids, and they can be trivially matched by their
+ // parameters. For example OpExtInstImport instructions.
+ // - Some sections annotate ids. These are matched at the end, after the ids
+ // themselves are matched. For example OpName or OpDecorate instructions.
+ // - Some sections produce ids that depend on other ids and they can be
+ // recursively matched. For example OpType* instructions.
+ // - Some sections produce ids that are not trivially matched. For these ids,
+ // the debug info is used when possible, or a best guess (such as through
+ // decorations) is used. For example OpVariable instructions.
+ // - Matching functions is done with multiple attempts:
+ // * Functions with identical debug names are matched if there are no
+ // overloads.
+ // * Otherwise, functions with identical debug names and types are matched.
+ // * The rest of the functions are best-effort matched, first in groups of
+ // identical type, then any with any.
+ // * The best-effort matching takes the diff of every pair of functions in
+ // a group and selects the top matches that also meet a similarity
+ // index.
+ // * Once a pair of functions are matched, the fuzzy diff of the
+ // instructions is used to match the instructions in the function body.
+ // The fuzzy diff makes sure that sufficiently similar instructions are
+ // matched and that yet-to-be-matched result ids don't result in a larger
+ // diff.
+ //
+ // Once the instructions are matched between the src and dst SPIR-V, the src
+ // is traversed and its disassembly is output. In the process, any unmatched
+ // instruction is prefixed with -, and any unmatched instruction in dst in the
+ // same section is output prefixed with +. To avoid confusion, the
+ // instructions in dst are output with matching ids in src so the output
+ // assembly is consistent.
+
+ Differ differ(src, dst, out, options);
+
+ // First, match instructions between the different non-annotation sections of
+ // the SPIR-V.
+ differ.MatchCapabilities();
+ differ.MatchExtensions();
+ differ.MatchExtInstImportIds();
+ differ.MatchMemoryModel();
+ differ.MatchEntryPointIds();
+ differ.MatchExecutionModes();
+ differ.MatchTypeForwardPointers();
+ differ.MatchTypeIds();
+ differ.MatchConstants();
+ differ.MatchVariableIds();
+ differ.MatchFunctions();
+
+ // Match instructions that annotate previously-matched ids.
+ differ.MatchDebugs1();
+ differ.MatchDebugs2();
+ differ.MatchDebugs3();
+ differ.MatchExtInstDebugInfo();
+ differ.MatchAnnotations();
+
+ // Show the disassembly with the diff.
+ //
+ // TODO: Based on an option, output either based on src or dst, i.e. the diff
+ // can show the ids and instruction/function order either from src or dst.
+ spv_result_t result = differ.Output();
+
+ differ.DumpIdMap();
+
+ return result;
+}
+
+} // namespace diff
+} // namespace spvtools
diff --git a/source/diff/diff.h b/source/diff/diff.h
new file mode 100644
index 00000000..932de9ee
--- /dev/null
+++ b/source/diff/diff.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2022 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_DIFF_DIFF_H_
+#define SOURCE_DIFF_DIFF_H_
+
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace diff {
+
+struct Options {
+ bool ignore_set_binding = false;
+ bool ignore_location = false;
+ bool indent = false;
+ bool no_header = false;
+ bool color_output = false;
+ bool dump_id_map = false;
+};
+
+// Given two SPIR-V modules, this function outputs the textual diff of their
+// assembly in `out`. The diff is *semantic*, so that the ordering of certain
+// instructions wouldn't matter.
+//
+// The output is a disassembly of src, with diff(1)-style + and - lines that
+// show how the src is changed into dst. To make this disassembly
+// self-consistent, the ids that are output are all in the space of the src
+// module; e.g. any + lines (showing instructions from the dst module) have
+// their ids mapped to the matched instruction in the src module (or a new id
+// allocated in the src module if unmatched).
+spv_result_t Diff(opt::IRContext* src, opt::IRContext* dst, std::ostream& out,
+ Options options);
+
+} // namespace diff
+} // namespace spvtools
+
+#endif // SOURCE_DIFF_DIFF_H_
diff --git a/source/diff/lcs.h b/source/diff/lcs.h
new file mode 100644
index 00000000..6c00e864
--- /dev/null
+++ b/source/diff/lcs.h
@@ -0,0 +1,224 @@
+// Copyright (c) 2022 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_DIFF_LCS_H_
+#define SOURCE_DIFF_LCS_H_
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <stack>
+#include <vector>
+
+namespace spvtools {
+namespace diff {
+
+// The result of a diff.
+using DiffMatch = std::vector<bool>;
+
+// Helper class to find the longest common subsequence between two function
+// bodies.
+template <typename Sequence>
+class LongestCommonSubsequence {
+ public:
+ LongestCommonSubsequence(const Sequence& src, const Sequence& dst)
+ : src_(src),
+ dst_(dst),
+ table_(src.size(), std::vector<DiffMatchEntry>(dst.size())) {}
+
+ // Given two sequences, it creates a matching between them. The elements are
+ // simply marked as matched in src and dst, with any unmatched element in src
+ // implying a removal and any unmatched element in dst implying an addition.
+ //
+ // Returns the length of the longest common subsequence.
+ template <typename T>
+ uint32_t Get(std::function<bool(T src_elem, T dst_elem)> match,
+ DiffMatch* src_match_result, DiffMatch* dst_match_result);
+
+ private:
+ struct DiffMatchIndex {
+ uint32_t src_offset;
+ uint32_t dst_offset;
+ };
+
+ template <typename T>
+ void CalculateLCS(std::function<bool(T src_elem, T dst_elem)> match);
+ void RetrieveMatch(DiffMatch* src_match_result, DiffMatch* dst_match_result);
+ bool IsInBound(DiffMatchIndex index) {
+ return index.src_offset < src_.size() && index.dst_offset < dst_.size();
+ }
+ bool IsCalculated(DiffMatchIndex index) {
+ assert(IsInBound(index));
+ return table_[index.src_offset][index.dst_offset].valid;
+ }
+ bool IsCalculatedOrOutOfBound(DiffMatchIndex index) {
+ return !IsInBound(index) || IsCalculated(index);
+ }
+ uint32_t GetMemoizedLength(DiffMatchIndex index) {
+ if (!IsInBound(index)) {
+ return 0;
+ }
+ assert(IsCalculated(index));
+ return table_[index.src_offset][index.dst_offset].best_match_length;
+ }
+ bool IsMatched(DiffMatchIndex index) {
+ assert(IsCalculated(index));
+ return table_[index.src_offset][index.dst_offset].matched;
+ }
+ void MarkMatched(DiffMatchIndex index, uint32_t best_match_length,
+ bool matched) {
+ assert(IsInBound(index));
+ DiffMatchEntry& entry = table_[index.src_offset][index.dst_offset];
+ assert(!entry.valid);
+
+ entry.best_match_length = best_match_length & 0x3FFFFFFF;
+ assert(entry.best_match_length == best_match_length);
+ entry.matched = matched;
+ entry.valid = true;
+ }
+
+ const Sequence& src_;
+ const Sequence& dst_;
+
+ struct DiffMatchEntry {
+ DiffMatchEntry() : best_match_length(0), matched(false), valid(false) {}
+
+ uint32_t best_match_length : 30;
+ // Whether src[i] and dst[j] matched. This is an optimization to avoid
+ // calling the `match` function again when walking the LCS table.
+ uint32_t matched : 1;
+ // Use for the recursive algorithm to know if the contents of this entry are
+ // valid.
+ uint32_t valid : 1;
+ };
+
+ std::vector<std::vector<DiffMatchEntry>> table_;
+};
+
+template <typename Sequence>
+template <typename T>
+uint32_t LongestCommonSubsequence<Sequence>::Get(
+ std::function<bool(T src_elem, T dst_elem)> match,
+ DiffMatch* src_match_result, DiffMatch* dst_match_result) {
+ CalculateLCS(match);
+ RetrieveMatch(src_match_result, dst_match_result);
+ return GetMemoizedLength({0, 0});
+}
+
+template <typename Sequence>
+template <typename T>
+void LongestCommonSubsequence<Sequence>::CalculateLCS(
+ std::function<bool(T src_elem, T dst_elem)> match) {
+ // The LCS algorithm is simple. Given sequences s and d, with a:b depicting a
+ // range in python syntax:
+ //
+ // lcs(s[i:], d[j:]) =
+ // lcs(s[i+1:], d[j+1:]) + 1 if s[i] == d[j]
+ // max(lcs(s[i+1:], d[j:]), lcs(s[i:], d[j+1:])) o.w.
+ //
+ // Once the LCS table is filled according to the above, it can be walked and
+ // the best match retrieved.
+ //
+ // This is a recursive function with memoization, which avoids filling table
+ // entries where unnecessary. This makes the best case O(N) instead of
+ // O(N^2). The implemention uses a std::stack to avoid stack overflow on long
+ // sequences.
+
+ if (src_.empty() || dst_.empty()) {
+ return;
+ }
+
+ std::stack<DiffMatchIndex> to_calculate;
+ to_calculate.push({0, 0});
+
+ while (!to_calculate.empty()) {
+ DiffMatchIndex current = to_calculate.top();
+ to_calculate.pop();
+ assert(IsInBound(current));
+
+ // If already calculated through another path, ignore it.
+ if (IsCalculated(current)) {
+ continue;
+ }
+
+ if (match(src_[current.src_offset], dst_[current.dst_offset])) {
+ // If the current elements match, advance both indices and calculate the
+ // LCS if not already. Visit `current` again afterwards, so its
+ // corresponding entry will be updated.
+ DiffMatchIndex next = {current.src_offset + 1, current.dst_offset + 1};
+ if (IsCalculatedOrOutOfBound(next)) {
+ MarkMatched(current, GetMemoizedLength(next) + 1, true);
+ } else {
+ to_calculate.push(current);
+ to_calculate.push(next);
+ }
+ continue;
+ }
+
+ // We've reached a pair of elements that don't match. Calculate the LCS for
+ // both cases of either being left unmatched and take the max. Visit
+ // `current` again afterwards, so its corresponding entry will be updated.
+ DiffMatchIndex next_src = {current.src_offset + 1, current.dst_offset};
+ DiffMatchIndex next_dst = {current.src_offset, current.dst_offset + 1};
+
+ if (IsCalculatedOrOutOfBound(next_src) &&
+ IsCalculatedOrOutOfBound(next_dst)) {
+ uint32_t best_match_length =
+ std::max(GetMemoizedLength(next_src), GetMemoizedLength(next_dst));
+ MarkMatched(current, best_match_length, false);
+ continue;
+ }
+
+ to_calculate.push(current);
+ if (!IsCalculatedOrOutOfBound(next_src)) {
+ to_calculate.push(next_src);
+ }
+ if (!IsCalculatedOrOutOfBound(next_dst)) {
+ to_calculate.push(next_dst);
+ }
+ }
+}
+
+template <typename Sequence>
+void LongestCommonSubsequence<Sequence>::RetrieveMatch(
+ DiffMatch* src_match_result, DiffMatch* dst_match_result) {
+ src_match_result->clear();
+ dst_match_result->clear();
+
+ src_match_result->resize(src_.size(), false);
+ dst_match_result->resize(dst_.size(), false);
+
+ DiffMatchIndex current = {0, 0};
+ while (IsInBound(current)) {
+ if (IsMatched(current)) {
+ (*src_match_result)[current.src_offset++] = true;
+ (*dst_match_result)[current.dst_offset++] = true;
+ continue;
+ }
+
+ if (GetMemoizedLength({current.src_offset + 1, current.dst_offset}) >=
+ GetMemoizedLength({current.src_offset, current.dst_offset + 1})) {
+ ++current.src_offset;
+ } else {
+ ++current.dst_offset;
+ }
+ }
+}
+
+} // namespace diff
+} // namespace spvtools
+
+#endif // SOURCE_DIFF_LCS_H_
diff --git a/source/disassemble.h b/source/disassemble.h
index 8eacb100..b520a1ea 100644
--- a/source/disassemble.h
+++ b/source/disassemble.h
@@ -25,9 +25,10 @@ namespace spvtools {
// Decodes the given SPIR-V instruction binary representation to its assembly
// text. The context is inferred from the provided module binary. The options
-// parameter is a bit field of spv_binary_to_text_options_t. Decoded text will
-// be stored into *text. Any error will be written into *diagnostic if
-// diagnostic is non-null.
+// parameter is a bit field of spv_binary_to_text_options_t (note: the option
+// SPV_BINARY_TO_TEXT_OPTION_PRINT will be ignored). Decoded text will be
+// stored into *text. Any error will be written into *diagnostic if diagnostic
+// is non-null.
std::string spvInstructionBinaryToText(const spv_target_env env,
const uint32_t* inst_binary,
const size_t inst_word_count,
diff --git a/source/libspirv.cpp b/source/libspirv.cpp
index 0bc09350..be76caaa 100644
--- a/source/libspirv.cpp
+++ b/source/libspirv.cpp
@@ -99,7 +99,9 @@ bool SpirvTools::Disassemble(const uint32_t* binary, const size_t binary_size,
spv_text spvtext = nullptr;
spv_result_t status = spvBinaryToText(impl_->context, binary, binary_size,
options, &spvtext, nullptr);
- if (status == SPV_SUCCESS) {
+ if (status == SPV_SUCCESS &&
+ (options & SPV_BINARY_TO_TEXT_OPTION_PRINT) == 0) {
+ assert(spvtext);
text->assign(spvtext->str, spvtext->str + spvtext->length);
}
spvTextDestroy(spvtext);
diff --git a/source/link/linker.cpp b/source/link/linker.cpp
index 76ce775d..3b388cc6 100644
--- a/source/link/linker.cpp
+++ b/source/link/linker.cpp
@@ -34,6 +34,7 @@
#include "source/opt/ir_loader.h"
#include "source/opt/pass_manager.h"
#include "source/opt/remove_duplicates_pass.h"
+#include "source/opt/remove_unused_interface_variables_pass.h"
#include "source/opt/type_manager.h"
#include "source/spirv_constant.h"
#include "source/spirv_target_env.h"
@@ -807,11 +808,16 @@ spv_result_t Link(const Context& context, const uint32_t* const* binaries,
pass_res = manager.Run(&linked_context);
if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
- // Phase 11: Warn if SPIR-V limits were exceeded
+ // Phase 11: Recompute EntryPoint variables
+ manager.AddPass<opt::RemoveUnusedInterfaceVariablesPass>();
+ pass_res = manager.Run(&linked_context);
+ if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
+
+ // Phase 12: Warn if SPIR-V limits were exceeded
res = VerifyLimits(consumer, linked_context);
if (res != SPV_SUCCESS) return res;
- // Phase 12: Output the module
+ // Phase 13: Output the module
linked_context.module()->ToBinary(linked_binary, true);
return SPV_SUCCESS;
diff --git a/source/opcode.cpp b/source/opcode.cpp
index 88085df7..3f927290 100644
--- a/source/opcode.cpp
+++ b/source/opcode.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2015-2020 The Khronos Group Inc.
+// Copyright (c) 2015-2022 The Khronos Group Inc.
// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
// reserved.
//
@@ -453,6 +453,7 @@ bool spvOpcodeIsAbort(SpvOp opcode) {
case SpvOpTerminateInvocation:
case SpvOpTerminateRayKHR:
case SpvOpIgnoreIntersectionKHR:
+ case SpvOpEmitMeshTasksEXT:
return true;
default:
return false;
@@ -467,11 +468,6 @@ bool spvOpcodeIsBlockTerminator(SpvOp opcode) {
return spvOpcodeIsBranch(opcode) || spvOpcodeIsReturnOrAbort(opcode);
}
-bool spvOpcodeTerminatesExecution(SpvOp opcode) {
- return opcode == SpvOpKill || opcode == SpvOpTerminateInvocation ||
- opcode == SpvOpTerminateRayKHR || opcode == SpvOpIgnoreIntersectionKHR;
-}
-
bool spvOpcodeIsBaseOpaqueType(SpvOp opcode) {
switch (opcode) {
case SpvOpTypeImage:
@@ -528,6 +524,7 @@ bool spvOpcodeIsNonUniformGroupOperation(SpvOp opcode) {
case SpvOpGroupNonUniformLogicalXor:
case SpvOpGroupNonUniformQuadBroadcast:
case SpvOpGroupNonUniformQuadSwap:
+ case SpvOpGroupNonUniformRotateKHR:
return true;
default:
return false;
@@ -631,6 +628,7 @@ bool spvOpcodeIsDebug(SpvOp opcode) {
case SpvOpString:
case SpvOpLine:
case SpvOpNoLine:
+ case SpvOpModuleProcessed:
return true;
default:
return false;
diff --git a/source/opcode.h b/source/opcode.h
index c8525a25..77a0bed2 100644
--- a/source/opcode.h
+++ b/source/opcode.h
@@ -110,18 +110,16 @@ bool spvOpcodeIsBranch(SpvOp opcode);
// Returns true if the given opcode is a return instruction.
bool spvOpcodeIsReturn(SpvOp opcode);
-// Returns true if the given opcode aborts execution.
+// Returns true if the given opcode aborts execution. To abort means that after
+// executing that instruction, no other instructions will be executed regardless
+// of the context in which the instruction appears. Note that `OpUnreachable`
+// is considered an abort even if its behaviour is undefined.
bool spvOpcodeIsAbort(SpvOp opcode);
// Returns true if the given opcode is a return instruction or it aborts
// execution.
bool spvOpcodeIsReturnOrAbort(SpvOp opcode);
-// Returns true if the given opcode is a kill instruction or it terminates
-// execution. Note that branches, returns, and unreachables do not terminate
-// execution.
-bool spvOpcodeTerminatesExecution(SpvOp opcode);
-
// Returns true if the given opcode is a basic block terminator.
bool spvOpcodeIsBlockTerminator(SpvOp opcode);
diff --git a/source/operand.cpp b/source/operand.cpp
index 6d83e81e..0c255a35 100644
--- a/source/operand.cpp
+++ b/source/operand.cpp
@@ -72,12 +72,16 @@ spv_result_t spvOperandTableNameLookup(spv_target_env env,
// Note that the second rule assumes the extension enabling this operand
// is indeed requested in the SPIR-V code; checking that should be
// validator's work.
- if (((version >= entry.minVersion && version <= entry.lastVersion) ||
- entry.numExtensions > 0u || entry.numCapabilities > 0u) &&
- nameLength == strlen(entry.name) &&
+ if (nameLength == strlen(entry.name) &&
!strncmp(entry.name, name, nameLength)) {
- *pEntry = &entry;
- return SPV_SUCCESS;
+ if ((version >= entry.minVersion && version <= entry.lastVersion) ||
+ entry.numExtensions > 0u || entry.numCapabilities > 0u) {
+ *pEntry = &entry;
+ return SPV_SUCCESS;
+ } else {
+ // if there is no extension/capability then the version is wrong
+ return SPV_ERROR_WRONG_VERSION;
+ }
}
}
}
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt
index 7508dc02..75fe4c0f 100644
--- a/source/opt/CMakeLists.txt
+++ b/source/opt/CMakeLists.txt
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
set(SPIRV_TOOLS_OPT_SOURCES
+ fix_func_call_arguments.h
aggressive_dead_code_elim_pass.h
amd_ext_to_khr.h
basic_block.h
@@ -45,6 +46,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
eliminate_dead_constant_pass.h
eliminate_dead_functions_pass.h
eliminate_dead_functions_util.h
+ eliminate_dead_input_components_pass.h
eliminate_dead_members_pass.h
empty_pass.h
feature_manager.h
@@ -66,6 +68,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
instruction.h
instruction_list.h
instrument_pass.h
+ interface_var_sroa.h
interp_fixup_pass.h
ir_builder.h
ir_context.h
@@ -99,6 +102,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
reflect.h
register_pressure.h
relax_float_ops_pass.h
+ remove_dontinline_pass.h
remove_duplicates_pass.h
remove_unused_interface_variables_pass.h
replace_desc_array_access_using_var_index.h
@@ -124,6 +128,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
workaround1209.h
wrap_opkill.h
+ fix_func_call_arguments.cpp
aggressive_dead_code_elim_pass.cpp
amd_ext_to_khr.cpp
basic_block.cpp
@@ -157,6 +162,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
eliminate_dead_constant_pass.cpp
eliminate_dead_functions_pass.cpp
eliminate_dead_functions_util.cpp
+ eliminate_dead_input_components_pass.cpp
eliminate_dead_members_pass.cpp
feature_manager.cpp
fix_storage_class.cpp
@@ -177,6 +183,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
instruction.cpp
instruction_list.cpp
instrument_pass.cpp
+ interface_var_sroa.cpp
interp_fixup_pass.cpp
ir_context.cpp
ir_loader.cpp
@@ -207,6 +214,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
redundancy_elimination.cpp
register_pressure.cpp
relax_float_ops_pass.cpp
+ remove_dontinline_pass.cpp
remove_duplicates_pass.cpp
remove_unused_interface_variables_pass.cpp
replace_desc_array_access_using_var_index.cpp
diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp
index 9827c535..7fa5c8a9 100644
--- a/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -338,23 +338,25 @@ void AggressiveDCEPass::ProcessWorkList(Function* func) {
}
}
+void AggressiveDCEPass::AddDebugScopeToWorkList(const Instruction* inst) {
+ auto scope = inst->GetDebugScope();
+ auto lex_scope_id = scope.GetLexicalScope();
+ if (lex_scope_id != kNoDebugScope)
+ AddToWorklist(get_def_use_mgr()->GetDef(lex_scope_id));
+ auto inlined_at_id = scope.GetInlinedAt();
+ if (inlined_at_id != kNoInlinedAt)
+ AddToWorklist(get_def_use_mgr()->GetDef(inlined_at_id));
+}
+
void AggressiveDCEPass::AddDebugInstructionsToWorkList(
const Instruction* inst) {
for (auto& line_inst : inst->dbg_line_insts()) {
if (line_inst.IsDebugLineInst()) {
AddOperandsToWorkList(&line_inst);
}
+ AddDebugScopeToWorkList(&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);
- }
+ AddDebugScopeToWorkList(inst);
}
void AggressiveDCEPass::AddDecorationsToWorkList(const Instruction* inst) {
@@ -630,6 +632,16 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
auto dbg_none = context()->get_debug_info_mgr()->GetDebugInfoNone();
AddToWorklist(dbg_none);
}
+
+ // Add top level DebugInfo to worklist
+ for (auto& dbg : get_module()->ext_inst_debuginfo()) {
+ auto op = dbg.GetShader100DebugOpcode();
+ if (op == NonSemanticShaderDebugInfo100DebugCompilationUnit ||
+ op == NonSemanticShaderDebugInfo100DebugEntryPoint ||
+ op == NonSemanticShaderDebugInfo100DebugSourceContinued) {
+ AddToWorklist(&dbg);
+ }
+ }
}
Pass::Status AggressiveDCEPass::ProcessImpl() {
@@ -659,9 +671,14 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
InitializeModuleScopeLiveInstructions();
- // Process all entry point functions.
- ProcessFunction pfn = [this](Function* fp) { return AggressiveDCE(fp); };
- modified |= context()->ProcessReachableCallTree(pfn);
+ // Run |AggressiveDCE| on the remaining functions. The order does not matter,
+ // since |AggressiveDCE| is intra-procedural. This can mean that function
+ // will become dead if all function call to them are removed. These dead
+ // function will still be in the module after this pass. We expect this to be
+ // rare.
+ for (Function& fp : *context()->module()) {
+ modified |= AggressiveDCE(&fp);
+ }
// If the decoration manager is kept live then the context will try to keep it
// up to date. ADCE deals with group decorations by changing the operands in
@@ -687,8 +704,9 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
}
// Cleanup all CFG including all unreachable blocks.
- ProcessFunction cleanup = [this](Function* f) { return CFGCleanup(f); };
- modified |= context()->ProcessReachableCallTree(cleanup);
+ for (Function& fp : *context()->module()) {
+ modified |= CFGCleanup(&fp);
+ }
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
@@ -967,6 +985,8 @@ void AggressiveDCEPass::InitExtensions() {
"SPV_KHR_integer_dot_product",
"SPV_EXT_shader_image_int64",
"SPV_KHR_non_semantic_info",
+ "SPV_KHR_uniform_group_instructions",
+ "SPV_KHR_fragment_shader_barycentric",
});
}
diff --git a/source/opt/aggressive_dead_code_elim_pass.h b/source/opt/aggressive_dead_code_elim_pass.h
index 1b3fd1e8..c1291dc4 100644
--- a/source/opt/aggressive_dead_code_elim_pass.h
+++ b/source/opt/aggressive_dead_code_elim_pass.h
@@ -178,6 +178,9 @@ class AggressiveDCEPass : public MemPass {
// Adds all decorations of |inst| to the work list.
void AddDecorationsToWorkList(const Instruction* inst);
+ // Adds DebugScope instruction associated with |inst| to the work list.
+ void AddDebugScopeToWorkList(const Instruction* inst);
+
// Adds all debug instruction associated with |inst| to the work list.
void AddDebugInstructionsToWorkList(const Instruction* inst);
diff --git a/source/opt/ccp_pass.cpp b/source/opt/ccp_pass.cpp
index 5099b477..5f855027 100644
--- a/source/opt/ccp_pass.cpp
+++ b/source/opt/ccp_pass.cpp
@@ -172,7 +172,8 @@ SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) {
if (folded_inst != nullptr) {
// We do not want to change the body of the function by adding new
// instructions. When folding we can only generate new constants.
- assert(folded_inst->IsConstant() &&
+ assert((folded_inst->IsConstant() ||
+ IsSpecConstantInst(folded_inst->opcode())) &&
"CCP is only interested in constant values.");
uint32_t new_val = ComputeLatticeMeet(instr, folded_inst->result_id());
values_[instr->result_id()] = new_val;
diff --git a/source/opt/cfg.cpp b/source/opt/cfg.cpp
index ac0fcc36..a0248d54 100644
--- a/source/opt/cfg.cpp
+++ b/source/opt/cfg.cpp
@@ -74,6 +74,12 @@ void CFG::RemoveNonExistingEdges(uint32_t blk_id) {
void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root,
std::list<BasicBlock*>* order) {
+ ComputeStructuredOrder(func, root, nullptr, order);
+}
+
+void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root,
+ BasicBlock* end,
+ std::list<BasicBlock*>* order) {
assert(module_->context()->get_feature_mgr()->HasCapability(
SpvCapabilityShader) &&
"This only works on structured control flow");
@@ -81,7 +87,8 @@ void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root,
// Compute structured successors and do DFS.
ComputeStructuredSuccessors(func);
auto ignore_block = [](cbb_ptr) {};
- auto ignore_edge = [](cbb_ptr, cbb_ptr) {};
+ auto terminal = [end](cbb_ptr bb) { return bb == end; };
+
auto get_structured_successors = [this](const BasicBlock* b) {
return &(block2structured_succs_[b]);
};
@@ -92,7 +99,7 @@ void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root,
order->push_front(const_cast<BasicBlock*>(b));
};
CFA<BasicBlock>::DepthFirstTraversal(root, get_structured_successors,
- ignore_block, post_order, ignore_edge);
+ ignore_block, post_order, terminal);
}
void CFG::ForEachBlockInPostOrder(BasicBlock* bb,
@@ -205,7 +212,7 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) {
// Find the back edge
BasicBlock* latch_block = nullptr;
Function::iterator latch_block_iter = header_it;
- while (++latch_block_iter != fn->end()) {
+ for (; latch_block_iter != fn->end(); ++latch_block_iter) {
// If blocks are in the proper order, then the only branch that appears
// after the header is the latch.
if (std::find(pred.begin(), pred.end(), latch_block_iter->id()) !=
@@ -237,6 +244,15 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) {
context->set_instr_block(inst, new_header);
});
+ // If |bb| was the latch block, the branch back to the header is not in
+ // |new_header|.
+ if (latch_block == bb) {
+ if (new_header->ContinueBlockId() == bb->id()) {
+ new_header->GetLoopMergeInst()->SetInOperand(1, {new_header_id});
+ }
+ latch_block = new_header;
+ }
+
// Adjust the OpPhi instructions as needed.
bb->ForEachPhiInst([latch_block, bb, new_header, context](Instruction* phi) {
std::vector<uint32_t> preheader_phi_ops;
diff --git a/source/opt/cfg.h b/source/opt/cfg.h
index 33412f18..fa4fef2d 100644
--- a/source/opt/cfg.h
+++ b/source/opt/cfg.h
@@ -66,6 +66,14 @@ class CFG {
void ComputeStructuredOrder(Function* func, BasicBlock* root,
std::list<BasicBlock*>* order);
+ // Compute structured block order into |order| for |func| starting at |root|
+ // and ending at |end|. This order has the property that dominators come
+ // before all blocks they dominate, merge blocks come after all blocks that
+ // are in the control constructs of their header, and continue blocks come
+ // after all the blocks in the body of their loop.
+ void ComputeStructuredOrder(Function* func, BasicBlock* root, BasicBlock* end,
+ std::list<BasicBlock*>* order);
+
// Applies |f| to all blocks that can be reach from |bb| in post order.
void ForEachBlockInPostOrder(BasicBlock* bb,
const std::function<void(BasicBlock*)>& f);
diff --git a/source/opt/compact_ids_pass.cpp b/source/opt/compact_ids_pass.cpp
index 8815b8c6..5a2a54b1 100644
--- a/source/opt/compact_ids_pass.cpp
+++ b/source/opt/compact_ids_pass.cpp
@@ -44,6 +44,11 @@ Pass::Status CompactIdsPass::Process() {
bool modified = false;
std::unordered_map<uint32_t, uint32_t> result_id_mapping;
+ // Disable automatic DebugInfo analysis for the life of the CompactIds pass.
+ // The DebugInfo manager requires the SPIR-V to be valid to run, but this is
+ // not true at all times in CompactIds as it remaps all ids.
+ context()->InvalidateAnalyses(IRContext::kAnalysisDebugInfo);
+
context()->module()->ForEachInst(
[&result_id_mapping, &modified](Instruction* inst) {
auto operand = inst->begin();
@@ -86,7 +91,8 @@ Pass::Status CompactIdsPass::Process() {
},
true);
- if (modified) {
+ if (context()->module()->id_bound() != result_id_mapping.size() + 1) {
+ modified = true;
context()->module()->SetIdBound(
static_cast<uint32_t>(result_id_mapping.size() + 1));
// There are ids in the feature manager that could now be invalid
diff --git a/source/opt/const_folding_rules.cpp b/source/opt/const_folding_rules.cpp
index 249e11e5..cb360874 100644
--- a/source/opt/const_folding_rules.cpp
+++ b/source/opt/const_folding_rules.cpp
@@ -251,6 +251,193 @@ ConstantFoldingRule FoldVectorTimesScalar() {
};
}
+ConstantFoldingRule FoldVectorTimesMatrix() {
+ return [](IRContext* context, Instruction* inst,
+ const std::vector<const analysis::Constant*>& constants)
+ -> const analysis::Constant* {
+ assert(inst->opcode() == SpvOpVectorTimesMatrix);
+ analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+ analysis::TypeManager* type_mgr = context->get_type_mgr();
+
+ if (!inst->IsFloatingPointFoldingAllowed()) {
+ if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) {
+ return nullptr;
+ }
+ }
+
+ const analysis::Constant* c1 = constants[0];
+ const analysis::Constant* c2 = constants[1];
+
+ if (c1 == nullptr || c2 == nullptr) {
+ return nullptr;
+ }
+
+ // Check result type.
+ const analysis::Type* result_type = type_mgr->GetType(inst->type_id());
+ const analysis::Vector* vector_type = result_type->AsVector();
+ assert(vector_type != nullptr);
+ const analysis::Type* element_type = vector_type->element_type();
+ assert(element_type != nullptr);
+ const analysis::Float* float_type = element_type->AsFloat();
+ assert(float_type != nullptr);
+
+ // Check types of c1 and c2.
+ assert(c1->type()->AsVector() == vector_type);
+ assert(c1->type()->AsVector()->element_type() == element_type &&
+ c2->type()->AsMatrix()->element_type() == vector_type);
+
+ // Get a float vector that is the result of vector-times-matrix.
+ std::vector<const analysis::Constant*> c1_components =
+ c1->GetVectorComponents(const_mgr);
+ std::vector<const analysis::Constant*> c2_components =
+ c2->AsMatrixConstant()->GetComponents();
+ uint32_t resultVectorSize = result_type->AsVector()->element_count();
+
+ std::vector<uint32_t> ids;
+
+ if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) {
+ std::vector<uint32_t> words(float_type->width() / 32, 0);
+ for (uint32_t i = 0; i < resultVectorSize; ++i) {
+ const analysis::Constant* new_elem =
+ const_mgr->GetConstant(float_type, words);
+ ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+ }
+ return const_mgr->GetConstant(vector_type, ids);
+ }
+
+ if (float_type->width() == 32) {
+ for (uint32_t i = 0; i < resultVectorSize; ++i) {
+ float result_scalar = 0.0f;
+ const analysis::VectorConstant* c2_vec =
+ c2_components[i]->AsVectorConstant();
+ for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) {
+ float c1_scalar = c1_components[j]->GetFloat();
+ float c2_scalar = c2_vec->GetComponents()[j]->GetFloat();
+ result_scalar += c1_scalar * c2_scalar;
+ }
+ utils::FloatProxy<float> result(result_scalar);
+ std::vector<uint32_t> words = result.GetWords();
+ const analysis::Constant* new_elem =
+ const_mgr->GetConstant(float_type, words);
+ ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+ }
+ return const_mgr->GetConstant(vector_type, ids);
+ } else if (float_type->width() == 64) {
+ for (uint32_t i = 0; i < c2_components.size(); ++i) {
+ double result_scalar = 0.0;
+ const analysis::VectorConstant* c2_vec =
+ c2_components[i]->AsVectorConstant();
+ for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) {
+ double c1_scalar = c1_components[j]->GetDouble();
+ double c2_scalar = c2_vec->GetComponents()[j]->GetDouble();
+ result_scalar += c1_scalar * c2_scalar;
+ }
+ utils::FloatProxy<double> result(result_scalar);
+ std::vector<uint32_t> words = result.GetWords();
+ const analysis::Constant* new_elem =
+ const_mgr->GetConstant(float_type, words);
+ ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+ }
+ return const_mgr->GetConstant(vector_type, ids);
+ }
+ return nullptr;
+ };
+}
+
+ConstantFoldingRule FoldMatrixTimesVector() {
+ return [](IRContext* context, Instruction* inst,
+ const std::vector<const analysis::Constant*>& constants)
+ -> const analysis::Constant* {
+ assert(inst->opcode() == SpvOpMatrixTimesVector);
+ analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+ analysis::TypeManager* type_mgr = context->get_type_mgr();
+
+ if (!inst->IsFloatingPointFoldingAllowed()) {
+ if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) {
+ return nullptr;
+ }
+ }
+
+ const analysis::Constant* c1 = constants[0];
+ const analysis::Constant* c2 = constants[1];
+
+ if (c1 == nullptr || c2 == nullptr) {
+ return nullptr;
+ }
+
+ // Check result type.
+ const analysis::Type* result_type = type_mgr->GetType(inst->type_id());
+ const analysis::Vector* vector_type = result_type->AsVector();
+ assert(vector_type != nullptr);
+ const analysis::Type* element_type = vector_type->element_type();
+ assert(element_type != nullptr);
+ const analysis::Float* float_type = element_type->AsFloat();
+ assert(float_type != nullptr);
+
+ // Check types of c1 and c2.
+ assert(c1->type()->AsMatrix()->element_type() == vector_type);
+ assert(c2->type()->AsVector()->element_type() == element_type);
+
+ // Get a float vector that is the result of matrix-times-vector.
+ std::vector<const analysis::Constant*> c1_components =
+ c1->AsMatrixConstant()->GetComponents();
+ std::vector<const analysis::Constant*> c2_components =
+ c2->GetVectorComponents(const_mgr);
+ uint32_t resultVectorSize = result_type->AsVector()->element_count();
+
+ std::vector<uint32_t> ids;
+
+ if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) {
+ std::vector<uint32_t> words(float_type->width() / 32, 0);
+ for (uint32_t i = 0; i < resultVectorSize; ++i) {
+ const analysis::Constant* new_elem =
+ const_mgr->GetConstant(float_type, words);
+ ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+ }
+ return const_mgr->GetConstant(vector_type, ids);
+ }
+
+ if (float_type->width() == 32) {
+ for (uint32_t i = 0; i < resultVectorSize; ++i) {
+ float result_scalar = 0.0f;
+ for (uint32_t j = 0; j < c1_components.size(); ++j) {
+ float c1_scalar = c1_components[j]
+ ->AsVectorConstant()
+ ->GetComponents()[i]
+ ->GetFloat();
+ float c2_scalar = c2_components[j]->GetFloat();
+ result_scalar += c1_scalar * c2_scalar;
+ }
+ utils::FloatProxy<float> result(result_scalar);
+ std::vector<uint32_t> words = result.GetWords();
+ const analysis::Constant* new_elem =
+ const_mgr->GetConstant(float_type, words);
+ ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+ }
+ return const_mgr->GetConstant(vector_type, ids);
+ } else if (float_type->width() == 64) {
+ for (uint32_t i = 0; i < resultVectorSize; ++i) {
+ double result_scalar = 0.0;
+ for (uint32_t j = 0; j < c1_components.size(); ++j) {
+ double c1_scalar = c1_components[j]
+ ->AsVectorConstant()
+ ->GetComponents()[i]
+ ->GetDouble();
+ double c2_scalar = c2_components[j]->GetDouble();
+ result_scalar += c1_scalar * c2_scalar;
+ }
+ utils::FloatProxy<double> result(result_scalar);
+ std::vector<uint32_t> words = result.GetWords();
+ const analysis::Constant* new_elem =
+ const_mgr->GetConstant(float_type, words);
+ ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+ }
+ return const_mgr->GetConstant(vector_type, ids);
+ }
+ return nullptr;
+ };
+}
+
ConstantFoldingRule FoldCompositeWithConstants() {
// Folds an OpCompositeConstruct where all of the inputs are constants to a
// constant. A new constant is created if necessary.
@@ -1288,6 +1475,8 @@ void ConstantFoldingRules::AddFoldingRules() {
rules_[SpvOpVectorShuffle].push_back(FoldVectorShuffleWithConstants());
rules_[SpvOpVectorTimesScalar].push_back(FoldVectorTimesScalar());
+ rules_[SpvOpVectorTimesMatrix].push_back(FoldVectorTimesMatrix());
+ rules_[SpvOpMatrixTimesVector].push_back(FoldMatrixTimesVector());
rules_[SpvOpFNegate].push_back(FoldFNegate());
rules_[SpvOpQuantizeToF16].push_back(FoldQuantizeToF16());
diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp
index d286cd26..bcff08c1 100644
--- a/source/opt/constants.cpp
+++ b/source/opt/constants.cpp
@@ -158,6 +158,7 @@ Type* ConstantManager::GetType(const Instruction* inst) const {
std::vector<const Constant*> ConstantManager::GetOperandConstants(
const Instruction* inst) const {
std::vector<const Constant*> constants;
+ constants.reserve(inst->NumInOperands());
for (uint32_t i = 0; i < inst->NumInOperands(); i++) {
const Operand* operand = &inst->GetInOperand(i);
if (operand->type != SPV_OPERAND_TYPE_ID) {
diff --git a/source/opt/copy_prop_arrays.cpp b/source/opt/copy_prop_arrays.cpp
index 62ed5e77..0b235629 100644
--- a/source/opt/copy_prop_arrays.cpp
+++ b/source/opt/copy_prop_arrays.cpp
@@ -151,9 +151,17 @@ Instruction* CopyPropagateArrays::BuildNewAccessChain(
return source->GetVariable();
}
+ source->BuildConstants();
+ std::vector<uint32_t> access_ids(source->AccessChain().size());
+ std::transform(
+ source->AccessChain().cbegin(), source->AccessChain().cend(),
+ access_ids.begin(), [](const AccessChainEntry& entry) {
+ assert(entry.is_result_id && "Constants needs to be built first.");
+ return entry.result_id;
+ });
+
return builder.AddAccessChain(source->GetPointerTypeId(this),
- source->GetVariable()->result_id(),
- source->AccessChain());
+ source->GetVariable()->result_id(), access_ids);
}
bool CopyPropagateArrays::HasNoStores(Instruction* ptr_inst) {
@@ -168,6 +176,8 @@ bool CopyPropagateArrays::HasNoStores(Instruction* ptr_inst) {
return false;
} else if (use->opcode() == SpvOpImageTexelPointer) {
return true;
+ } else if (use->opcode() == SpvOpEntryPoint) {
+ return true;
}
// Some other instruction. Be conservative.
return false;
@@ -268,30 +278,20 @@ std::unique_ptr<CopyPropagateArrays::MemoryObject>
CopyPropagateArrays::BuildMemoryObjectFromExtract(Instruction* extract_inst) {
assert(extract_inst->opcode() == SpvOpCompositeExtract &&
"Expecting an OpCompositeExtract instruction.");
- analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
-
std::unique_ptr<MemoryObject> result = GetSourceObjectIfAny(
extract_inst->GetSingleWordInOperand(kCompositeExtractObjectInOperand));
- if (result) {
- analysis::Integer int_type(32, false);
- const analysis::Type* uint32_type =
- context()->get_type_mgr()->GetRegisteredType(&int_type);
-
- std::vector<uint32_t> components;
- // Convert the indices in the extract instruction to a series of ids that
- // can be used by the |OpAccessChain| instruction.
- for (uint32_t i = 1; i < extract_inst->NumInOperands(); ++i) {
- uint32_t index = extract_inst->GetSingleWordInOperand(i);
- const analysis::Constant* index_const =
- const_mgr->GetConstant(uint32_type, {index});
- components.push_back(
- const_mgr->GetDefiningInstruction(index_const)->result_id());
- }
- result->GetMember(components);
- return result;
+ if (!result) {
+ return nullptr;
}
- return nullptr;
+
+ // Copy the indices of the extract instruction to |OpAccessChain| indices.
+ std::vector<AccessChainEntry> components;
+ for (uint32_t i = 1; i < extract_inst->NumInOperands(); ++i) {
+ components.push_back({false, {extract_inst->GetSingleWordInOperand(i)}});
+ }
+ result->PushIndirection(components);
+ return result;
}
std::unique_ptr<CopyPropagateArrays::MemoryObject>
@@ -315,19 +315,12 @@ CopyPropagateArrays::BuildMemoryObjectFromCompositeConstruct(
return nullptr;
}
- analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
- const analysis::Constant* last_access =
- const_mgr->FindDeclaredConstant(memory_object->AccessChain().back());
- if (!last_access || !last_access->type()->AsInteger()) {
+ AccessChainEntry last_access = memory_object->AccessChain().back();
+ if (!IsAccessChainIndexValidAndEqualTo(last_access, 0)) {
return nullptr;
}
- if (last_access->GetU32() != 0) {
- return nullptr;
- }
-
- memory_object->GetParent();
-
+ memory_object->PopIndirection();
if (memory_object->GetNumberOfMembers() !=
conststruct_inst->NumInOperands()) {
return nullptr;
@@ -349,13 +342,8 @@ CopyPropagateArrays::BuildMemoryObjectFromCompositeConstruct(
return nullptr;
}
- last_access =
- const_mgr->FindDeclaredConstant(member_object->AccessChain().back());
- if (!last_access || !last_access->type()->AsInteger()) {
- return nullptr;
- }
-
- if (last_access->GetU32() != i) {
+ last_access = member_object->AccessChain().back();
+ if (!IsAccessChainIndexValidAndEqualTo(last_access, i)) {
return nullptr;
}
}
@@ -409,17 +397,12 @@ CopyPropagateArrays::BuildMemoryObjectFromInsert(Instruction* insert_inst) {
return nullptr;
}
- const analysis::Constant* last_access =
- const_mgr->FindDeclaredConstant(memory_object->AccessChain().back());
- if (!last_access || !last_access->type()->AsInteger()) {
+ AccessChainEntry last_access = memory_object->AccessChain().back();
+ if (!IsAccessChainIndexValidAndEqualTo(last_access, number_of_elements - 1)) {
return nullptr;
}
- if (last_access->GetU32() != number_of_elements - 1) {
- return nullptr;
- }
-
- memory_object->GetParent();
+ memory_object->PopIndirection();
Instruction* current_insert =
def_use_mgr->GetDef(insert_inst->GetSingleWordInOperand(1));
@@ -456,14 +439,9 @@ CopyPropagateArrays::BuildMemoryObjectFromInsert(Instruction* insert_inst) {
return nullptr;
}
- const analysis::Constant* current_last_access =
- const_mgr->FindDeclaredConstant(
- current_memory_object->AccessChain().back());
- if (!current_last_access || !current_last_access->type()->AsInteger()) {
- return nullptr;
- }
-
- if (current_last_access->GetU32() != i - 1) {
+ AccessChainEntry current_last_access =
+ current_memory_object->AccessChain().back();
+ if (!IsAccessChainIndexValidAndEqualTo(current_last_access, i - 1)) {
return nullptr;
}
current_insert =
@@ -473,6 +451,21 @@ CopyPropagateArrays::BuildMemoryObjectFromInsert(Instruction* insert_inst) {
return memory_object;
}
+bool CopyPropagateArrays::IsAccessChainIndexValidAndEqualTo(
+ const AccessChainEntry& entry, uint32_t value) const {
+ if (!entry.is_result_id) {
+ return entry.immediate == value;
+ }
+
+ analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
+ const analysis::Constant* constant =
+ const_mgr->FindDeclaredConstant(entry.result_id);
+ if (!constant || !constant->type()->AsInteger()) {
+ return false;
+ }
+ return constant->GetU32() == value;
+}
+
bool CopyPropagateArrays::IsPointerToArrayType(uint32_t type_id) {
analysis::TypeManager* type_mgr = context()->get_type_mgr();
analysis::Pointer* pointer_type = type_mgr->GetType(type_id)->AsPointer();
@@ -530,6 +523,12 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst,
// Variable index means the type is a type where every element
// is the same type. Use element 0 to get the type.
access_chain.push_back(0);
+
+ // We are trying to access a struct with variable indices.
+ // This cannot happen.
+ if (pointee_type->kind() == analysis::Type::kStruct) {
+ return false;
+ }
}
}
@@ -745,11 +744,11 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst,
context()->AnalyzeUses(use);
}
break;
+ case SpvOpDecorate:
+ // We treat an OpImageTexelPointer as a load. The result type should
+ // always have the Image storage class, and should not need to be
+ // updated.
case SpvOpImageTexelPointer:
- // We treat an OpImageTexelPointer as a load. The result type should
- // always have the Image storage class, and should not need to be
- // updated.
-
// Replace the actual use.
context()->ForgetUses(use);
use->SetOperand(index, {new_ptr_inst->result_id()});
@@ -785,8 +784,8 @@ uint32_t CopyPropagateArrays::GetMemberTypeId(
return id;
}
-void CopyPropagateArrays::MemoryObject::GetMember(
- const std::vector<uint32_t>& access_chain) {
+void CopyPropagateArrays::MemoryObject::PushIndirection(
+ const std::vector<AccessChainEntry>& access_chain) {
access_chain_.insert(access_chain_.end(), access_chain.begin(),
access_chain.end());
}
@@ -821,23 +820,29 @@ uint32_t CopyPropagateArrays::MemoryObject::GetNumberOfMembers() {
template <class iterator>
CopyPropagateArrays::MemoryObject::MemoryObject(Instruction* var_inst,
iterator begin, iterator end)
- : variable_inst_(var_inst), access_chain_(begin, end) {}
+ : variable_inst_(var_inst) {
+ std::transform(begin, end, std::back_inserter(access_chain_),
+ [](uint32_t id) {
+ return AccessChainEntry{true, {id}};
+ });
+}
std::vector<uint32_t> CopyPropagateArrays::MemoryObject::GetAccessIds() const {
analysis::ConstantManager* const_mgr =
variable_inst_->context()->get_constant_mgr();
- std::vector<uint32_t> access_indices;
- for (uint32_t id : AccessChain()) {
- const analysis::Constant* element_index_const =
- const_mgr->FindDeclaredConstant(id);
- if (!element_index_const) {
- access_indices.push_back(0);
- } else {
- access_indices.push_back(element_index_const->GetU32());
- }
- }
- return access_indices;
+ std::vector<uint32_t> indices(AccessChain().size());
+ std::transform(AccessChain().cbegin(), AccessChain().cend(), indices.begin(),
+ [&const_mgr](const AccessChainEntry& entry) {
+ if (entry.is_result_id) {
+ const analysis::Constant* constant =
+ const_mgr->FindDeclaredConstant(entry.result_id);
+ return constant == nullptr ? 0 : constant->GetU32();
+ }
+
+ return entry.immediate;
+ });
+ return indices;
}
bool CopyPropagateArrays::MemoryObject::Contains(
@@ -858,5 +863,24 @@ bool CopyPropagateArrays::MemoryObject::Contains(
return true;
}
+void CopyPropagateArrays::MemoryObject::BuildConstants() {
+ for (auto& entry : access_chain_) {
+ if (entry.is_result_id) {
+ continue;
+ }
+
+ auto context = variable_inst_->context();
+ analysis::Integer int_type(32, false);
+ const analysis::Type* uint32_type =
+ context->get_type_mgr()->GetRegisteredType(&int_type);
+ analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+ const analysis::Constant* index_const =
+ const_mgr->GetConstant(uint32_type, {entry.immediate});
+ entry.result_id =
+ const_mgr->GetDefiningInstruction(index_const)->result_id();
+ entry.is_result_id = true;
+ }
+}
+
} // namespace opt
} // namespace spvtools
diff --git a/source/opt/copy_prop_arrays.h b/source/opt/copy_prop_arrays.h
index f4314a74..9e7641f6 100644
--- a/source/opt/copy_prop_arrays.h
+++ b/source/opt/copy_prop_arrays.h
@@ -35,7 +35,7 @@ namespace opt {
//
// The hard part is keeping all of the types correct. We do not want to
// have to do too large a search to update everything, which may not be
-// possible, do we give up if we see any instruction that might be hard to
+// possible, so we give up if we see any instruction that might be hard to
// update.
class CopyPropagateArrays : public MemPass {
@@ -52,6 +52,22 @@ class CopyPropagateArrays : public MemPass {
}
private:
+ // Represents one index in the OpAccessChain instruction. It can be either
+ // an instruction's result_id (OpConstant by ex), or a immediate value.
+ // Immediate values are used to prepare the final access chain without
+ // creating OpConstant instructions until done.
+ struct AccessChainEntry {
+ bool is_result_id;
+ union {
+ uint32_t result_id;
+ uint32_t immediate;
+ };
+
+ bool operator!=(const AccessChainEntry& other) const {
+ return other.is_result_id != is_result_id || other.result_id != result_id;
+ }
+ };
+
// The class used to identify a particular memory object. This memory object
// will be owned by a particular variable, meaning that the memory is part of
// that variable. It could be the entire variable or a member of the
@@ -70,12 +86,12 @@ class CopyPropagateArrays : public MemPass {
// (starting from the current member). The elements in |access_chain| are
// interpreted the same as the indices in the |OpAccessChain|
// instruction.
- void GetMember(const std::vector<uint32_t>& access_chain);
+ void PushIndirection(const std::vector<AccessChainEntry>& access_chain);
// Change |this| to now represent the first enclosing object to which it
// belongs. (Remove the last element off the access_chain). It is invalid
// to call this function if |this| does not represent a member of its owner.
- void GetParent() {
+ void PopIndirection() {
assert(IsMember());
access_chain_.pop_back();
}
@@ -95,7 +111,13 @@ class CopyPropagateArrays : public MemPass {
// member that |this| represents starting from the owning variable. These
// values are to be interpreted the same way the indices are in an
// |OpAccessChain| instruction.
- const std::vector<uint32_t>& AccessChain() const { return access_chain_; }
+ const std::vector<AccessChainEntry>& AccessChain() const {
+ return access_chain_;
+ }
+
+ // Converts all immediate values in the AccessChain their OpConstant
+ // equivalent.
+ void BuildConstants();
// Returns the type id of the pointer type that can be used to point to this
// memory object.
@@ -137,7 +159,7 @@ class CopyPropagateArrays : public MemPass {
// The access chain to reach the particular member the memory object
// represents. It should be interpreted the same way the indices in an
// |OpAccessChain| are interpreted.
- std::vector<uint32_t> access_chain_;
+ std::vector<AccessChainEntry> access_chain_;
std::vector<uint32_t> GetAccessIds() const;
};
@@ -192,10 +214,14 @@ class CopyPropagateArrays : public MemPass {
std::unique_ptr<MemoryObject> BuildMemoryObjectFromInsert(
Instruction* insert_inst);
+ // Return true if the given entry can represent the given value.
+ bool IsAccessChainIndexValidAndEqualTo(const AccessChainEntry& entry,
+ uint32_t value) const;
+
// Return true if |type_id| is a pointer type whose pointee type is an array.
bool IsPointerToArrayType(uint32_t type_id);
- // Returns true of there are not stores using |ptr_inst| or something derived
+ // Returns true if there are not stores using |ptr_inst| or something derived
// from it.
bool HasNoStores(Instruction* ptr_inst);
diff --git a/source/opt/dead_branch_elim_pass.cpp b/source/opt/dead_branch_elim_pass.cpp
index 356dbcb3..d99b7f78 100644
--- a/source/opt/dead_branch_elim_pass.cpp
+++ b/source/opt/dead_branch_elim_pass.cpp
@@ -207,7 +207,7 @@ bool DeadBranchElimPass::SimplifyBranch(BasicBlock* block,
Instruction::OperandList new_operands;
new_operands.push_back(terminator->GetInOperand(0));
new_operands.push_back({SPV_OPERAND_TYPE_ID, {live_lab_id}});
- terminator->SetInOperands(move(new_operands));
+ terminator->SetInOperands(std::move(new_operands));
context()->UpdateDefUse(terminator);
} else {
// Check if the merge instruction is still needed because of a
@@ -459,17 +459,8 @@ void DeadBranchElimPass::FixBlockOrder() {
};
// Reorders blocks according to structured order.
- ProcessFunction reorder_structured = [this](Function* function) {
- std::list<BasicBlock*> order;
- context()->cfg()->ComputeStructuredOrder(function, &*function->begin(),
- &order);
- std::vector<BasicBlock*> blocks;
- for (auto block : order) {
- blocks.push_back(block);
- }
- for (uint32_t i = 1; i < blocks.size(); ++i) {
- function->MoveBasicBlockToAfter(blocks[i]->id(), blocks[i - 1]);
- }
+ ProcessFunction reorder_structured = [](Function* function) {
+ function->ReorderBasicBlocksInStructuredOrder();
return true;
};
diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp
index c1df6258..5ec0efb8 100644
--- a/source/opt/debug_info_manager.cpp
+++ b/source/opt/debug_info_manager.cpp
@@ -1,4 +1,5 @@
-// Copyright (c) 2020 Google LLC
+// Copyright (c) 2020-2022 Google LLC
+// Copyright (c) 2022 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -24,6 +25,8 @@
static const uint32_t kOpLineOperandLineIndex = 1;
static const uint32_t kLineOperandIndexDebugFunction = 7;
static const uint32_t kLineOperandIndexDebugLexicalBlock = 5;
+static const uint32_t kLineOperandIndexDebugLine = 5;
+static const uint32_t kConstanstOperandIndexLiteral = 2;
static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
static const uint32_t kDebugFunctionDefinitionOperandDebugFunctionIndex = 4;
static const uint32_t kDebugFunctionDefinitionOperandOpFunctionIndex = 5;
@@ -210,7 +213,20 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
break;
}
} else {
- line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex);
+ if (line->opcode() == SpvOpLine) {
+ line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex);
+ } else if (line->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugLine) {
+ auto const line_number_id =
+ line->GetSingleWordOperand(kLineOperandIndexDebugLine);
+ auto const line_number_inst =
+ context()->get_def_use_mgr()->GetDef(line_number_id);
+ line_number =
+ line_number_inst->GetSingleWordOperand(kConstanstOperandIndexLiteral);
+ } else {
+ assert(false &&
+ "Unreachable. A line instruction must be OpLine or DebugLine");
+ }
// If we need the line number as an ID, generate that constant now.
// If Constant or DefUse managers are invalid, generate constant
@@ -546,10 +562,10 @@ bool DebugInfoManager::IsDeclareVisibleToInstr(Instruction* dbg_declare,
return false;
}
-bool DebugInfoManager::AddDebugValueIfVarDeclIsVisible(
- Instruction* scope_and_line, uint32_t variable_id, uint32_t value_id,
- Instruction* insert_pos,
- std::unordered_set<Instruction*>* invisible_decls) {
+bool DebugInfoManager::AddDebugValueForVariable(Instruction* scope_and_line,
+ uint32_t variable_id,
+ uint32_t value_id,
+ Instruction* insert_pos) {
assert(scope_and_line != nullptr);
auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
@@ -557,11 +573,6 @@ bool DebugInfoManager::AddDebugValueIfVarDeclIsVisible(
bool modified = false;
for (auto* dbg_decl_or_val : dbg_decl_itr->second) {
- if (!IsDeclareVisibleToInstr(dbg_decl_or_val, scope_and_line)) {
- if (invisible_decls) invisible_decls->insert(dbg_decl_or_val);
- continue;
- }
-
// Avoid inserting the new DebugValue between OpPhi or OpVariable
// instructions.
Instruction* insert_before = insert_pos->NextNode();
@@ -849,7 +860,7 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
fn_id_to_dbg_fn_.erase(fn_id);
}
if (instr->GetShader100DebugOpcode() ==
- NonSemanticShaderDebugInfo100DebugFunction) {
+ NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
auto fn_id = instr->GetSingleWordOperand(
kDebugFunctionDefinitionOperandOpFunctionIndex);
fn_id_to_dbg_fn_.erase(fn_id);
diff --git a/source/opt/debug_info_manager.h b/source/opt/debug_info_manager.h
index df34b30f..abb7b9a0 100644
--- a/source/opt/debug_info_manager.h
+++ b/source/opt/debug_info_manager.h
@@ -15,6 +15,7 @@
#ifndef SOURCE_OPT_DEBUG_INFO_MANAGER_H_
#define SOURCE_OPT_DEBUG_INFO_MANAGER_H_
+#include <set>
#include <unordered_map>
#include <unordered_set>
@@ -144,12 +145,10 @@ class DebugInfoManager {
// Generates a DebugValue instruction with value |value_id| for every local
// variable that is in the scope of |scope_and_line| and whose memory is
// |variable_id| and inserts it after the instruction |insert_pos|.
- // Returns whether a DebugValue is added or not. |invisible_decls| returns
- // DebugDeclares invisible to |scope_and_line|.
- bool AddDebugValueIfVarDeclIsVisible(
- Instruction* scope_and_line, uint32_t variable_id, uint32_t value_id,
- Instruction* insert_pos,
- std::unordered_set<Instruction*>* invisible_decls);
+ // Returns whether a DebugValue is added or not.
+ bool AddDebugValueForVariable(Instruction* scope_and_line,
+ uint32_t variable_id, uint32_t value_id,
+ Instruction* insert_pos);
// Creates a DebugValue for DebugDeclare |dbg_decl| and inserts it before
// |insert_before|. The new DebugValue has the same line and scope as
@@ -244,9 +243,18 @@ class DebugInfoManager {
// operand is the function.
std::unordered_map<uint32_t, Instruction*> fn_id_to_dbg_fn_;
+ // Orders Instruction* for use in associative containers (i.e. less than
+ // ordering). Unique Id is used.
+ typedef Instruction* InstPtr;
+ struct InstPtrLess {
+ bool operator()(const InstPtr& lhs, const InstPtr& rhs) const {
+ return lhs->unique_id() < rhs->unique_id();
+ }
+ };
+
// Mapping from variable or value ids to DebugDeclare or DebugValue
// instructions whose operand is the variable or value.
- std::unordered_map<uint32_t, std::unordered_set<Instruction*>>
+ std::unordered_map<uint32_t, std::set<InstPtr, InstPtrLess>>
var_id_to_dbg_decl_;
// Mapping from DebugScope ids to users.
diff --git a/source/opt/dominator_tree.cpp b/source/opt/dominator_tree.cpp
index d86de151..2680be2a 100644
--- a/source/opt/dominator_tree.cpp
+++ b/source/opt/dominator_tree.cpp
@@ -57,9 +57,9 @@ template <typename BBType, typename SuccessorLambda, typename PreLambda,
typename PostLambda>
static void DepthFirstSearch(const BBType* bb, SuccessorLambda successors,
PreLambda pre, PostLambda post) {
- // Ignore backedge operation.
- auto nop_backedge = [](const BBType*, const BBType*) {};
- CFA<BBType>::DepthFirstTraversal(bb, successors, pre, post, nop_backedge);
+ auto no_terminal_blocks = [](const BBType*) { return false; };
+ CFA<BBType>::DepthFirstTraversal(bb, successors, pre, post,
+ no_terminal_blocks);
}
// Wrapper around CFA::DepthFirstTraversal to provide an interface to perform
@@ -103,7 +103,8 @@ class BasicBlockSuccessorHelper {
using Function = typename GetFunctionClass<BBType>::FunctionType;
using BasicBlockListTy = std::vector<BasicBlock*>;
- using BasicBlockMapTy = std::map<const BasicBlock*, BasicBlockListTy>;
+ using BasicBlockMapTy =
+ std::unordered_map<const BasicBlock*, BasicBlockListTy>;
public:
// For compliance with the dominance tree computation, entry nodes are
@@ -158,19 +159,7 @@ BasicBlockSuccessorHelper<BBType>::BasicBlockSuccessorHelper(
template <typename BBType>
void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap(
Function& f, const BasicBlock* placeholder_start_node) {
- std::map<uint32_t, BasicBlock*> id_to_BB_map;
- auto GetSuccessorBasicBlock = [&f, &id_to_BB_map](uint32_t successor_id) {
- BasicBlock*& Succ = id_to_BB_map[successor_id];
- if (!Succ) {
- for (BasicBlock& BBIt : f) {
- if (successor_id == BBIt.id()) {
- Succ = &BBIt;
- break;
- }
- }
- }
- return Succ;
- };
+ IRContext* context = f.DefInst().context();
if (invert_graph_) {
// For the post dominator tree, we see the inverted graph.
@@ -184,9 +173,8 @@ void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap(
BasicBlockListTy& pred_list = predecessors_[&bb];
const auto& const_bb = bb;
const_bb.ForEachSuccessorLabel(
- [this, &pred_list, &bb,
- &GetSuccessorBasicBlock](const uint32_t successor_id) {
- BasicBlock* succ = GetSuccessorBasicBlock(successor_id);
+ [this, &pred_list, &bb, context](const uint32_t successor_id) {
+ BasicBlock* succ = context->get_instr_block(successor_id);
// Inverted graph: our successors in the CFG
// are our predecessors in the inverted graph.
this->successors_[succ].push_back(&bb);
@@ -207,7 +195,7 @@ void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap(
const auto& const_bb = bb;
const_bb.ForEachSuccessorLabel([&](const uint32_t successor_id) {
- BasicBlock* succ = GetSuccessorBasicBlock(successor_id);
+ BasicBlock* succ = context->get_instr_block(successor_id);
succ_list.push_back(succ);
predecessors_[succ].push_back(&bb);
});
diff --git a/source/opt/eliminate_dead_input_components_pass.cpp b/source/opt/eliminate_dead_input_components_pass.cpp
new file mode 100644
index 00000000..aa2776bb
--- /dev/null
+++ b/source/opt/eliminate_dead_input_components_pass.cpp
@@ -0,0 +1,189 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// 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/eliminate_dead_input_components_pass.h"
+
+#include <set>
+#include <vector>
+
+#include "source/opt/instruction.h"
+#include "source/opt/ir_builder.h"
+#include "source/opt/ir_context.h"
+#include "source/util/bit_vector.h"
+
+namespace {
+
+const uint32_t kAccessChainBaseInIdx = 0;
+const uint32_t kAccessChainIndex0InIdx = 1;
+const uint32_t kConstantValueInIdx = 0;
+const uint32_t kVariableStorageClassInIdx = 0;
+
+} // namespace
+
+namespace spvtools {
+namespace opt {
+
+Pass::Status EliminateDeadInputComponentsPass::Process() {
+ // Current functionality assumes shader capability
+ if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
+ return Status::SuccessWithoutChange;
+ analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+ analysis::TypeManager* type_mgr = context()->get_type_mgr();
+ bool modified = false;
+ std::vector<std::pair<Instruction*, unsigned>> arrays_to_change;
+ for (auto& var : context()->types_values()) {
+ if (var.opcode() != SpvOpVariable) {
+ continue;
+ }
+ analysis::Type* var_type = type_mgr->GetType(var.type_id());
+ analysis::Pointer* ptr_type = var_type->AsPointer();
+ if (ptr_type == nullptr) {
+ continue;
+ }
+ if (ptr_type->storage_class() != SpvStorageClassInput) {
+ continue;
+ }
+ const analysis::Array* arr_type = ptr_type->pointee_type()->AsArray();
+ if (arr_type != nullptr) {
+ unsigned arr_len_id = arr_type->LengthId();
+ Instruction* arr_len_inst = def_use_mgr->GetDef(arr_len_id);
+ if (arr_len_inst->opcode() != SpvOpConstant) {
+ continue;
+ }
+ // SPIR-V requires array size is >= 1, so this works for signed or
+ // unsigned size
+ unsigned original_max =
+ arr_len_inst->GetSingleWordInOperand(kConstantValueInIdx) - 1;
+ unsigned max_idx = FindMaxIndex(var, original_max);
+ if (max_idx != original_max) {
+ ChangeArrayLength(var, max_idx + 1);
+ modified = true;
+ }
+ continue;
+ }
+ const analysis::Struct* struct_type = ptr_type->pointee_type()->AsStruct();
+ if (struct_type == nullptr) continue;
+ const auto elt_types = struct_type->element_types();
+ unsigned original_max = static_cast<unsigned>(elt_types.size()) - 1;
+ unsigned max_idx = FindMaxIndex(var, original_max);
+ if (max_idx != original_max) {
+ ChangeStructLength(var, max_idx + 1);
+ modified = true;
+ }
+ }
+
+ return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+}
+
+unsigned EliminateDeadInputComponentsPass::FindMaxIndex(Instruction& var,
+ unsigned original_max) {
+ unsigned max = 0;
+ bool seen_non_const_ac = false;
+ assert(var.opcode() == SpvOpVariable && "must be variable");
+ context()->get_def_use_mgr()->WhileEachUser(
+ var.result_id(), [&max, &seen_non_const_ac, var, this](Instruction* use) {
+ auto use_opcode = use->opcode();
+ if (use_opcode == SpvOpLoad || use_opcode == SpvOpCopyMemory ||
+ use_opcode == SpvOpCopyMemorySized ||
+ use_opcode == SpvOpCopyObject) {
+ seen_non_const_ac = true;
+ return false;
+ }
+ if (use->opcode() != SpvOpAccessChain &&
+ use->opcode() != SpvOpInBoundsAccessChain) {
+ return true;
+ }
+ // OpAccessChain with no indices currently not optimized
+ if (use->NumInOperands() == 1) {
+ seen_non_const_ac = true;
+ return false;
+ }
+ unsigned base_id = use->GetSingleWordInOperand(kAccessChainBaseInIdx);
+ USE_ASSERT(base_id == var.result_id() && "unexpected base");
+ unsigned idx_id = use->GetSingleWordInOperand(kAccessChainIndex0InIdx);
+ Instruction* idx_inst = context()->get_def_use_mgr()->GetDef(idx_id);
+ if (idx_inst->opcode() != SpvOpConstant) {
+ seen_non_const_ac = true;
+ return false;
+ }
+ unsigned value = idx_inst->GetSingleWordInOperand(kConstantValueInIdx);
+ if (value > max) max = value;
+ return true;
+ });
+ return seen_non_const_ac ? original_max : max;
+}
+
+void EliminateDeadInputComponentsPass::ChangeArrayLength(Instruction& arr_var,
+ unsigned length) {
+ analysis::TypeManager* type_mgr = context()->get_type_mgr();
+ analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
+ analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+ analysis::Pointer* ptr_type =
+ type_mgr->GetType(arr_var.type_id())->AsPointer();
+ const analysis::Array* arr_ty = ptr_type->pointee_type()->AsArray();
+ assert(arr_ty && "expecting array type");
+ uint32_t length_id = const_mgr->GetUIntConst(length);
+ analysis::Array new_arr_ty(arr_ty->element_type(),
+ arr_ty->GetConstantLengthInfo(length_id, length));
+ analysis::Type* reg_new_arr_ty = type_mgr->GetRegisteredType(&new_arr_ty);
+ analysis::Pointer new_ptr_ty(reg_new_arr_ty, SpvStorageClassInput);
+ analysis::Type* reg_new_ptr_ty = type_mgr->GetRegisteredType(&new_ptr_ty);
+ uint32_t new_ptr_ty_id = type_mgr->GetTypeInstruction(reg_new_ptr_ty);
+ arr_var.SetResultType(new_ptr_ty_id);
+ def_use_mgr->AnalyzeInstUse(&arr_var);
+ // Move arr_var after its new type to preserve order
+ USE_ASSERT(arr_var.GetSingleWordInOperand(kVariableStorageClassInIdx) !=
+ SpvStorageClassFunction &&
+ "cannot move Function variable");
+ Instruction* new_ptr_ty_inst = def_use_mgr->GetDef(new_ptr_ty_id);
+ arr_var.RemoveFromList();
+ arr_var.InsertAfter(new_ptr_ty_inst);
+}
+
+void EliminateDeadInputComponentsPass::ChangeStructLength(
+ Instruction& struct_var, unsigned length) {
+ analysis::TypeManager* type_mgr = context()->get_type_mgr();
+ analysis::Pointer* ptr_type =
+ type_mgr->GetType(struct_var.type_id())->AsPointer();
+ const analysis::Struct* struct_ty = ptr_type->pointee_type()->AsStruct();
+ assert(struct_ty && "expecting struct type");
+ const auto orig_elt_types = struct_ty->element_types();
+ std::vector<const analysis::Type*> new_elt_types;
+ for (unsigned u = 0; u < length; ++u)
+ new_elt_types.push_back(orig_elt_types[u]);
+ analysis::Struct new_struct_ty(new_elt_types);
+ analysis::Type* reg_new_struct_ty =
+ type_mgr->GetRegisteredType(&new_struct_ty);
+ uint32_t new_struct_ty_id = type_mgr->GetTypeInstruction(reg_new_struct_ty);
+ uint32_t old_struct_ty_id = type_mgr->GetTypeInstruction(struct_ty);
+ analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr();
+ deco_mgr->CloneDecorations(old_struct_ty_id, new_struct_ty_id);
+ analysis::Pointer new_ptr_ty(reg_new_struct_ty, SpvStorageClassInput);
+ analysis::Type* reg_new_ptr_ty = type_mgr->GetRegisteredType(&new_ptr_ty);
+ uint32_t new_ptr_ty_id = type_mgr->GetTypeInstruction(reg_new_ptr_ty);
+ struct_var.SetResultType(new_ptr_ty_id);
+ analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+ def_use_mgr->AnalyzeInstUse(&struct_var);
+ // Move struct_var after its new type to preserve order
+ USE_ASSERT(struct_var.GetSingleWordInOperand(kVariableStorageClassInIdx) !=
+ SpvStorageClassFunction &&
+ "cannot move Function variable");
+ Instruction* new_ptr_ty_inst = def_use_mgr->GetDef(new_ptr_ty_id);
+ struct_var.RemoveFromList();
+ struct_var.InsertAfter(new_ptr_ty_inst);
+}
+
+} // namespace opt
+} // namespace spvtools
diff --git a/source/opt/eliminate_dead_input_components_pass.h b/source/opt/eliminate_dead_input_components_pass.h
new file mode 100644
index 00000000..a3a133c2
--- /dev/null
+++ b/source/opt/eliminate_dead_input_components_pass.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// 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_ELIMINATE_DEAD_INPUT_COMPONENTS_H_
+#define SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_
+
+#include <unordered_map>
+
+#include "source/opt/ir_context.h"
+#include "source/opt/module.h"
+#include "source/opt/pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// See optimizer.hpp for documentation.
+class EliminateDeadInputComponentsPass : public Pass {
+ public:
+ explicit EliminateDeadInputComponentsPass() {}
+
+ const char* name() const override {
+ return "eliminate-dead-input-components";
+ }
+
+ Status Process() override;
+
+ // Return the mask of preserved Analyses.
+ IRContext::Analysis GetPreservedAnalyses() override {
+ return IRContext::kAnalysisDefUse |
+ IRContext::kAnalysisInstrToBlockMapping |
+ IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG |
+ IRContext::kAnalysisDominatorAnalysis |
+ IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap |
+ IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
+ }
+
+ private:
+ // Find the max constant used to index the variable declared by |var|
+ // through OpAccessChain or OpInBoundsAccessChain. If any non-constant
+ // indices or non-Op*AccessChain use of |var|, return |original_max|.
+ unsigned FindMaxIndex(Instruction& var, unsigned original_max);
+
+ // Change the length of the array |inst| to |length|
+ void ChangeArrayLength(Instruction& inst, unsigned length);
+
+ // Change the length of the struct |struct_var| to |length|
+ void ChangeStructLength(Instruction& struct_var, unsigned length);
+};
+
+} // namespace opt
+} // namespace spvtools
+
+#endif // SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_
diff --git a/source/opt/fix_func_call_arguments.cpp b/source/opt/fix_func_call_arguments.cpp
new file mode 100644
index 00000000..d140fb4b
--- /dev/null
+++ b/source/opt/fix_func_call_arguments.cpp
@@ -0,0 +1,90 @@
+// Copyright (c) 2022 Advanced Micro Devices, Inc.
+//
+// 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 "fix_func_call_arguments.h"
+
+#include "ir_builder.h"
+
+using namespace spvtools;
+using namespace opt;
+
+bool FixFuncCallArgumentsPass::ModuleHasASingleFunction() {
+ auto funcsNum = get_module()->end() - get_module()->begin();
+ return funcsNum == 1;
+}
+
+Pass::Status FixFuncCallArgumentsPass::Process() {
+ bool modified = false;
+ if (ModuleHasASingleFunction()) return Status::SuccessWithoutChange;
+ for (auto& func : *get_module()) {
+ func.ForEachInst([this, &modified](Instruction* inst) {
+ if (inst->opcode() == SpvOpFunctionCall) {
+ modified |= FixFuncCallArguments(inst);
+ }
+ });
+ }
+ return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+}
+
+bool FixFuncCallArgumentsPass::FixFuncCallArguments(
+ Instruction* func_call_inst) {
+ bool modified = false;
+ for (uint32_t i = 0; i < func_call_inst->NumInOperands(); ++i) {
+ Operand& op = func_call_inst->GetInOperand(i);
+ if (op.type != SPV_OPERAND_TYPE_ID) continue;
+ Instruction* operand_inst = get_def_use_mgr()->GetDef(op.AsId());
+ if (operand_inst->opcode() == SpvOpAccessChain) {
+ uint32_t var_id =
+ ReplaceAccessChainFuncCallArguments(func_call_inst, operand_inst);
+ func_call_inst->SetInOperand(i, {var_id});
+ modified = true;
+ }
+ }
+ if (modified) {
+ context()->UpdateDefUse(func_call_inst);
+ }
+ return modified;
+}
+
+uint32_t FixFuncCallArgumentsPass::ReplaceAccessChainFuncCallArguments(
+ Instruction* func_call_inst, Instruction* operand_inst) {
+ InstructionBuilder builder(
+ context(), func_call_inst,
+ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+ Instruction* next_insert_point = func_call_inst->NextNode();
+ // Get Variable insertion point
+ Function* func = context()->get_instr_block(func_call_inst)->GetParent();
+ Instruction* variable_insertion_point = &*(func->begin()->begin());
+ Instruction* op_ptr_type = get_def_use_mgr()->GetDef(operand_inst->type_id());
+ Instruction* op_type =
+ get_def_use_mgr()->GetDef(op_ptr_type->GetSingleWordInOperand(1));
+ uint32_t varType = context()->get_type_mgr()->FindPointerToType(
+ op_type->result_id(), SpvStorageClassFunction);
+ // Create new variable
+ builder.SetInsertPoint(variable_insertion_point);
+ Instruction* var = builder.AddVariable(varType, SpvStorageClassFunction);
+ // Load access chain to the new variable before function call
+ builder.SetInsertPoint(func_call_inst);
+
+ uint32_t operand_id = operand_inst->result_id();
+ Instruction* load = builder.AddLoad(op_type->result_id(), operand_id);
+ builder.AddStore(var->result_id(), load->result_id());
+ // Load return value to the acesschain after function call
+ builder.SetInsertPoint(next_insert_point);
+ load = builder.AddLoad(op_type->result_id(), var->result_id());
+ builder.AddStore(operand_id, load->result_id());
+
+ return var->result_id();
+}
diff --git a/source/opt/fix_func_call_arguments.h b/source/opt/fix_func_call_arguments.h
new file mode 100644
index 00000000..15781b8c
--- /dev/null
+++ b/source/opt/fix_func_call_arguments.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2022 Advanced Micro Devices, Inc.
+//
+// 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 _VAR_FUNC_CALL_PASS_H
+#define _VAR_FUNC_CALL_PASS_H
+
+#include "source/opt/pass.h"
+
+namespace spvtools {
+namespace opt {
+class FixFuncCallArgumentsPass : public Pass {
+ public:
+ FixFuncCallArgumentsPass() {}
+ const char* name() const override { return "fix-for-funcall-param"; }
+ Status Process() override;
+ // Returns true if the module has one one function.
+ bool ModuleHasASingleFunction();
+ // Copies from the memory pointed to by |operand_inst| to a new function scope
+ // variable created before |func_call_inst|, and
+ // copies the value of the new variable back to the memory pointed to by
+ // |operand_inst| after |funct_call_inst| Returns the id of
+ // the new variable.
+ uint32_t ReplaceAccessChainFuncCallArguments(Instruction* func_call_inst,
+ Instruction* operand_inst);
+
+ // Fix function call |func_call_inst| non memory object arguments
+ bool FixFuncCallArguments(Instruction* func_call_inst);
+
+ IRContext::Analysis GetPreservedAnalyses() override {
+ return IRContext::kAnalysisTypes;
+ }
+};
+} // namespace opt
+} // namespace spvtools
+
+#endif // _VAR_FUNC_CALL_PASS_H \ No newline at end of file
diff --git a/source/opt/fold_spec_constant_op_and_composite_pass.cpp b/source/opt/fold_spec_constant_op_and_composite_pass.cpp
index 85f11fde..8d68850a 100644
--- a/source/opt/fold_spec_constant_op_and_composite_pass.cpp
+++ b/source/opt/fold_spec_constant_op_and_composite_pass.cpp
@@ -144,15 +144,6 @@ bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
return true;
}
-uint32_t FoldSpecConstantOpAndCompositePass::GetTypeComponent(
- uint32_t typeId, uint32_t element) const {
- Instruction* type = context()->get_def_use_mgr()->GetDef(typeId);
- uint32_t subtype = type->GetTypeComponent(element);
- assert(subtype != 0);
-
- return subtype;
-}
-
Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder(
Module::inst_iterator* inst_iter_ptr) {
// If one of operands to the instruction is not a
@@ -214,86 +205,6 @@ Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder(
return new_const_inst;
}
-Instruction* FoldSpecConstantOpAndCompositePass::DoVectorShuffle(
- Module::inst_iterator* pos) {
- Instruction* inst = &**pos;
- analysis::Vector* result_vec_type =
- context()->get_constant_mgr()->GetType(inst)->AsVector();
- assert(inst->NumInOperands() - 1 > 2 &&
- "OpSpecConstantOp DoVectorShuffle instruction requires more than 2 "
- "operands (2 vector ids and at least one literal operand");
- assert(result_vec_type &&
- "The result of VectorShuffle must be of type vector");
-
- // A temporary null constants that can be used as the components of the result
- // vector. This is needed when any one of the vector operands are null
- // constant.
- const analysis::Constant* null_component_constants = nullptr;
-
- // Get a concatenated vector of scalar constants. The vector should be built
- // with the components from the first and the second operand of VectorShuffle.
- std::vector<const analysis::Constant*> concatenated_components;
- // Note that for OpSpecConstantOp, the second in-operand is the first id
- // operand. The first in-operand is the spec opcode.
- for (uint32_t i : {1, 2}) {
- assert(inst->GetInOperand(i).type == SPV_OPERAND_TYPE_ID &&
- "The vector operand must have a SPV_OPERAND_TYPE_ID type");
- uint32_t operand_id = inst->GetSingleWordInOperand(i);
- auto operand_const =
- context()->get_constant_mgr()->FindDeclaredConstant(operand_id);
- if (!operand_const) return nullptr;
- const analysis::Type* operand_type = operand_const->type();
- assert(operand_type->AsVector() &&
- "The first two operand of VectorShuffle must be of vector type");
- if (auto vec_const = operand_const->AsVectorConstant()) {
- // case 1: current operand is a non-null vector constant.
- concatenated_components.insert(concatenated_components.end(),
- vec_const->GetComponents().begin(),
- vec_const->GetComponents().end());
- } else if (operand_const->AsNullConstant()) {
- // case 2: current operand is a null vector constant. Create a temporary
- // null scalar constant as the component.
- if (!null_component_constants) {
- const analysis::Type* component_type =
- operand_type->AsVector()->element_type();
- null_component_constants =
- context()->get_constant_mgr()->GetConstant(component_type, {});
- }
- // Append the null scalar consts to the concatenated components
- // vector.
- concatenated_components.insert(concatenated_components.end(),
- operand_type->AsVector()->element_count(),
- null_component_constants);
- } else {
- // no other valid cases
- return nullptr;
- }
- }
- // Create null component constants if there are any. The component constants
- // must be added to the module before the dependee composite constants to
- // satisfy SSA def-use dominance.
- if (null_component_constants) {
- context()->get_constant_mgr()->BuildInstructionAndAddToModule(
- null_component_constants, pos);
- }
- // Create the new vector constant with the selected components.
- std::vector<const analysis::Constant*> selected_components;
- for (uint32_t i = 3; i < inst->NumInOperands(); i++) {
- assert(inst->GetInOperand(i).type == SPV_OPERAND_TYPE_LITERAL_INTEGER &&
- "The literal operand must of type SPV_OPERAND_TYPE_LITERAL_INTEGER");
- uint32_t literal = inst->GetSingleWordInOperand(i);
- assert(literal < concatenated_components.size() &&
- "Literal index out of bound of the concatenated vector");
- selected_components.push_back(concatenated_components[literal]);
- }
- auto new_vec_const = MakeUnique<analysis::VectorConstant>(
- result_vec_type, selected_components);
- auto reg_vec_const =
- context()->get_constant_mgr()->RegisterConstant(std::move(new_vec_const));
- return context()->get_constant_mgr()->BuildInstructionAndAddToModule(
- reg_vec_const, pos);
-}
-
namespace {
// A helper function to check the type for component wise operations. Returns
// true if the type:
diff --git a/source/opt/fold_spec_constant_op_and_composite_pass.h b/source/opt/fold_spec_constant_op_and_composite_pass.h
index 361d3cac..9a8fb403 100644
--- a/source/opt/fold_spec_constant_op_and_composite_pass.h
+++ b/source/opt/fold_spec_constant_op_and_composite_pass.h
@@ -58,22 +58,11 @@ class FoldSpecConstantOpAndCompositePass : public Pass {
// |inst_iter_ptr| using the instruction folder.
Instruction* FoldWithInstructionFolder(Module::inst_iterator* inst_iter_ptr);
- // Try to fold the OpSpecConstantOp VectorShuffle instruction pointed by the
- // given instruction iterator to a normal constant defining instruction.
- // Returns the pointer to the new constant defining instruction if succeeded.
- // Otherwise return nullptr.
- Instruction* DoVectorShuffle(Module::inst_iterator* inst_iter_ptr);
-
// Try to fold the OpSpecConstantOp <component wise operations> instruction
// pointed by the given instruction iterator to a normal constant defining
// instruction. Returns the pointer to the new constant defining instruction
// if succeeded, otherwise return nullptr.
Instruction* DoComponentWiseOperation(Module::inst_iterator* inst_iter_ptr);
-
- // Returns the |element|'th subtype of |type|.
- //
- // |type| must be a composite type.
- uint32_t GetTypeComponent(uint32_t type, uint32_t element) const;
};
} // namespace opt
diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp
index 4904f186..3f10bd00 100644
--- a/source/opt/folding_rules.cpp
+++ b/source/opt/folding_rules.cpp
@@ -277,6 +277,11 @@ uint32_t Reciprocal(analysis::ConstantManager* const_mgr,
uint32_t width = c->type()->AsFloat()->width();
assert(width == 32 || width == 64);
std::vector<uint32_t> words;
+
+ if (c->IsZero()) {
+ return 0;
+ }
+
if (width == 64) {
spvtools::utils::FloatProxy<double> result(1.0 / c->GetDouble());
if (!IsValidResult(result.getAsFloat())) return 0;
@@ -1430,6 +1435,132 @@ FoldingRule FactorAddMuls() {
};
}
+// Replaces |inst| inplace with an FMA instruction |(x*y)+a|.
+void ReplaceWithFma(Instruction* inst, uint32_t x, uint32_t y, uint32_t a) {
+ uint32_t ext =
+ inst->context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+
+ if (ext == 0) {
+ inst->context()->AddExtInstImport("GLSL.std.450");
+ ext = inst->context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+ assert(ext != 0 &&
+ "Could not add the GLSL.std.450 extended instruction set");
+ }
+
+ std::vector<Operand> operands;
+ operands.push_back({SPV_OPERAND_TYPE_ID, {ext}});
+ operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {GLSLstd450Fma}});
+ operands.push_back({SPV_OPERAND_TYPE_ID, {x}});
+ operands.push_back({SPV_OPERAND_TYPE_ID, {y}});
+ operands.push_back({SPV_OPERAND_TYPE_ID, {a}});
+
+ inst->SetOpcode(SpvOpExtInst);
+ inst->SetInOperands(std::move(operands));
+}
+
+// Folds a multiple and add into an Fma.
+//
+// Cases:
+// (x * y) + a = Fma x y a
+// a + (x * y) = Fma x y a
+bool MergeMulAddArithmetic(IRContext* context, Instruction* inst,
+ const std::vector<const analysis::Constant*>&) {
+ assert(inst->opcode() == SpvOpFAdd);
+
+ if (!inst->IsFloatingPointFoldingAllowed()) {
+ return false;
+ }
+
+ analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+ for (int i = 0; i < 2; i++) {
+ uint32_t op_id = inst->GetSingleWordInOperand(i);
+ Instruction* op_inst = def_use_mgr->GetDef(op_id);
+
+ if (op_inst->opcode() != SpvOpFMul) {
+ continue;
+ }
+
+ if (!op_inst->IsFloatingPointFoldingAllowed()) {
+ continue;
+ }
+
+ uint32_t x = op_inst->GetSingleWordInOperand(0);
+ uint32_t y = op_inst->GetSingleWordInOperand(1);
+ uint32_t a = inst->GetSingleWordInOperand((i + 1) % 2);
+ ReplaceWithFma(inst, x, y, a);
+ return true;
+ }
+ return false;
+}
+
+// Replaces |sub| inplace with an FMA instruction |(x*y)+a| where |a| first gets
+// negated if |negate_addition| is true, otherwise |x| gets negated.
+void ReplaceWithFmaAndNegate(Instruction* sub, uint32_t x, uint32_t y,
+ uint32_t a, bool negate_addition) {
+ uint32_t ext =
+ sub->context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+
+ if (ext == 0) {
+ sub->context()->AddExtInstImport("GLSL.std.450");
+ ext = sub->context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+ assert(ext != 0 &&
+ "Could not add the GLSL.std.450 extended instruction set");
+ }
+
+ InstructionBuilder ir_builder(
+ sub->context(), sub,
+ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+ Instruction* neg = ir_builder.AddUnaryOp(sub->type_id(), SpvOpFNegate,
+ negate_addition ? a : x);
+ uint32_t neg_op = neg->result_id(); // -a : -x
+
+ std::vector<Operand> operands;
+ operands.push_back({SPV_OPERAND_TYPE_ID, {ext}});
+ operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {GLSLstd450Fma}});
+ operands.push_back({SPV_OPERAND_TYPE_ID, {negate_addition ? x : neg_op}});
+ operands.push_back({SPV_OPERAND_TYPE_ID, {y}});
+ operands.push_back({SPV_OPERAND_TYPE_ID, {negate_addition ? neg_op : a}});
+
+ sub->SetOpcode(SpvOpExtInst);
+ sub->SetInOperands(std::move(operands));
+}
+
+// Folds a multiply and subtract into an Fma and negation.
+//
+// Cases:
+// (x * y) - a = Fma x y -a
+// a - (x * y) = Fma -x y a
+bool MergeMulSubArithmetic(IRContext* context, Instruction* sub,
+ const std::vector<const analysis::Constant*>&) {
+ assert(sub->opcode() == SpvOpFSub);
+
+ if (!sub->IsFloatingPointFoldingAllowed()) {
+ return false;
+ }
+
+ analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+ for (int i = 0; i < 2; i++) {
+ uint32_t op_id = sub->GetSingleWordInOperand(i);
+ Instruction* mul = def_use_mgr->GetDef(op_id);
+
+ if (mul->opcode() != SpvOpFMul) {
+ continue;
+ }
+
+ if (!mul->IsFloatingPointFoldingAllowed()) {
+ continue;
+ }
+
+ uint32_t x = mul->GetSingleWordInOperand(0);
+ uint32_t y = mul->GetSingleWordInOperand(1);
+ uint32_t a = sub->GetSingleWordInOperand((i + 1) % 2);
+ ReplaceWithFmaAndNegate(sub, x, y, a, i == 0);
+ return true;
+ }
+ return false;
+}
+
FoldingRule IntMultipleBy1() {
return [](IRContext*, Instruction* inst,
const std::vector<const analysis::Constant*>& constants) {
@@ -1573,6 +1704,57 @@ bool CompositeConstructFeedingExtract(
return true;
}
+// Walks the indexes chain from |start| to |end| of an OpCompositeInsert or
+// OpCompositeExtract instruction, and returns the type of the final element
+// being accessed.
+const analysis::Type* GetElementType(uint32_t type_id,
+ Instruction::iterator start,
+ Instruction::iterator end,
+ const analysis::TypeManager* type_mgr) {
+ const analysis::Type* type = type_mgr->GetType(type_id);
+ for (auto index : make_range(std::move(start), std::move(end))) {
+ assert(index.type == SPV_OPERAND_TYPE_LITERAL_INTEGER &&
+ index.words.size() == 1);
+ if (auto* array_type = type->AsArray()) {
+ type = array_type->element_type();
+ } else if (auto* matrix_type = type->AsMatrix()) {
+ type = matrix_type->element_type();
+ } else if (auto* struct_type = type->AsStruct()) {
+ type = struct_type->element_types()[index.words[0]];
+ } else {
+ type = nullptr;
+ }
+ }
+ return type;
+}
+
+// Returns true of |inst_1| and |inst_2| have the same indexes that will be used
+// to index into a composite object, excluding the last index. The two
+// instructions must have the same opcode, and be either OpCompositeExtract or
+// OpCompositeInsert instructions.
+bool HaveSameIndexesExceptForLast(Instruction* inst_1, Instruction* inst_2) {
+ assert(inst_1->opcode() == inst_2->opcode() &&
+ "Expecting the opcodes to be the same.");
+ assert((inst_1->opcode() == SpvOpCompositeInsert ||
+ inst_1->opcode() == SpvOpCompositeExtract) &&
+ "Instructions must be OpCompositeInsert or OpCompositeExtract.");
+
+ if (inst_1->NumInOperands() != inst_2->NumInOperands()) {
+ return false;
+ }
+
+ uint32_t first_index_position =
+ (inst_1->opcode() == SpvOpCompositeInsert ? 2 : 1);
+ for (uint32_t i = first_index_position; i < inst_1->NumInOperands() - 1;
+ i++) {
+ if (inst_1->GetSingleWordInOperand(i) !=
+ inst_2->GetSingleWordInOperand(i)) {
+ return false;
+ }
+ }
+ return true;
+}
+
// If the OpCompositeConstruct is simply putting back together elements that
// where extracted from the same source, we can simply reuse the source.
//
@@ -1595,19 +1777,24 @@ bool CompositeExtractFeedingConstruct(
// - extractions
// - extracting the same position they are inserting
// - all extract from the same id.
+ Instruction* first_element_inst = nullptr;
for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
const uint32_t element_id = inst->GetSingleWordInOperand(i);
Instruction* element_inst = def_use_mgr->GetDef(element_id);
+ if (first_element_inst == nullptr) {
+ first_element_inst = element_inst;
+ }
if (element_inst->opcode() != SpvOpCompositeExtract) {
return false;
}
- if (element_inst->NumInOperands() != 2) {
+ if (!HaveSameIndexesExceptForLast(element_inst, first_element_inst)) {
return false;
}
- if (element_inst->GetSingleWordInOperand(1) != i) {
+ if (element_inst->GetSingleWordInOperand(element_inst->NumInOperands() -
+ 1) != i) {
return false;
}
@@ -1623,13 +1810,31 @@ bool CompositeExtractFeedingConstruct(
// The last check it to see that the object being extracted from is the
// correct type.
Instruction* original_inst = def_use_mgr->GetDef(original_id);
- if (original_inst->type_id() != inst->type_id()) {
+ analysis::TypeManager* type_mgr = context->get_type_mgr();
+ const analysis::Type* original_type =
+ GetElementType(original_inst->type_id(), first_element_inst->begin() + 3,
+ first_element_inst->end() - 1, type_mgr);
+
+ if (original_type == nullptr) {
return false;
}
- // Simplify by using the original object.
- inst->SetOpcode(SpvOpCopyObject);
- inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}});
+ if (inst->type_id() != type_mgr->GetId(original_type)) {
+ return false;
+ }
+
+ if (first_element_inst->NumInOperands() == 2) {
+ // Simplify by using the original object.
+ inst->SetOpcode(SpvOpCopyObject);
+ inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}});
+ return true;
+ }
+
+ // Copies the original id and all indexes except for the last to the new
+ // extract instruction.
+ inst->SetOpcode(SpvOpCompositeExtract);
+ inst->SetInOperands(std::vector<Operand>(first_element_inst->begin() + 2,
+ first_element_inst->end() - 1));
return true;
}
@@ -1833,6 +2038,139 @@ FoldingRule FMixFeedingExtract() {
};
}
+// Returns the number of elements in the composite type |type|. Returns 0 if
+// |type| is a scalar value.
+uint32_t GetNumberOfElements(const analysis::Type* type) {
+ if (auto* vector_type = type->AsVector()) {
+ return vector_type->element_count();
+ }
+ if (auto* matrix_type = type->AsMatrix()) {
+ return matrix_type->element_count();
+ }
+ if (auto* struct_type = type->AsStruct()) {
+ return static_cast<uint32_t>(struct_type->element_types().size());
+ }
+ if (auto* array_type = type->AsArray()) {
+ return array_type->length_info().words[0];
+ }
+ return 0;
+}
+
+// Returns a map with the set of values that were inserted into an object by
+// the chain of OpCompositeInsertInstruction starting with |inst|.
+// The map will map the index to the value inserted at that index.
+std::map<uint32_t, uint32_t> GetInsertedValues(Instruction* inst) {
+ analysis::DefUseManager* def_use_mgr = inst->context()->get_def_use_mgr();
+ std::map<uint32_t, uint32_t> values_inserted;
+ Instruction* current_inst = inst;
+ while (current_inst->opcode() == SpvOpCompositeInsert) {
+ if (current_inst->NumInOperands() > inst->NumInOperands()) {
+ // This is the catch the case
+ // %2 = OpCompositeInsert %m2x2int %v2int_1_0 %m2x2int_undef 0
+ // %3 = OpCompositeInsert %m2x2int %int_4 %2 0 0
+ // %4 = OpCompositeInsert %m2x2int %v2int_2_3 %3 1
+ // In this case we cannot do a single construct to get the matrix.
+ uint32_t partially_inserted_element_index =
+ current_inst->GetSingleWordInOperand(inst->NumInOperands() - 1);
+ if (values_inserted.count(partially_inserted_element_index) == 0)
+ return {};
+ }
+ if (HaveSameIndexesExceptForLast(inst, current_inst)) {
+ values_inserted.insert(
+ {current_inst->GetSingleWordInOperand(current_inst->NumInOperands() -
+ 1),
+ current_inst->GetSingleWordInOperand(kInsertObjectIdInIdx)});
+ }
+ current_inst = def_use_mgr->GetDef(
+ current_inst->GetSingleWordInOperand(kInsertCompositeIdInIdx));
+ }
+ return values_inserted;
+}
+
+// Returns true of there is an entry in |values_inserted| for every element of
+// |Type|.
+bool DoInsertedValuesCoverEntireObject(
+ const analysis::Type* type, std::map<uint32_t, uint32_t>& values_inserted) {
+ uint32_t container_size = GetNumberOfElements(type);
+ if (container_size != values_inserted.size()) {
+ return false;
+ }
+
+ if (values_inserted.rbegin()->first >= container_size) {
+ return false;
+ }
+ return true;
+}
+
+// Returns the type of the element that immediately contains the element being
+// inserted by the OpCompositeInsert instruction |inst|.
+const analysis::Type* GetContainerType(Instruction* inst) {
+ assert(inst->opcode() == SpvOpCompositeInsert);
+ analysis::TypeManager* type_mgr = inst->context()->get_type_mgr();
+ return GetElementType(inst->type_id(), inst->begin() + 4, inst->end() - 1,
+ type_mgr);
+}
+
+// Returns an OpCompositeConstruct instruction that build an object with
+// |type_id| out of the values in |values_inserted|. Each value will be
+// placed at the index corresponding to the value. The new instruction will
+// be placed before |insert_before|.
+Instruction* BuildCompositeConstruct(
+ uint32_t type_id, const std::map<uint32_t, uint32_t>& values_inserted,
+ Instruction* insert_before) {
+ InstructionBuilder ir_builder(
+ insert_before->context(), insert_before,
+ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+ std::vector<uint32_t> ids_in_order;
+ for (auto it : values_inserted) {
+ ids_in_order.push_back(it.second);
+ }
+ Instruction* construct =
+ ir_builder.AddCompositeConstruct(type_id, ids_in_order);
+ return construct;
+}
+
+// Replaces the OpCompositeInsert |inst| that inserts |construct| into the same
+// object as |inst| with final index removed. If the resulting
+// OpCompositeInsert instruction would have no remaining indexes, the
+// instruction is replaced with an OpCopyObject instead.
+void InsertConstructedObject(Instruction* inst, const Instruction* construct) {
+ if (inst->NumInOperands() == 3) {
+ inst->SetOpcode(SpvOpCopyObject);
+ inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {construct->result_id()}}});
+ } else {
+ inst->SetInOperand(kInsertObjectIdInIdx, {construct->result_id()});
+ inst->RemoveOperand(inst->NumOperands() - 1);
+ }
+}
+
+// Replaces a series of |OpCompositeInsert| instruction that cover the entire
+// object with an |OpCompositeConstruct|.
+bool CompositeInsertToCompositeConstruct(
+ IRContext* context, Instruction* inst,
+ const std::vector<const analysis::Constant*>&) {
+ assert(inst->opcode() == SpvOpCompositeInsert &&
+ "Wrong opcode. Should be OpCompositeInsert.");
+ if (inst->NumInOperands() < 3) return false;
+
+ std::map<uint32_t, uint32_t> values_inserted = GetInsertedValues(inst);
+ const analysis::Type* container_type = GetContainerType(inst);
+ if (container_type == nullptr) {
+ return false;
+ }
+
+ if (!DoInsertedValuesCoverEntireObject(container_type, values_inserted)) {
+ return false;
+ }
+
+ analysis::TypeManager* type_mgr = context->get_type_mgr();
+ Instruction* construct = BuildCompositeConstruct(
+ type_mgr->GetId(container_type), values_inserted, inst);
+ InsertConstructedObject(inst, construct);
+ return true;
+}
+
FoldingRule RedundantPhi() {
// An OpPhi instruction where all values are the same or the result of the phi
// itself, can be replaced by the value itself.
@@ -2368,7 +2706,7 @@ FoldingRule VectorShuffleFeedingShuffle() {
// fold.
return false;
}
- } else {
+ } else if (component_index != undef_literal) {
if (new_feeder_id == 0) {
// First time through, save the id of the operand the element comes
// from.
@@ -2382,7 +2720,7 @@ FoldingRule VectorShuffleFeedingShuffle() {
component_index -= feeder_op0_length;
}
- if (!feeder_is_op0) {
+ if (!feeder_is_op0 && component_index != undef_literal) {
component_index += op0_length;
}
}
@@ -2410,7 +2748,8 @@ FoldingRule VectorShuffleFeedingShuffle() {
if (adjustment != 0) {
for (uint32_t i = 2; i < new_operands.size(); i++) {
- if (inst->GetSingleWordInOperand(i) >= op0_length) {
+ uint32_t operand = inst->GetSingleWordInOperand(i);
+ if (operand >= op0_length && operand != undef_literal) {
new_operands[i].words[0] -= adjustment;
}
}
@@ -2533,6 +2872,8 @@ void FoldingRules::AddFoldingRules() {
rules_[SpvOpCompositeExtract].push_back(VectorShuffleFeedingExtract());
rules_[SpvOpCompositeExtract].push_back(FMixFeedingExtract());
+ rules_[SpvOpCompositeInsert].push_back(CompositeInsertToCompositeConstruct);
+
rules_[SpvOpDot].push_back(DotProductDoingExtract());
rules_[SpvOpEntryPoint].push_back(RemoveRedundantOperands());
@@ -2543,6 +2884,7 @@ void FoldingRules::AddFoldingRules() {
rules_[SpvOpFAdd].push_back(MergeAddSubArithmetic());
rules_[SpvOpFAdd].push_back(MergeGenericAddSubArithmetic());
rules_[SpvOpFAdd].push_back(FactorAddMuls());
+ rules_[SpvOpFAdd].push_back(MergeMulAddArithmetic);
rules_[SpvOpFDiv].push_back(RedundantFDiv());
rules_[SpvOpFDiv].push_back(ReciprocalFDiv());
@@ -2563,6 +2905,7 @@ void FoldingRules::AddFoldingRules() {
rules_[SpvOpFSub].push_back(MergeSubNegateArithmetic());
rules_[SpvOpFSub].push_back(MergeSubAddArithmetic());
rules_[SpvOpFSub].push_back(MergeSubSubArithmetic());
+ rules_[SpvOpFSub].push_back(MergeMulSubArithmetic);
rules_[SpvOpIAdd].push_back(RedundantIAdd());
rules_[SpvOpIAdd].push_back(MergeAddNegateArithmetic());
diff --git a/source/opt/function.cpp b/source/opt/function.cpp
index 38c66951..bb51df3f 100644
--- a/source/opt/function.cpp
+++ b/source/opt/function.cpp
@@ -270,5 +270,13 @@ std::string Function::PrettyPrint(uint32_t options) const {
});
return str.str();
}
+
+void Function::ReorderBasicBlocksInStructuredOrder() {
+ std::list<BasicBlock*> order;
+ IRContext* context = this->def_inst_->context();
+ context->cfg()->ComputeStructuredOrder(this, blocks_[0].get(), &order);
+ ReorderBasicBlocks(order.begin(), order.end());
+}
+
} // namespace opt
} // namespace spvtools
diff --git a/source/opt/function.h b/source/opt/function.h
index 917bf584..146cbe34 100644
--- a/source/opt/function.h
+++ b/source/opt/function.h
@@ -19,6 +19,7 @@
#include <functional>
#include <memory>
#include <string>
+#include <unordered_set>
#include <utility>
#include <vector>
@@ -180,7 +181,19 @@ class Function {
// Returns true is a function declaration and not a function definition.
bool IsDeclaration() { return begin() == end(); }
+ // Reorders the basic blocks in the function to match the structured order.
+ void ReorderBasicBlocksInStructuredOrder();
+
private:
+ // Reorders the basic blocks in the function to match the order given by the
+ // range |{begin,end}|. The range must contain every basic block in the
+ // function, and no extras.
+ template <class It>
+ void ReorderBasicBlocks(It begin, It end);
+
+ template <class It>
+ bool ContainsAllBlocksInTheFunction(It begin, It end);
+
// The OpFunction instruction that begins the definition of this function.
std::unique_ptr<Instruction> def_inst_;
// All parameters to this function.
@@ -262,6 +275,34 @@ inline void Function::AddNonSemanticInstruction(
non_semantic_.emplace_back(std::move(non_semantic));
}
+template <class It>
+void Function::ReorderBasicBlocks(It begin, It end) {
+ // Asserts to make sure every node in the function is in new_order.
+ assert(ContainsAllBlocksInTheFunction(begin, end));
+
+ // We have a pointer to all the elements in order, so we can release all
+ // pointers in |block_|, and then create the new unique pointers from |{begin,
+ // end}|.
+ std::for_each(blocks_.begin(), blocks_.end(),
+ [](std::unique_ptr<BasicBlock>& bb) { bb.release(); });
+ std::transform(begin, end, blocks_.begin(), [](BasicBlock* bb) {
+ return std::unique_ptr<BasicBlock>(bb);
+ });
+}
+
+template <class It>
+bool Function::ContainsAllBlocksInTheFunction(It begin, It end) {
+ std::unordered_multiset<BasicBlock*> range(begin, end);
+ if (range.size() != blocks_.size()) {
+ return false;
+ }
+
+ for (auto& bb : blocks_) {
+ if (range.count(bb.get()) == 0) return false;
+ }
+ return true;
+}
+
} // namespace opt
} // namespace spvtools
diff --git a/source/opt/if_conversion.cpp b/source/opt/if_conversion.cpp
index 49206617..1232796e 100644
--- a/source/opt/if_conversion.cpp
+++ b/source/opt/if_conversion.cpp
@@ -160,6 +160,11 @@ bool IfConversion::CheckBlock(BasicBlock* block, DominatorAnalysis* dominators,
BasicBlock* inc1 = context()->get_instr_block(preds[1]);
if (dominators->Dominates(block, inc1)) return false;
+ if (inc0 == inc1) {
+ // If the predecessor blocks are the same, then there is only 1 value for
+ // the OpPhi. Other transformation should be able to simplify that.
+ return false;
+ }
// All phis will have the same common dominator, so cache the result
// for this block. If there is no common dominator, then we cannot transform
// any phi in this basic block.
@@ -169,6 +174,8 @@ bool IfConversion::CheckBlock(BasicBlock* block, DominatorAnalysis* dominators,
if (branch->opcode() != SpvOpBranchConditional) return false;
auto merge = (*common)->GetMergeInst();
if (!merge || merge->opcode() != SpvOpSelectionMerge) return false;
+ if (merge->GetSingleWordInOperand(1) == SpvSelectionControlDontFlattenMask)
+ return false;
if ((*common)->MergeBlockIdIfAny() != block->id()) return false;
return true;
diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp
index 2cc31258..e14516f7 100644
--- a/source/opt/inline_pass.cpp
+++ b/source/opt/inline_pass.cpp
@@ -508,6 +508,37 @@ void InlinePass::MoveLoopMergeInstToFirstBlock(
delete &*loop_merge_itr;
}
+void InlinePass::UpdateSingleBlockLoopContinueTarget(
+ uint32_t new_id, std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+ auto& header = new_blocks->front();
+ auto* merge_inst = header->GetLoopMergeInst();
+
+ // The back-edge block is split at the branch to create a new back-edge
+ // block. The old block is modified to branch to the new block. The loop
+ // merge instruction is updated to declare the new block as the continue
+ // target. This has the effect of changing the loop from being a large
+ // continue construct and an empty loop construct to being a loop with a loop
+ // construct and a trivial continue construct. This change is made to satisfy
+ // structural dominance.
+
+ // Add the new basic block.
+ std::unique_ptr<BasicBlock> new_block =
+ MakeUnique<BasicBlock>(NewLabel(new_id));
+ auto& old_backedge = new_blocks->back();
+ auto old_branch = old_backedge->tail();
+
+ // Move the old back edge into the new block.
+ std::unique_ptr<Instruction> br(&*old_branch);
+ new_block->AddInstruction(std::move(br));
+
+ // Add a branch to the new block from the old back-edge block.
+ AddBranch(new_id, &old_backedge);
+ new_blocks->push_back(std::move(new_block));
+
+ // Update the loop's continue target to the new block.
+ merge_inst->SetInOperand(1u, {new_id});
+}
+
bool InlinePass::GenInlineCode(
std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
std::vector<std::unique_ptr<Instruction>>* new_vars,
@@ -639,9 +670,19 @@ bool InlinePass::GenInlineCode(
// Finalize inline code.
new_blocks->push_back(std::move(new_blk_ptr));
- if (caller_is_loop_header && (new_blocks->size() > 1))
+ if (caller_is_loop_header && (new_blocks->size() > 1)) {
MoveLoopMergeInstToFirstBlock(new_blocks);
+ // If the loop was a single basic block previously, update it's structure.
+ auto& header = new_blocks->front();
+ auto* merge_inst = header->GetLoopMergeInst();
+ if (merge_inst->GetSingleWordInOperand(1u) == header->id()) {
+ auto new_id = context()->TakeNextId();
+ if (new_id == 0) return false;
+ UpdateSingleBlockLoopContinueTarget(new_id, new_blocks);
+ }
+ }
+
// Update block map given replacement blocks.
for (auto& blk : *new_blocks) {
id2block_[blk->id()] = &*blk;
@@ -753,22 +794,25 @@ bool InlinePass::IsInlinableFunction(Function* func) {
return false;
}
- // Do not inline functions with an OpKill if they are called from a continue
- // construct. If it is inlined into a continue construct it will generate
- // invalid code.
+ // Do not inline functions with an abort instruction if they are called from a
+ // continue construct. If it is inlined into a continue construct the backedge
+ // will no longer post-dominate the continue target, which is invalid. An
+ // `OpUnreachable` is acceptable because it will not change post-dominance if
+ // it is statically unreachable.
bool func_is_called_from_continue =
funcs_called_from_continue_.count(func->result_id()) != 0;
- if (func_is_called_from_continue && ContainsKillOrTerminateInvocation(func)) {
+ if (func_is_called_from_continue && ContainsAbortOtherThanUnreachable(func)) {
return false;
}
return true;
}
-bool InlinePass::ContainsKillOrTerminateInvocation(Function* func) const {
+bool InlinePass::ContainsAbortOtherThanUnreachable(Function* func) const {
return !func->WhileEachInst([](Instruction* inst) {
- return !spvOpcodeTerminatesExecution(inst->opcode());
+ return inst->opcode() == SpvOpUnreachable ||
+ !spvOpcodeIsAbort(inst->opcode());
});
}
diff --git a/source/opt/inline_pass.h b/source/opt/inline_pass.h
index 9a5429ba..d29c1e07 100644
--- a/source/opt/inline_pass.h
+++ b/source/opt/inline_pass.h
@@ -139,9 +139,9 @@ class InlinePass : public Pass {
// Return true if |func| is a function that can be inlined.
bool IsInlinableFunction(Function* func);
- // Returns true if |func| contains an OpKill or OpTerminateInvocation
- // instruction.
- bool ContainsKillOrTerminateInvocation(Function* func) const;
+ // Returns true if |func| contains an abort instruction that is not an
+ // `OpUnreachable` instruction.
+ bool ContainsAbortOtherThanUnreachable(Function* func) const;
// Update phis in succeeding blocks to point to new last block
void UpdateSucceedingPhis(
@@ -235,6 +235,12 @@ class InlinePass : public Pass {
// Move the OpLoopMerge from the last block back to the first.
void MoveLoopMergeInstToFirstBlock(
std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
+
+ // Update the structure of single block loops so that the inlined code ends
+ // up in the loop construct and a new continue target is added to satisfy
+ // structural dominance.
+ void UpdateSingleBlockLoopContinueTarget(
+ uint32_t new_id, std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
};
} // namespace opt
diff --git a/source/opt/inst_bindless_check_pass.cpp b/source/opt/inst_bindless_check_pass.cpp
index 5607239a..c2c5d6cb 100644
--- a/source/opt/inst_bindless_check_pass.cpp
+++ b/source/opt/inst_bindless_check_pass.cpp
@@ -39,13 +39,6 @@ static const int kSpvTypeImageMS = 4;
static const int kSpvTypeImageSampled = 5;
} // anonymous namespace
-// Avoid unused variable warning/error on Linux
-#ifndef NDEBUG
-#define USE_ASSERT(x) assert(x)
-#else
-#define USE_ASSERT(x) ((void)(x))
-#endif
-
namespace spvtools {
namespace opt {
diff --git a/source/opt/inst_buff_addr_check_pass.cpp b/source/opt/inst_buff_addr_check_pass.cpp
index e2336d36..3318f88f 100644
--- a/source/opt/inst_buff_addr_check_pass.cpp
+++ b/source/opt/inst_buff_addr_check_pass.cpp
@@ -393,6 +393,8 @@ uint32_t InstBuffAddrCheckPass::GetSearchAndTestFuncId() {
get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst);
input_func->SetFunctionEnd(std::move(func_end_inst));
context()->AddFunction(std::move(input_func));
+ context()->AddDebug2Inst(
+ NewGlobalName(search_test_func_id_, "search_and_test"));
}
return search_test_func_id_;
}
diff --git a/source/opt/inst_buff_addr_check_pass.h b/source/opt/inst_buff_addr_check_pass.h
index a8232239..fb43c397 100644
--- a/source/opt/inst_buff_addr_check_pass.h
+++ b/source/opt/inst_buff_addr_check_pass.h
@@ -39,7 +39,7 @@ class InstBuffAddrCheckPass : public InstrumentPass {
// See optimizer.hpp for pass user documentation.
Status Process() override;
- const char* name() const override { return "inst-bindless-check-pass"; }
+ const char* name() const override { return "inst-buff-addr-check-pass"; }
private:
// Return byte alignment of type |type_id|. Must be int, float, vector,
diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp
index 2461e41e..e775a992 100644
--- a/source/opt/instruction.cpp
+++ b/source/opt/instruction.cpp
@@ -76,10 +76,9 @@ Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
dbg_scope_(kNoDebugScope, kNoInlinedAt) {
for (uint32_t i = 0; i < inst.num_operands; ++i) {
const auto& current_payload = inst.operands[i];
- std::vector<uint32_t> words(
- inst.words + current_payload.offset,
+ operands_.emplace_back(
+ current_payload.type, inst.words + current_payload.offset,
inst.words + current_payload.offset + current_payload.num_words);
- operands_.emplace_back(current_payload.type, std::move(words));
}
assert((!IsLineInst() || dbg_line.empty()) &&
"Op(No)Line attaching to Op(No)Line found");
@@ -96,10 +95,9 @@ Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
dbg_scope_(dbg_scope) {
for (uint32_t i = 0; i < inst.num_operands; ++i) {
const auto& current_payload = inst.operands[i];
- std::vector<uint32_t> words(
- inst.words + current_payload.offset,
+ operands_.emplace_back(
+ current_payload.type, inst.words + current_payload.offset,
inst.words + current_payload.offset + current_payload.num_words);
- operands_.emplace_back(current_payload.type, std::move(words));
}
}
@@ -506,26 +504,6 @@ bool Instruction::IsReadOnlyPointerKernel() const {
return storage_class == SpvStorageClassUniformConstant;
}
-uint32_t Instruction::GetTypeComponent(uint32_t element) const {
- uint32_t subtype = 0;
- switch (opcode()) {
- case SpvOpTypeStruct:
- subtype = GetSingleWordInOperand(element);
- break;
- case SpvOpTypeArray:
- case SpvOpTypeRuntimeArray:
- case SpvOpTypeVector:
- case SpvOpTypeMatrix:
- // These types all have uniform subtypes.
- subtype = GetSingleWordInOperand(0u);
- break;
- default:
- break;
- }
-
- return subtype;
-}
-
void Instruction::UpdateLexicalScope(uint32_t scope) {
dbg_scope_.SetLexicalScope(scope);
for (auto& i : dbg_line_insts_) {
@@ -695,8 +673,12 @@ NonSemanticShaderDebugInfo100Instructions Instruction::GetShader100DebugOpcode()
return NonSemanticShaderDebugInfo100InstructionsMax;
}
- return NonSemanticShaderDebugInfo100Instructions(
- GetSingleWordInOperand(kExtInstInstructionInIdx));
+ uint32_t opcode = GetSingleWordInOperand(kExtInstInstructionInIdx);
+ if (opcode >= NonSemanticShaderDebugInfo100InstructionsMax) {
+ return NonSemanticShaderDebugInfo100InstructionsMax;
+ }
+
+ return NonSemanticShaderDebugInfo100Instructions(opcode);
}
CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const {
diff --git a/source/opt/instruction.h b/source/opt/instruction.h
index f87f563a..e79c6289 100644
--- a/source/opt/instruction.h
+++ b/source/opt/instruction.h
@@ -84,9 +84,20 @@ struct Operand {
Operand(spv_operand_type_t t, const OperandData& w) : type(t), words(w) {}
+ template <class InputIt>
+ Operand(spv_operand_type_t t, InputIt firstOperandData,
+ InputIt lastOperandData)
+ : type(t), words(firstOperandData, lastOperandData) {}
+
spv_operand_type_t type; // Type of this logical operand.
OperandData words; // Binary segments of this logical operand.
+ uint32_t AsId() const {
+ assert(spvIsIdType(type));
+ assert(words.size() == 1);
+ return words[0];
+ }
+
// Returns a string operand as a std::string.
std::string AsString() const {
assert(type == SPV_OPERAND_TYPE_LITERAL_STRING);
@@ -95,7 +106,10 @@ struct Operand {
// Returns a literal integer operand as a uint64_t
uint64_t AsLiteralUint64() const {
- assert(type == SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER);
+ assert(type == SPV_OPERAND_TYPE_LITERAL_INTEGER ||
+ type == SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER ||
+ type == SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER ||
+ type == SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER);
assert(1 <= words.size());
assert(words.size() <= 2);
uint64_t result = 0;
@@ -294,6 +308,7 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
inline void SetInOperands(OperandList&& new_operands);
// Sets the result type id.
inline void SetResultType(uint32_t ty_id);
+ inline bool HasResultType() const { return has_type_id_; }
// Sets the result id
inline void SetResultId(uint32_t res_id);
inline bool HasResultId() const { return has_result_id_; }
@@ -489,10 +504,6 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// Returns true if this instruction exits this function or aborts execution.
bool IsReturnOrAbort() const { return spvOpcodeIsReturnOrAbort(opcode()); }
- // Returns the id for the |element|'th subtype. If the |this| is not a
- // composite type, this function returns 0.
- uint32_t GetTypeComponent(uint32_t element) const;
-
// Returns true if this instruction is a basic block terminator.
bool IsBlockTerminator() const {
return spvOpcodeIsBlockTerminator(opcode());
diff --git a/source/opt/instrument_pass.cpp b/source/opt/instrument_pass.cpp
index ed34fb02..d143d595 100644
--- a/source/opt/instrument_pass.cpp
+++ b/source/opt/instrument_pass.cpp
@@ -88,6 +88,51 @@ std::unique_ptr<Instruction> InstrumentPass::NewLabel(uint32_t label_id) {
return newLabel;
}
+std::unique_ptr<Instruction> InstrumentPass::NewName(
+ uint32_t id, const std::string& name_str) {
+ std::unique_ptr<Instruction> new_name(new Instruction(
+ context(), SpvOpName, 0, 0,
+ std::initializer_list<Operand>{
+ {SPV_OPERAND_TYPE_ID, {id}},
+ {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}}));
+
+ return new_name;
+}
+
+std::unique_ptr<Instruction> InstrumentPass::NewGlobalName(
+ uint32_t id, const std::string& name_str) {
+ std::string prefixed_name;
+ switch (validation_id_) {
+ case kInstValidationIdBindless:
+ prefixed_name = "inst_bindless_";
+ break;
+ case kInstValidationIdBuffAddr:
+ prefixed_name = "inst_buff_addr_";
+ break;
+ case kInstValidationIdDebugPrintf:
+ prefixed_name = "inst_printf_";
+ break;
+ default:
+ assert(false); // add new instrumentation pass here
+ prefixed_name = "inst_pass_";
+ break;
+ }
+ prefixed_name += name_str;
+ return NewName(id, prefixed_name);
+}
+
+std::unique_ptr<Instruction> InstrumentPass::NewMemberName(
+ uint32_t id, uint32_t member_index, const std::string& name_str) {
+ std::unique_ptr<Instruction> new_name(new Instruction(
+ context(), SpvOpMemberName, 0, 0,
+ std::initializer_list<Operand>{
+ {SPV_OPERAND_TYPE_ID, {id}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {member_index}},
+ {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}}));
+
+ return new_name;
+}
+
uint32_t InstrumentPass::Gen32BitCvtCode(uint32_t val_id,
InstructionBuilder* builder) {
// Convert integer value to 32-bit if necessary
@@ -200,7 +245,9 @@ void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx,
} break;
case SpvExecutionModelGLCompute:
case SpvExecutionModelTaskNV:
- case SpvExecutionModelMeshNV: {
+ case SpvExecutionModelMeshNV:
+ case SpvExecutionModelTaskEXT:
+ case SpvExecutionModelMeshEXT: {
// Load and store GlobalInvocationId.
uint32_t load_id = GenVarLoad(
context()->GetBuiltinInputVarId(SpvBuiltInGlobalInvocationId),
@@ -525,6 +572,10 @@ uint32_t InstrumentPass::GetOutputBufferId() {
{{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
{SpvStorageClassStorageBuffer}}}));
context()->AddGlobalValue(std::move(newVarOp));
+ context()->AddDebug2Inst(NewGlobalName(obufTyId, "OutputBuffer"));
+ context()->AddDebug2Inst(NewMemberName(obufTyId, 0, "written_count"));
+ context()->AddDebug2Inst(NewMemberName(obufTyId, 1, "data"));
+ context()->AddDebug2Inst(NewGlobalName(output_buffer_id_, "output_buffer"));
deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationDescriptorSet,
desc_set_);
deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationBinding,
@@ -569,6 +620,9 @@ uint32_t InstrumentPass::GetInputBufferId() {
{{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
{SpvStorageClassStorageBuffer}}}));
context()->AddGlobalValue(std::move(newVarOp));
+ context()->AddDebug2Inst(NewGlobalName(ibufTyId, "InputBuffer"));
+ context()->AddDebug2Inst(NewMemberName(ibufTyId, 0, "data"));
+ context()->AddDebug2Inst(NewGlobalName(input_buffer_id_, "input_buffer"));
deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationDescriptorSet,
desc_set_);
deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationBinding,
@@ -783,6 +837,12 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx,
get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst);
output_func->SetFunctionEnd(std::move(func_end_inst));
context()->AddFunction(std::move(output_func));
+
+ std::string name("stream_write_");
+ name += std::to_string(param_cnt);
+
+ context()->AddDebug2Inst(
+ NewGlobalName(param2output_func_id_[param_cnt], name));
}
return param2output_func_id_[param_cnt];
}
@@ -863,6 +923,11 @@ uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) {
get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst);
input_func->SetFunctionEnd(std::move(func_end_inst));
context()->AddFunction(std::move(input_func));
+
+ std::string name("direct_read_");
+ name += std::to_string(param_cnt);
+ context()->AddDebug2Inst(NewGlobalName(func_id, name));
+
param2input_func_id_[param_cnt] = func_id;
return func_id;
}
@@ -1001,7 +1066,8 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
stage != SpvExecutionModelAnyHitNV &&
stage != SpvExecutionModelClosestHitNV &&
stage != SpvExecutionModelMissNV &&
- stage != SpvExecutionModelCallableNV) {
+ stage != SpvExecutionModelCallableNV &&
+ stage != SpvExecutionModelTaskEXT && stage != SpvExecutionModelMeshEXT) {
if (consumer()) {
std::string message = "Stage not supported by instrumentation";
consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str());
diff --git a/source/opt/instrument_pass.h b/source/opt/instrument_pass.h
index 90c1dd47..215b0263 100644
--- a/source/opt/instrument_pass.h
+++ b/source/opt/instrument_pass.h
@@ -224,6 +224,19 @@ class InstrumentPass : public Pass {
// Return new label.
std::unique_ptr<Instruction> NewLabel(uint32_t label_id);
+ // Set the name function parameter or local variable
+ std::unique_ptr<Instruction> NewName(uint32_t id,
+ const std::string& name_str);
+
+ // Set the name for a function or global variable, names will be
+ // prefixed to identify which instrumentation pass generated them.
+ std::unique_ptr<Instruction> NewGlobalName(uint32_t id,
+ const std::string& name_str);
+
+ // Set the name for a structure member
+ std::unique_ptr<Instruction> NewMemberName(uint32_t id, uint32_t member_index,
+ const std::string& name_str);
+
// Return id for 32-bit unsigned type
uint32_t GetUintId();
diff --git a/source/opt/interface_var_sroa.cpp b/source/opt/interface_var_sroa.cpp
new file mode 100644
index 00000000..1b2cb363
--- /dev/null
+++ b/source/opt/interface_var_sroa.cpp
@@ -0,0 +1,968 @@
+// Copyright (c) 2022 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/interface_var_sroa.h"
+
+#include <iostream>
+
+#include "source/opt/decoration_manager.h"
+#include "source/opt/def_use_manager.h"
+#include "source/opt/function.h"
+#include "source/opt/log.h"
+#include "source/opt/type_manager.h"
+#include "source/util/make_unique.h"
+
+const static uint32_t kOpDecorateDecorationInOperandIndex = 1;
+const static uint32_t kOpDecorateLiteralInOperandIndex = 2;
+const static uint32_t kOpEntryPointInOperandInterface = 3;
+const static uint32_t kOpVariableStorageClassInOperandIndex = 0;
+const static uint32_t kOpTypeArrayElemTypeInOperandIndex = 0;
+const static uint32_t kOpTypeArrayLengthInOperandIndex = 1;
+const static uint32_t kOpTypeMatrixColCountInOperandIndex = 1;
+const static uint32_t kOpTypeMatrixColTypeInOperandIndex = 0;
+const static uint32_t kOpTypePtrTypeInOperandIndex = 1;
+const static uint32_t kOpConstantValueInOperandIndex = 0;
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+// Get the length of the OpTypeArray |array_type|.
+uint32_t GetArrayLength(analysis::DefUseManager* def_use_mgr,
+ Instruction* array_type) {
+ assert(array_type->opcode() == SpvOpTypeArray);
+ uint32_t const_int_id =
+ array_type->GetSingleWordInOperand(kOpTypeArrayLengthInOperandIndex);
+ Instruction* array_length_inst = def_use_mgr->GetDef(const_int_id);
+ assert(array_length_inst->opcode() == SpvOpConstant);
+ return array_length_inst->GetSingleWordInOperand(
+ kOpConstantValueInOperandIndex);
+}
+
+// Get the element type instruction of the OpTypeArray |array_type|.
+Instruction* GetArrayElementType(analysis::DefUseManager* def_use_mgr,
+ Instruction* array_type) {
+ assert(array_type->opcode() == SpvOpTypeArray);
+ uint32_t elem_type_id =
+ array_type->GetSingleWordInOperand(kOpTypeArrayElemTypeInOperandIndex);
+ return def_use_mgr->GetDef(elem_type_id);
+}
+
+// Get the column type instruction of the OpTypeMatrix |matrix_type|.
+Instruction* GetMatrixColumnType(analysis::DefUseManager* def_use_mgr,
+ Instruction* matrix_type) {
+ assert(matrix_type->opcode() == SpvOpTypeMatrix);
+ uint32_t column_type_id =
+ matrix_type->GetSingleWordInOperand(kOpTypeMatrixColTypeInOperandIndex);
+ return def_use_mgr->GetDef(column_type_id);
+}
+
+// Traverses the component type of OpTypeArray or OpTypeMatrix. Repeats it
+// |depth_to_component| times recursively and returns the component type.
+// |type_id| is the result id of the OpTypeArray or OpTypeMatrix instruction.
+uint32_t GetComponentTypeOfArrayMatrix(analysis::DefUseManager* def_use_mgr,
+ uint32_t type_id,
+ uint32_t depth_to_component) {
+ if (depth_to_component == 0) return type_id;
+
+ Instruction* type_inst = def_use_mgr->GetDef(type_id);
+ if (type_inst->opcode() == SpvOpTypeArray) {
+ uint32_t elem_type_id =
+ type_inst->GetSingleWordInOperand(kOpTypeArrayElemTypeInOperandIndex);
+ return GetComponentTypeOfArrayMatrix(def_use_mgr, elem_type_id,
+ depth_to_component - 1);
+ }
+
+ assert(type_inst->opcode() == SpvOpTypeMatrix);
+ uint32_t column_type_id =
+ type_inst->GetSingleWordInOperand(kOpTypeMatrixColTypeInOperandIndex);
+ return GetComponentTypeOfArrayMatrix(def_use_mgr, column_type_id,
+ depth_to_component - 1);
+}
+
+// Creates an OpDecorate instruction whose Target is |var_id| and Decoration is
+// |decoration|. Adds |literal| as an extra operand of the instruction.
+void CreateDecoration(analysis::DecorationManager* decoration_mgr,
+ uint32_t var_id, SpvDecoration decoration,
+ uint32_t literal) {
+ std::vector<Operand> operands({
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {var_id}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_DECORATION,
+ {static_cast<uint32_t>(decoration)}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {literal}},
+ });
+ decoration_mgr->AddDecoration(SpvOpDecorate, std::move(operands));
+}
+
+// Replaces load instructions with composite construct instructions in all the
+// users of the loads. |loads_to_composites| is the mapping from each load to
+// its corresponding OpCompositeConstruct.
+void ReplaceLoadWithCompositeConstruct(
+ IRContext* context,
+ const std::unordered_map<Instruction*, Instruction*>& loads_to_composites) {
+ for (const auto& load_and_composite : loads_to_composites) {
+ Instruction* load = load_and_composite.first;
+ Instruction* composite_construct = load_and_composite.second;
+
+ std::vector<Instruction*> users;
+ context->get_def_use_mgr()->ForEachUse(
+ load, [&users, composite_construct](Instruction* user, uint32_t index) {
+ user->GetOperand(index).words[0] = composite_construct->result_id();
+ users.push_back(user);
+ });
+
+ for (Instruction* user : users)
+ context->get_def_use_mgr()->AnalyzeInstUse(user);
+ }
+}
+
+// Returns the storage class of the instruction |var|.
+SpvStorageClass GetStorageClass(Instruction* var) {
+ return static_cast<SpvStorageClass>(
+ var->GetSingleWordInOperand(kOpVariableStorageClassInOperandIndex));
+}
+
+} // namespace
+
+bool InterfaceVariableScalarReplacement::HasExtraArrayness(
+ Instruction& entry_point, Instruction* var) {
+ SpvExecutionModel execution_model =
+ static_cast<SpvExecutionModel>(entry_point.GetSingleWordInOperand(0));
+ if (execution_model != SpvExecutionModelTessellationEvaluation &&
+ execution_model != SpvExecutionModelTessellationControl) {
+ return false;
+ }
+ if (!context()->get_decoration_mgr()->HasDecoration(var->result_id(),
+ SpvDecorationPatch)) {
+ if (execution_model == SpvExecutionModelTessellationControl) return true;
+ return GetStorageClass(var) != SpvStorageClassOutput;
+ }
+ return false;
+}
+
+bool InterfaceVariableScalarReplacement::
+ CheckExtraArraynessConflictBetweenEntries(Instruction* interface_var,
+ bool has_extra_arrayness) {
+ if (has_extra_arrayness) {
+ return !ReportErrorIfHasNoExtraArraynessForOtherEntry(interface_var);
+ }
+ return !ReportErrorIfHasExtraArraynessForOtherEntry(interface_var);
+}
+
+bool InterfaceVariableScalarReplacement::GetVariableLocation(
+ Instruction* var, uint32_t* location) {
+ return !context()->get_decoration_mgr()->WhileEachDecoration(
+ var->result_id(), SpvDecorationLocation,
+ [location](const Instruction& inst) {
+ *location =
+ inst.GetSingleWordInOperand(kOpDecorateLiteralInOperandIndex);
+ return false;
+ });
+}
+
+bool InterfaceVariableScalarReplacement::GetVariableComponent(
+ Instruction* var, uint32_t* component) {
+ return !context()->get_decoration_mgr()->WhileEachDecoration(
+ var->result_id(), SpvDecorationComponent,
+ [component](const Instruction& inst) {
+ *component =
+ inst.GetSingleWordInOperand(kOpDecorateLiteralInOperandIndex);
+ return false;
+ });
+}
+
+std::vector<Instruction*>
+InterfaceVariableScalarReplacement::CollectInterfaceVariables(
+ Instruction& entry_point) {
+ std::vector<Instruction*> interface_vars;
+ for (uint32_t i = kOpEntryPointInOperandInterface;
+ i < entry_point.NumInOperands(); ++i) {
+ Instruction* interface_var = context()->get_def_use_mgr()->GetDef(
+ entry_point.GetSingleWordInOperand(i));
+ assert(interface_var->opcode() == SpvOpVariable);
+
+ SpvStorageClass storage_class = GetStorageClass(interface_var);
+ if (storage_class != SpvStorageClassInput &&
+ storage_class != SpvStorageClassOutput) {
+ continue;
+ }
+
+ interface_vars.push_back(interface_var);
+ }
+ return interface_vars;
+}
+
+void InterfaceVariableScalarReplacement::KillInstructionAndUsers(
+ Instruction* inst) {
+ if (inst->opcode() == SpvOpEntryPoint) {
+ return;
+ }
+ if (inst->opcode() != SpvOpAccessChain) {
+ context()->KillInst(inst);
+ return;
+ }
+ std::vector<Instruction*> users;
+ context()->get_def_use_mgr()->ForEachUser(
+ inst, [&users](Instruction* user) { users.push_back(user); });
+ for (auto user : users) {
+ context()->KillInst(user);
+ }
+ context()->KillInst(inst);
+}
+
+void InterfaceVariableScalarReplacement::KillInstructionsAndUsers(
+ const std::vector<Instruction*>& insts) {
+ for (Instruction* inst : insts) {
+ KillInstructionAndUsers(inst);
+ }
+}
+
+void InterfaceVariableScalarReplacement::KillLocationAndComponentDecorations(
+ uint32_t var_id) {
+ context()->get_decoration_mgr()->RemoveDecorationsFrom(
+ var_id, [](const Instruction& inst) {
+ uint32_t decoration =
+ inst.GetSingleWordInOperand(kOpDecorateDecorationInOperandIndex);
+ return decoration == SpvDecorationLocation ||
+ decoration == SpvDecorationComponent;
+ });
+}
+
+bool InterfaceVariableScalarReplacement::ReplaceInterfaceVariableWithScalars(
+ Instruction* interface_var, Instruction* interface_var_type,
+ uint32_t location, uint32_t component, uint32_t extra_array_length) {
+ NestedCompositeComponents scalar_interface_vars =
+ CreateScalarInterfaceVarsForReplacement(interface_var_type,
+ GetStorageClass(interface_var),
+ extra_array_length);
+
+ AddLocationAndComponentDecorations(scalar_interface_vars, &location,
+ component);
+ KillLocationAndComponentDecorations(interface_var->result_id());
+
+ if (!ReplaceInterfaceVarWith(interface_var, extra_array_length,
+ scalar_interface_vars)) {
+ return false;
+ }
+
+ context()->KillInst(interface_var);
+ return true;
+}
+
+bool InterfaceVariableScalarReplacement::ReplaceInterfaceVarWith(
+ Instruction* interface_var, uint32_t extra_array_length,
+ const NestedCompositeComponents& scalar_interface_vars) {
+ std::vector<Instruction*> users;
+ context()->get_def_use_mgr()->ForEachUser(
+ interface_var, [&users](Instruction* user) { users.push_back(user); });
+
+ std::vector<uint32_t> interface_var_component_indices;
+ std::unordered_map<Instruction*, Instruction*> loads_to_composites;
+ std::unordered_map<Instruction*, Instruction*>
+ loads_for_access_chain_to_composites;
+ if (extra_array_length != 0) {
+ // Note that the extra arrayness is the first dimension of the array
+ // interface variable.
+ for (uint32_t index = 0; index < extra_array_length; ++index) {
+ std::unordered_map<Instruction*, Instruction*> loads_to_component_values;
+ if (!ReplaceComponentsOfInterfaceVarWith(
+ interface_var, users, scalar_interface_vars,
+ interface_var_component_indices, &index,
+ &loads_to_component_values,
+ &loads_for_access_chain_to_composites)) {
+ return false;
+ }
+ AddComponentsToCompositesForLoads(loads_to_component_values,
+ &loads_to_composites, 0);
+ }
+ } else if (!ReplaceComponentsOfInterfaceVarWith(
+ interface_var, users, scalar_interface_vars,
+ interface_var_component_indices, nullptr, &loads_to_composites,
+ &loads_for_access_chain_to_composites)) {
+ return false;
+ }
+
+ ReplaceLoadWithCompositeConstruct(context(), loads_to_composites);
+ ReplaceLoadWithCompositeConstruct(context(),
+ loads_for_access_chain_to_composites);
+
+ KillInstructionsAndUsers(users);
+ return true;
+}
+
+void InterfaceVariableScalarReplacement::AddLocationAndComponentDecorations(
+ const NestedCompositeComponents& vars, uint32_t* location,
+ uint32_t component) {
+ if (!vars.HasMultipleComponents()) {
+ uint32_t var_id = vars.GetComponentVariable()->result_id();
+ CreateDecoration(context()->get_decoration_mgr(), var_id,
+ SpvDecorationLocation, *location);
+ CreateDecoration(context()->get_decoration_mgr(), var_id,
+ SpvDecorationComponent, component);
+ ++(*location);
+ return;
+ }
+ for (const auto& var : vars.GetComponents()) {
+ AddLocationAndComponentDecorations(var, location, component);
+ }
+}
+
+bool InterfaceVariableScalarReplacement::ReplaceComponentsOfInterfaceVarWith(
+ Instruction* interface_var,
+ const std::vector<Instruction*>& interface_var_users,
+ const NestedCompositeComponents& scalar_interface_vars,
+ std::vector<uint32_t>& interface_var_component_indices,
+ const uint32_t* extra_array_index,
+ std::unordered_map<Instruction*, Instruction*>* loads_to_composites,
+ std::unordered_map<Instruction*, Instruction*>*
+ loads_for_access_chain_to_composites) {
+ if (!scalar_interface_vars.HasMultipleComponents()) {
+ for (Instruction* interface_var_user : interface_var_users) {
+ if (!ReplaceComponentOfInterfaceVarWith(
+ interface_var, interface_var_user,
+ scalar_interface_vars.GetComponentVariable(),
+ interface_var_component_indices, extra_array_index,
+ loads_to_composites, loads_for_access_chain_to_composites)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return ReplaceMultipleComponentsOfInterfaceVarWith(
+ interface_var, interface_var_users, scalar_interface_vars.GetComponents(),
+ interface_var_component_indices, extra_array_index, loads_to_composites,
+ loads_for_access_chain_to_composites);
+}
+
+bool InterfaceVariableScalarReplacement::
+ ReplaceMultipleComponentsOfInterfaceVarWith(
+ Instruction* interface_var,
+ const std::vector<Instruction*>& interface_var_users,
+ const std::vector<NestedCompositeComponents>& components,
+ std::vector<uint32_t>& interface_var_component_indices,
+ const uint32_t* extra_array_index,
+ std::unordered_map<Instruction*, Instruction*>* loads_to_composites,
+ std::unordered_map<Instruction*, Instruction*>*
+ loads_for_access_chain_to_composites) {
+ for (uint32_t i = 0; i < components.size(); ++i) {
+ interface_var_component_indices.push_back(i);
+ std::unordered_map<Instruction*, Instruction*> loads_to_component_values;
+ std::unordered_map<Instruction*, Instruction*>
+ loads_for_access_chain_to_component_values;
+ if (!ReplaceComponentsOfInterfaceVarWith(
+ interface_var, interface_var_users, components[i],
+ interface_var_component_indices, extra_array_index,
+ &loads_to_component_values,
+ &loads_for_access_chain_to_component_values)) {
+ return false;
+ }
+ interface_var_component_indices.pop_back();
+
+ uint32_t depth_to_component =
+ static_cast<uint32_t>(interface_var_component_indices.size());
+ AddComponentsToCompositesForLoads(
+ loads_for_access_chain_to_component_values,
+ loads_for_access_chain_to_composites, depth_to_component);
+ if (extra_array_index) ++depth_to_component;
+ AddComponentsToCompositesForLoads(loads_to_component_values,
+ loads_to_composites, depth_to_component);
+ }
+ return true;
+}
+
+bool InterfaceVariableScalarReplacement::ReplaceComponentOfInterfaceVarWith(
+ Instruction* interface_var, Instruction* interface_var_user,
+ Instruction* scalar_var,
+ const std::vector<uint32_t>& interface_var_component_indices,
+ const uint32_t* extra_array_index,
+ std::unordered_map<Instruction*, Instruction*>* loads_to_component_values,
+ std::unordered_map<Instruction*, Instruction*>*
+ loads_for_access_chain_to_component_values) {
+ SpvOp opcode = interface_var_user->opcode();
+ if (opcode == SpvOpStore) {
+ uint32_t value_id = interface_var_user->GetSingleWordInOperand(1);
+ StoreComponentOfValueToScalarVar(value_id, interface_var_component_indices,
+ scalar_var, extra_array_index,
+ interface_var_user);
+ return true;
+ }
+ if (opcode == SpvOpLoad) {
+ Instruction* scalar_load =
+ LoadScalarVar(scalar_var, extra_array_index, interface_var_user);
+ loads_to_component_values->insert({interface_var_user, scalar_load});
+ return true;
+ }
+
+ // Copy OpName and annotation instructions only once. Therefore, we create
+ // them only for the first element of the extra array.
+ if (extra_array_index && *extra_array_index != 0) return true;
+
+ if (opcode == SpvOpDecorateId || opcode == SpvOpDecorateString ||
+ opcode == SpvOpDecorate) {
+ CloneAnnotationForVariable(interface_var_user, scalar_var->result_id());
+ return true;
+ }
+
+ if (opcode == SpvOpName) {
+ std::unique_ptr<Instruction> new_inst(interface_var_user->Clone(context()));
+ new_inst->SetInOperand(0, {scalar_var->result_id()});
+ context()->AddDebug2Inst(std::move(new_inst));
+ return true;
+ }
+
+ if (opcode == SpvOpEntryPoint) {
+ return ReplaceInterfaceVarInEntryPoint(interface_var, interface_var_user,
+ scalar_var->result_id());
+ }
+
+ if (opcode == SpvOpAccessChain) {
+ ReplaceAccessChainWith(interface_var_user, interface_var_component_indices,
+ scalar_var,
+ loads_for_access_chain_to_component_values);
+ return true;
+ }
+
+ std::string message("Unhandled instruction");
+ message += "\n " + interface_var_user->PrettyPrint(
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ message +=
+ "\nfor interface variable scalar replacement\n " +
+ interface_var->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str());
+ return false;
+}
+
+void InterfaceVariableScalarReplacement::UseBaseAccessChainForAccessChain(
+ Instruction* access_chain, Instruction* base_access_chain) {
+ assert(base_access_chain->opcode() == SpvOpAccessChain &&
+ access_chain->opcode() == SpvOpAccessChain &&
+ access_chain->GetSingleWordInOperand(0) ==
+ base_access_chain->result_id());
+ Instruction::OperandList new_operands;
+ for (uint32_t i = 0; i < base_access_chain->NumInOperands(); ++i) {
+ new_operands.emplace_back(base_access_chain->GetInOperand(i));
+ }
+ for (uint32_t i = 1; i < access_chain->NumInOperands(); ++i) {
+ new_operands.emplace_back(access_chain->GetInOperand(i));
+ }
+ access_chain->SetInOperands(std::move(new_operands));
+}
+
+Instruction* InterfaceVariableScalarReplacement::CreateAccessChainToVar(
+ uint32_t var_type_id, Instruction* var,
+ const std::vector<uint32_t>& index_ids, Instruction* insert_before,
+ uint32_t* component_type_id) {
+ analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+ *component_type_id = GetComponentTypeOfArrayMatrix(
+ def_use_mgr, var_type_id, static_cast<uint32_t>(index_ids.size()));
+
+ uint32_t ptr_type_id =
+ GetPointerType(*component_type_id, GetStorageClass(var));
+
+ std::unique_ptr<Instruction> new_access_chain(
+ new Instruction(context(), SpvOpAccessChain, ptr_type_id, TakeNextId(),
+ std::initializer_list<Operand>{
+ {SPV_OPERAND_TYPE_ID, {var->result_id()}}}));
+ for (uint32_t index_id : index_ids) {
+ new_access_chain->AddOperand({SPV_OPERAND_TYPE_ID, {index_id}});
+ }
+
+ Instruction* inst = new_access_chain.get();
+ def_use_mgr->AnalyzeInstDefUse(inst);
+ insert_before->InsertBefore(std::move(new_access_chain));
+ return inst;
+}
+
+Instruction* InterfaceVariableScalarReplacement::CreateAccessChainWithIndex(
+ uint32_t component_type_id, Instruction* var, uint32_t index,
+ Instruction* insert_before) {
+ uint32_t ptr_type_id =
+ GetPointerType(component_type_id, GetStorageClass(var));
+ uint32_t index_id = context()->get_constant_mgr()->GetUIntConst(index);
+ std::unique_ptr<Instruction> new_access_chain(
+ new Instruction(context(), SpvOpAccessChain, ptr_type_id, TakeNextId(),
+ std::initializer_list<Operand>{
+ {SPV_OPERAND_TYPE_ID, {var->result_id()}},
+ {SPV_OPERAND_TYPE_ID, {index_id}},
+ }));
+ Instruction* inst = new_access_chain.get();
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(inst);
+ insert_before->InsertBefore(std::move(new_access_chain));
+ return inst;
+}
+
+void InterfaceVariableScalarReplacement::ReplaceAccessChainWith(
+ Instruction* access_chain,
+ const std::vector<uint32_t>& interface_var_component_indices,
+ Instruction* scalar_var,
+ std::unordered_map<Instruction*, Instruction*>* loads_to_component_values) {
+ std::vector<uint32_t> indexes;
+ for (uint32_t i = 1; i < access_chain->NumInOperands(); ++i) {
+ indexes.push_back(access_chain->GetSingleWordInOperand(i));
+ }
+
+ // Note that we have a strong assumption that |access_chain| has only a single
+ // index that is for the extra arrayness.
+ context()->get_def_use_mgr()->ForEachUser(
+ access_chain,
+ [this, access_chain, &indexes, &interface_var_component_indices,
+ scalar_var, loads_to_component_values](Instruction* user) {
+ switch (user->opcode()) {
+ case SpvOpAccessChain: {
+ UseBaseAccessChainForAccessChain(user, access_chain);
+ ReplaceAccessChainWith(user, interface_var_component_indices,
+ scalar_var, loads_to_component_values);
+ return;
+ }
+ case SpvOpStore: {
+ uint32_t value_id = user->GetSingleWordInOperand(1);
+ StoreComponentOfValueToAccessChainToScalarVar(
+ value_id, interface_var_component_indices, scalar_var, indexes,
+ user);
+ return;
+ }
+ case SpvOpLoad: {
+ Instruction* value =
+ LoadAccessChainToVar(scalar_var, indexes, user);
+ loads_to_component_values->insert({user, value});
+ return;
+ }
+ default:
+ break;
+ }
+ });
+}
+
+void InterfaceVariableScalarReplacement::CloneAnnotationForVariable(
+ Instruction* annotation_inst, uint32_t var_id) {
+ assert(annotation_inst->opcode() == SpvOpDecorate ||
+ annotation_inst->opcode() == SpvOpDecorateId ||
+ annotation_inst->opcode() == SpvOpDecorateString);
+ std::unique_ptr<Instruction> new_inst(annotation_inst->Clone(context()));
+ new_inst->SetInOperand(0, {var_id});
+ context()->AddAnnotationInst(std::move(new_inst));
+}
+
+bool InterfaceVariableScalarReplacement::ReplaceInterfaceVarInEntryPoint(
+ Instruction* interface_var, Instruction* entry_point,
+ uint32_t scalar_var_id) {
+ analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+ uint32_t interface_var_id = interface_var->result_id();
+ if (interface_vars_removed_from_entry_point_operands_.find(
+ interface_var_id) !=
+ interface_vars_removed_from_entry_point_operands_.end()) {
+ entry_point->AddOperand({SPV_OPERAND_TYPE_ID, {scalar_var_id}});
+ def_use_mgr->AnalyzeInstUse(entry_point);
+ return true;
+ }
+
+ bool success = !entry_point->WhileEachInId(
+ [&interface_var_id, &scalar_var_id](uint32_t* id) {
+ if (*id == interface_var_id) {
+ *id = scalar_var_id;
+ return false;
+ }
+ return true;
+ });
+ if (!success) {
+ std::string message(
+ "interface variable is not an operand of the entry point");
+ message += "\n " + interface_var->PrettyPrint(
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ message += "\n " + entry_point->PrettyPrint(
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str());
+ return false;
+ }
+
+ def_use_mgr->AnalyzeInstUse(entry_point);
+ interface_vars_removed_from_entry_point_operands_.insert(interface_var_id);
+ return true;
+}
+
+uint32_t InterfaceVariableScalarReplacement::GetPointeeTypeIdOfVar(
+ Instruction* var) {
+ assert(var->opcode() == SpvOpVariable);
+
+ uint32_t ptr_type_id = var->type_id();
+ analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+ Instruction* ptr_type_inst = def_use_mgr->GetDef(ptr_type_id);
+
+ assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
+ "Variable must have a pointer type.");
+ return ptr_type_inst->GetSingleWordInOperand(kOpTypePtrTypeInOperandIndex);
+}
+
+void InterfaceVariableScalarReplacement::StoreComponentOfValueToScalarVar(
+ uint32_t value_id, const std::vector<uint32_t>& component_indices,
+ Instruction* scalar_var, const uint32_t* extra_array_index,
+ Instruction* insert_before) {
+ uint32_t component_type_id = GetPointeeTypeIdOfVar(scalar_var);
+ Instruction* ptr = scalar_var;
+ if (extra_array_index) {
+ auto* ty_mgr = context()->get_type_mgr();
+ analysis::Array* array_type = ty_mgr->GetType(component_type_id)->AsArray();
+ assert(array_type != nullptr);
+ component_type_id = ty_mgr->GetTypeInstruction(array_type->element_type());
+ ptr = CreateAccessChainWithIndex(component_type_id, scalar_var,
+ *extra_array_index, insert_before);
+ }
+
+ StoreComponentOfValueTo(component_type_id, value_id, component_indices, ptr,
+ extra_array_index, insert_before);
+}
+
+Instruction* InterfaceVariableScalarReplacement::LoadScalarVar(
+ Instruction* scalar_var, const uint32_t* extra_array_index,
+ Instruction* insert_before) {
+ uint32_t component_type_id = GetPointeeTypeIdOfVar(scalar_var);
+ Instruction* ptr = scalar_var;
+ if (extra_array_index) {
+ auto* ty_mgr = context()->get_type_mgr();
+ analysis::Array* array_type = ty_mgr->GetType(component_type_id)->AsArray();
+ assert(array_type != nullptr);
+ component_type_id = ty_mgr->GetTypeInstruction(array_type->element_type());
+ ptr = CreateAccessChainWithIndex(component_type_id, scalar_var,
+ *extra_array_index, insert_before);
+ }
+
+ return CreateLoad(component_type_id, ptr, insert_before);
+}
+
+Instruction* InterfaceVariableScalarReplacement::CreateLoad(
+ uint32_t type_id, Instruction* ptr, Instruction* insert_before) {
+ std::unique_ptr<Instruction> load(
+ new Instruction(context(), SpvOpLoad, type_id, TakeNextId(),
+ std::initializer_list<Operand>{
+ {SPV_OPERAND_TYPE_ID, {ptr->result_id()}}}));
+ Instruction* load_inst = load.get();
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(load_inst);
+ insert_before->InsertBefore(std::move(load));
+ return load_inst;
+}
+
+void InterfaceVariableScalarReplacement::StoreComponentOfValueTo(
+ uint32_t component_type_id, uint32_t value_id,
+ const std::vector<uint32_t>& component_indices, Instruction* ptr,
+ const uint32_t* extra_array_index, Instruction* insert_before) {
+ std::unique_ptr<Instruction> composite_extract(CreateCompositeExtract(
+ component_type_id, value_id, component_indices, extra_array_index));
+
+ std::unique_ptr<Instruction> new_store(
+ new Instruction(context(), SpvOpStore));
+ new_store->AddOperand({SPV_OPERAND_TYPE_ID, {ptr->result_id()}});
+ new_store->AddOperand(
+ {SPV_OPERAND_TYPE_ID, {composite_extract->result_id()}});
+
+ analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+ def_use_mgr->AnalyzeInstDefUse(composite_extract.get());
+ def_use_mgr->AnalyzeInstDefUse(new_store.get());
+
+ insert_before->InsertBefore(std::move(composite_extract));
+ insert_before->InsertBefore(std::move(new_store));
+}
+
+Instruction* InterfaceVariableScalarReplacement::CreateCompositeExtract(
+ uint32_t type_id, uint32_t composite_id,
+ const std::vector<uint32_t>& indexes, const uint32_t* extra_first_index) {
+ uint32_t component_id = TakeNextId();
+ Instruction* composite_extract = new Instruction(
+ context(), SpvOpCompositeExtract, type_id, component_id,
+ std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {composite_id}}});
+ if (extra_first_index) {
+ composite_extract->AddOperand(
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {*extra_first_index}});
+ }
+ for (uint32_t index : indexes) {
+ composite_extract->AddOperand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}});
+ }
+ return composite_extract;
+}
+
+void InterfaceVariableScalarReplacement::
+ StoreComponentOfValueToAccessChainToScalarVar(
+ uint32_t value_id, const std::vector<uint32_t>& component_indices,
+ Instruction* scalar_var,
+ const std::vector<uint32_t>& access_chain_indices,
+ Instruction* insert_before) {
+ uint32_t component_type_id = GetPointeeTypeIdOfVar(scalar_var);
+ Instruction* ptr = scalar_var;
+ if (!access_chain_indices.empty()) {
+ ptr = CreateAccessChainToVar(component_type_id, scalar_var,
+ access_chain_indices, insert_before,
+ &component_type_id);
+ }
+
+ StoreComponentOfValueTo(component_type_id, value_id, component_indices, ptr,
+ nullptr, insert_before);
+}
+
+Instruction* InterfaceVariableScalarReplacement::LoadAccessChainToVar(
+ Instruction* var, const std::vector<uint32_t>& indexes,
+ Instruction* insert_before) {
+ uint32_t component_type_id = GetPointeeTypeIdOfVar(var);
+ Instruction* ptr = var;
+ if (!indexes.empty()) {
+ ptr = CreateAccessChainToVar(component_type_id, var, indexes, insert_before,
+ &component_type_id);
+ }
+
+ return CreateLoad(component_type_id, ptr, insert_before);
+}
+
+Instruction*
+InterfaceVariableScalarReplacement::CreateCompositeConstructForComponentOfLoad(
+ Instruction* load, uint32_t depth_to_component) {
+ analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+ uint32_t type_id = load->type_id();
+ if (depth_to_component != 0) {
+ type_id = GetComponentTypeOfArrayMatrix(def_use_mgr, load->type_id(),
+ depth_to_component);
+ }
+ uint32_t new_id = context()->TakeNextId();
+ std::unique_ptr<Instruction> new_composite_construct(
+ new Instruction(context(), SpvOpCompositeConstruct, type_id, new_id, {}));
+ Instruction* composite_construct = new_composite_construct.get();
+ def_use_mgr->AnalyzeInstDefUse(composite_construct);
+
+ // Insert |new_composite_construct| after |load|. When there are multiple
+ // recursive composite construct instructions for a load, we have to place the
+ // composite construct with a lower depth later because it constructs the
+ // composite that contains other composites with lower depths.
+ auto* insert_before = load->NextNode();
+ while (true) {
+ auto itr =
+ composite_ids_to_component_depths.find(insert_before->result_id());
+ if (itr == composite_ids_to_component_depths.end()) break;
+ if (itr->second <= depth_to_component) break;
+ insert_before = insert_before->NextNode();
+ }
+ insert_before->InsertBefore(std::move(new_composite_construct));
+ composite_ids_to_component_depths.insert({new_id, depth_to_component});
+ return composite_construct;
+}
+
+void InterfaceVariableScalarReplacement::AddComponentsToCompositesForLoads(
+ const std::unordered_map<Instruction*, Instruction*>&
+ loads_to_component_values,
+ std::unordered_map<Instruction*, Instruction*>* loads_to_composites,
+ uint32_t depth_to_component) {
+ analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+ for (auto& load_and_component_vale : loads_to_component_values) {
+ Instruction* load = load_and_component_vale.first;
+ Instruction* component_value = load_and_component_vale.second;
+ Instruction* composite_construct = nullptr;
+ auto itr = loads_to_composites->find(load);
+ if (itr == loads_to_composites->end()) {
+ composite_construct =
+ CreateCompositeConstructForComponentOfLoad(load, depth_to_component);
+ loads_to_composites->insert({load, composite_construct});
+ } else {
+ composite_construct = itr->second;
+ }
+ composite_construct->AddOperand(
+ {SPV_OPERAND_TYPE_ID, {component_value->result_id()}});
+ def_use_mgr->AnalyzeInstDefUse(composite_construct);
+ }
+}
+
+uint32_t InterfaceVariableScalarReplacement::GetArrayType(
+ uint32_t elem_type_id, uint32_t array_length) {
+ analysis::Type* elem_type = context()->get_type_mgr()->GetType(elem_type_id);
+ uint32_t array_length_id =
+ context()->get_constant_mgr()->GetUIntConst(array_length);
+ analysis::Array array_type(
+ elem_type,
+ analysis::Array::LengthInfo{array_length_id, {0, array_length}});
+ return context()->get_type_mgr()->GetTypeInstruction(&array_type);
+}
+
+uint32_t InterfaceVariableScalarReplacement::GetPointerType(
+ uint32_t type_id, SpvStorageClass storage_class) {
+ analysis::Type* type = context()->get_type_mgr()->GetType(type_id);
+ analysis::Pointer ptr_type(type, storage_class);
+ return context()->get_type_mgr()->GetTypeInstruction(&ptr_type);
+}
+
+InterfaceVariableScalarReplacement::NestedCompositeComponents
+InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForArray(
+ Instruction* interface_var_type, SpvStorageClass storage_class,
+ uint32_t extra_array_length) {
+ assert(interface_var_type->opcode() == SpvOpTypeArray);
+
+ analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+ uint32_t array_length = GetArrayLength(def_use_mgr, interface_var_type);
+ Instruction* elem_type = GetArrayElementType(def_use_mgr, interface_var_type);
+
+ NestedCompositeComponents scalar_vars;
+ while (array_length > 0) {
+ NestedCompositeComponents scalar_vars_for_element =
+ CreateScalarInterfaceVarsForReplacement(elem_type, storage_class,
+ extra_array_length);
+ scalar_vars.AddComponent(scalar_vars_for_element);
+ --array_length;
+ }
+ return scalar_vars;
+}
+
+InterfaceVariableScalarReplacement::NestedCompositeComponents
+InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForMatrix(
+ Instruction* interface_var_type, SpvStorageClass storage_class,
+ uint32_t extra_array_length) {
+ assert(interface_var_type->opcode() == SpvOpTypeMatrix);
+
+ analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+ uint32_t column_count = interface_var_type->GetSingleWordInOperand(
+ kOpTypeMatrixColCountInOperandIndex);
+ Instruction* column_type =
+ GetMatrixColumnType(def_use_mgr, interface_var_type);
+
+ NestedCompositeComponents scalar_vars;
+ while (column_count > 0) {
+ NestedCompositeComponents scalar_vars_for_column =
+ CreateScalarInterfaceVarsForReplacement(column_type, storage_class,
+ extra_array_length);
+ scalar_vars.AddComponent(scalar_vars_for_column);
+ --column_count;
+ }
+ return scalar_vars;
+}
+
+InterfaceVariableScalarReplacement::NestedCompositeComponents
+InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForReplacement(
+ Instruction* interface_var_type, SpvStorageClass storage_class,
+ uint32_t extra_array_length) {
+ // Handle array case.
+ if (interface_var_type->opcode() == SpvOpTypeArray) {
+ return CreateScalarInterfaceVarsForArray(interface_var_type, storage_class,
+ extra_array_length);
+ }
+
+ // Handle matrix case.
+ if (interface_var_type->opcode() == SpvOpTypeMatrix) {
+ return CreateScalarInterfaceVarsForMatrix(interface_var_type, storage_class,
+ extra_array_length);
+ }
+
+ // Handle scalar or vector case.
+ NestedCompositeComponents scalar_var;
+ uint32_t type_id = interface_var_type->result_id();
+ if (extra_array_length != 0) {
+ type_id = GetArrayType(type_id, extra_array_length);
+ }
+ uint32_t ptr_type_id =
+ context()->get_type_mgr()->FindPointerToType(type_id, storage_class);
+ uint32_t id = TakeNextId();
+ std::unique_ptr<Instruction> variable(
+ new Instruction(context(), SpvOpVariable, ptr_type_id, id,
+ std::initializer_list<Operand>{
+ {SPV_OPERAND_TYPE_STORAGE_CLASS,
+ {static_cast<uint32_t>(storage_class)}}}));
+ scalar_var.SetSingleComponentVariable(variable.get());
+ context()->AddGlobalValue(std::move(variable));
+ return scalar_var;
+}
+
+Instruction* InterfaceVariableScalarReplacement::GetTypeOfVariable(
+ Instruction* var) {
+ uint32_t pointee_type_id = GetPointeeTypeIdOfVar(var);
+ analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+ return def_use_mgr->GetDef(pointee_type_id);
+}
+
+Pass::Status InterfaceVariableScalarReplacement::Process() {
+ Pass::Status status = Status::SuccessWithoutChange;
+ for (Instruction& entry_point : get_module()->entry_points()) {
+ status =
+ CombineStatus(status, ReplaceInterfaceVarsWithScalars(entry_point));
+ }
+ return status;
+}
+
+bool InterfaceVariableScalarReplacement::
+ ReportErrorIfHasExtraArraynessForOtherEntry(Instruction* var) {
+ if (vars_with_extra_arrayness.find(var) == vars_with_extra_arrayness.end())
+ return false;
+
+ std::string message(
+ "A variable is arrayed for an entry point but it is not "
+ "arrayed for another entry point");
+ message +=
+ "\n " + var->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str());
+ return true;
+}
+
+bool InterfaceVariableScalarReplacement::
+ ReportErrorIfHasNoExtraArraynessForOtherEntry(Instruction* var) {
+ if (vars_without_extra_arrayness.find(var) ==
+ vars_without_extra_arrayness.end())
+ return false;
+
+ std::string message(
+ "A variable is not arrayed for an entry point but it is "
+ "arrayed for another entry point");
+ message +=
+ "\n " + var->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str());
+ return true;
+}
+
+Pass::Status
+InterfaceVariableScalarReplacement::ReplaceInterfaceVarsWithScalars(
+ Instruction& entry_point) {
+ std::vector<Instruction*> interface_vars =
+ CollectInterfaceVariables(entry_point);
+
+ Pass::Status status = Status::SuccessWithoutChange;
+ for (Instruction* interface_var : interface_vars) {
+ uint32_t location, component;
+ if (!GetVariableLocation(interface_var, &location)) continue;
+ if (!GetVariableComponent(interface_var, &component)) component = 0;
+
+ Instruction* interface_var_type = GetTypeOfVariable(interface_var);
+ uint32_t extra_array_length = 0;
+ if (HasExtraArrayness(entry_point, interface_var)) {
+ extra_array_length =
+ GetArrayLength(context()->get_def_use_mgr(), interface_var_type);
+ interface_var_type =
+ GetArrayElementType(context()->get_def_use_mgr(), interface_var_type);
+ vars_with_extra_arrayness.insert(interface_var);
+ } else {
+ vars_without_extra_arrayness.insert(interface_var);
+ }
+
+ if (!CheckExtraArraynessConflictBetweenEntries(interface_var,
+ extra_array_length != 0)) {
+ return Pass::Status::Failure;
+ }
+
+ if (interface_var_type->opcode() != SpvOpTypeArray &&
+ interface_var_type->opcode() != SpvOpTypeMatrix) {
+ continue;
+ }
+
+ if (!ReplaceInterfaceVariableWithScalars(interface_var, interface_var_type,
+ location, component,
+ extra_array_length)) {
+ return Pass::Status::Failure;
+ }
+ status = Pass::Status::SuccessWithChange;
+ }
+
+ return status;
+}
+
+} // namespace opt
+} // namespace spvtools
diff --git a/source/opt/interface_var_sroa.h b/source/opt/interface_var_sroa.h
new file mode 100644
index 00000000..23baad0a
--- /dev/null
+++ b/source/opt/interface_var_sroa.h
@@ -0,0 +1,401 @@
+// Copyright (c) 2022 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_INTERFACE_VAR_SROA_H_
+#define SOURCE_OPT_INTERFACE_VAR_SROA_H_
+
+#include <unordered_set>
+
+#include "source/opt/pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// See optimizer.hpp for documentation.
+//
+// Note that the current implementation of this pass covers only store, load,
+// access chain instructions for the interface variables. Supporting other types
+// of instructions is a future work.
+class InterfaceVariableScalarReplacement : public Pass {
+ public:
+ InterfaceVariableScalarReplacement() {}
+
+ const char* name() const override {
+ return "interface-variable-scalar-replacement";
+ }
+ Status Process() override;
+
+ IRContext::Analysis GetPreservedAnalyses() override {
+ return IRContext::kAnalysisDecorations | IRContext::kAnalysisDefUse |
+ IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
+ }
+
+ private:
+ // A struct containing components of a composite variable. If the composite
+ // consists of multiple or recursive components, |component_variable| is
+ // nullptr and |nested_composite_components| keeps the components. If it has a
+ // single component, |nested_composite_components| is empty and
+ // |component_variable| is the component. Note that each element of
+ // |nested_composite_components| has the NestedCompositeComponents struct as
+ // its type that can recursively keep the components.
+ struct NestedCompositeComponents {
+ NestedCompositeComponents() : component_variable(nullptr) {}
+
+ bool HasMultipleComponents() const {
+ return !nested_composite_components.empty();
+ }
+
+ const std::vector<NestedCompositeComponents>& GetComponents() const {
+ return nested_composite_components;
+ }
+
+ void AddComponent(const NestedCompositeComponents& component) {
+ nested_composite_components.push_back(component);
+ }
+
+ Instruction* GetComponentVariable() const { return component_variable; }
+
+ void SetSingleComponentVariable(Instruction* var) {
+ component_variable = var;
+ }
+
+ private:
+ std::vector<NestedCompositeComponents> nested_composite_components;
+ Instruction* component_variable;
+ };
+
+ // Collects all interface variables used by the |entry_point|.
+ std::vector<Instruction*> CollectInterfaceVariables(Instruction& entry_point);
+
+ // Returns whether |var| has the extra arrayness for the entry point
+ // |entry_point| or not.
+ bool HasExtraArrayness(Instruction& entry_point, Instruction* var);
+
+ // Finds a Location BuiltIn decoration of |var| and returns it via
+ // |location|. Returns true whether the location exists or not.
+ bool GetVariableLocation(Instruction* var, uint32_t* location);
+
+ // Finds a Component BuiltIn decoration of |var| and returns it via
+ // |component|. Returns true whether the component exists or not.
+ bool GetVariableComponent(Instruction* var, uint32_t* component);
+
+ // Returns the interface variable instruction whose result id is
+ // |interface_var_id|.
+ Instruction* GetInterfaceVariable(uint32_t interface_var_id);
+
+ // Returns the type of |var| as an instruction.
+ Instruction* GetTypeOfVariable(Instruction* var);
+
+ // Replaces an interface variable |interface_var| whose type is
+ // |interface_var_type| with scalars and returns whether it succeeds or not.
+ // |location| is the value of Location Decoration for |interface_var|.
+ // |component| is the value of Component Decoration for |interface_var|.
+ // If |extra_array_length| is 0, it means |interface_var| has a Patch
+ // decoration. Otherwise, |extra_array_length| denotes the length of the extra
+ // array of |interface_var|.
+ bool ReplaceInterfaceVariableWithScalars(Instruction* interface_var,
+ Instruction* interface_var_type,
+ uint32_t location,
+ uint32_t component,
+ uint32_t extra_array_length);
+
+ // Creates scalar variables with the storage classe |storage_class| to replace
+ // an interface variable whose type is |interface_var_type|. If
+ // |extra_array_length| is not zero, adds the extra arrayness to the created
+ // scalar variables.
+ NestedCompositeComponents CreateScalarInterfaceVarsForReplacement(
+ Instruction* interface_var_type, SpvStorageClass storage_class,
+ uint32_t extra_array_length);
+
+ // Creates scalar variables with the storage classe |storage_class| to replace
+ // the interface variable whose type is OpTypeArray |interface_var_type| with.
+ // If |extra_array_length| is not zero, adds the extra arrayness to all the
+ // scalar variables.
+ NestedCompositeComponents CreateScalarInterfaceVarsForArray(
+ Instruction* interface_var_type, SpvStorageClass storage_class,
+ uint32_t extra_array_length);
+
+ // Creates scalar variables with the storage classe |storage_class| to replace
+ // the interface variable whose type is OpTypeMatrix |interface_var_type|
+ // with. If |extra_array_length| is not zero, adds the extra arrayness to all
+ // the scalar variables.
+ NestedCompositeComponents CreateScalarInterfaceVarsForMatrix(
+ Instruction* interface_var_type, SpvStorageClass storage_class,
+ uint32_t extra_array_length);
+
+ // Recursively adds Location and Component decorations to variables in
+ // |vars| with |location| and |component|. Increases |location| by one after
+ // it actually adds Location and Component decorations for a variable.
+ void AddLocationAndComponentDecorations(const NestedCompositeComponents& vars,
+ uint32_t* location,
+ uint32_t component);
+
+ // Replaces the interface variable |interface_var| with
+ // |scalar_interface_vars| and returns whether it succeeds or not.
+ // |extra_arrayness| is the extra arrayness of the interface variable.
+ // |scalar_interface_vars| contains the nested variables to replace the
+ // interface variable with.
+ bool ReplaceInterfaceVarWith(
+ Instruction* interface_var, uint32_t extra_arrayness,
+ const NestedCompositeComponents& scalar_interface_vars);
+
+ // Replaces |interface_var| in the operands of instructions
+ // |interface_var_users| with |scalar_interface_vars|. This is a recursive
+ // method and |interface_var_component_indices| is used to specify which
+ // recursive component of |interface_var| is replaced. Returns composite
+ // construct instructions to be replaced with load instructions of
+ // |interface_var_users| via |loads_to_composites|. Returns composite
+ // construct instructions to be replaced with load instructions of access
+ // chain instructions in |interface_var_users| via
+ // |loads_for_access_chain_to_composites|.
+ bool ReplaceComponentsOfInterfaceVarWith(
+ Instruction* interface_var,
+ const std::vector<Instruction*>& interface_var_users,
+ const NestedCompositeComponents& scalar_interface_vars,
+ std::vector<uint32_t>& interface_var_component_indices,
+ const uint32_t* extra_array_index,
+ std::unordered_map<Instruction*, Instruction*>* loads_to_composites,
+ std::unordered_map<Instruction*, Instruction*>*
+ loads_for_access_chain_to_composites);
+
+ // Replaces |interface_var| in the operands of instructions
+ // |interface_var_users| with |components| that is a vector of components for
+ // the interface variable |interface_var|. This is a recursive method and
+ // |interface_var_component_indices| is used to specify which recursive
+ // component of |interface_var| is replaced. Returns composite construct
+ // instructions to be replaced with load instructions of |interface_var_users|
+ // via |loads_to_composites|. Returns composite construct instructions to be
+ // replaced with load instructions of access chain instructions in
+ // |interface_var_users| via |loads_for_access_chain_to_composites|.
+ bool ReplaceMultipleComponentsOfInterfaceVarWith(
+ Instruction* interface_var,
+ const std::vector<Instruction*>& interface_var_users,
+ const std::vector<NestedCompositeComponents>& components,
+ std::vector<uint32_t>& interface_var_component_indices,
+ const uint32_t* extra_array_index,
+ std::unordered_map<Instruction*, Instruction*>* loads_to_composites,
+ std::unordered_map<Instruction*, Instruction*>*
+ loads_for_access_chain_to_composites);
+
+ // Replaces a component of |interface_var| that is used as an operand of
+ // instruction |interface_var_user| with |scalar_var|.
+ // |interface_var_component_indices| is a vector of recursive indices for
+ // which recursive component of |interface_var| is replaced. If
+ // |interface_var_user| is a load, returns the component value via
+ // |loads_to_component_values|. If |interface_var_user| is an access chain,
+ // returns the component value for loads of |interface_var_user| via
+ // |loads_for_access_chain_to_component_values|.
+ bool ReplaceComponentOfInterfaceVarWith(
+ Instruction* interface_var, Instruction* interface_var_user,
+ Instruction* scalar_var,
+ const std::vector<uint32_t>& interface_var_component_indices,
+ const uint32_t* extra_array_index,
+ std::unordered_map<Instruction*, Instruction*>* loads_to_component_values,
+ std::unordered_map<Instruction*, Instruction*>*
+ loads_for_access_chain_to_component_values);
+
+ // Creates instructions to load |scalar_var| and inserts them before
+ // |insert_before|. If |extra_array_index| is not null, they load
+ // |extra_array_index| th component of |scalar_var| instead of |scalar_var|
+ // itself.
+ Instruction* LoadScalarVar(Instruction* scalar_var,
+ const uint32_t* extra_array_index,
+ Instruction* insert_before);
+
+ // Creates instructions to load an access chain to |var| and inserts them
+ // before |insert_before|. |Indexes| will be Indexes operand of the access
+ // chain.
+ Instruction* LoadAccessChainToVar(Instruction* var,
+ const std::vector<uint32_t>& indexes,
+ Instruction* insert_before);
+
+ // Creates instructions to store a component of an aggregate whose id is
+ // |value_id| to an access chain to |scalar_var| and inserts the created
+ // instructions before |insert_before|. To get the component, recursively
+ // traverses the aggregate with |component_indices| as indexes.
+ // Numbers in |access_chain_indices| are the Indexes operand of the access
+ // chain to |scalar_var|
+ void StoreComponentOfValueToAccessChainToScalarVar(
+ uint32_t value_id, const std::vector<uint32_t>& component_indices,
+ Instruction* scalar_var,
+ const std::vector<uint32_t>& access_chain_indices,
+ Instruction* insert_before);
+
+ // Creates instructions to store a component of an aggregate whose id is
+ // |value_id| to |scalar_var| and inserts the created instructions before
+ // |insert_before|. To get the component, recursively traverses the aggregate
+ // using |extra_array_index| and |component_indices| as indexes.
+ void StoreComponentOfValueToScalarVar(
+ uint32_t value_id, const std::vector<uint32_t>& component_indices,
+ Instruction* scalar_var, const uint32_t* extra_array_index,
+ Instruction* insert_before);
+
+ // Creates instructions to store a component of an aggregate whose id is
+ // |value_id| to |ptr| and inserts the created instructions before
+ // |insert_before|. To get the component, recursively traverses the aggregate
+ // using |extra_array_index| and |component_indices| as indexes.
+ // |component_type_id| is the id of the type instruction of the component.
+ void StoreComponentOfValueTo(uint32_t component_type_id, uint32_t value_id,
+ const std::vector<uint32_t>& component_indices,
+ Instruction* ptr,
+ const uint32_t* extra_array_index,
+ Instruction* insert_before);
+
+ // Creates new OpCompositeExtract with |type_id| for Result Type,
+ // |composite_id| for Composite operand, and |indexes| for Indexes operands.
+ // If |extra_first_index| is not nullptr, uses it as the first Indexes
+ // operand.
+ Instruction* CreateCompositeExtract(uint32_t type_id, uint32_t composite_id,
+ const std::vector<uint32_t>& indexes,
+ const uint32_t* extra_first_index);
+
+ // Creates a new OpLoad whose Result Type is |type_id| and Pointer operand is
+ // |ptr|. Inserts the new instruction before |insert_before|.
+ Instruction* CreateLoad(uint32_t type_id, Instruction* ptr,
+ Instruction* insert_before);
+
+ // Clones an annotation instruction |annotation_inst| and sets the target
+ // operand of the new annotation instruction as |var_id|.
+ void CloneAnnotationForVariable(Instruction* annotation_inst,
+ uint32_t var_id);
+
+ // Replaces the interface variable |interface_var| in the operands of the
+ // entry point |entry_point| with |scalar_var_id|. If it cannot find
+ // |interface_var| from the operands of the entry point |entry_point|, adds
+ // |scalar_var_id| as an operand of the entry point |entry_point|.
+ bool ReplaceInterfaceVarInEntryPoint(Instruction* interface_var,
+ Instruction* entry_point,
+ uint32_t scalar_var_id);
+
+ // Creates an access chain instruction whose Base operand is |var| and Indexes
+ // operand is |index|. |component_type_id| is the id of the type instruction
+ // that is the type of component. Inserts the new access chain before
+ // |insert_before|.
+ Instruction* CreateAccessChainWithIndex(uint32_t component_type_id,
+ Instruction* var, uint32_t index,
+ Instruction* insert_before);
+
+ // Returns the pointee type of the type of variable |var|.
+ uint32_t GetPointeeTypeIdOfVar(Instruction* var);
+
+ // Replaces the access chain |access_chain| and its users with a new access
+ // chain that points |scalar_var| as the Base operand having
+ // |interface_var_component_indices| as Indexes operands and users of the new
+ // access chain. When some of the users are load instructions, returns the
+ // original load instruction to the new instruction that loads a component of
+ // the original load value via |loads_to_component_values|.
+ void ReplaceAccessChainWith(
+ Instruction* access_chain,
+ const std::vector<uint32_t>& interface_var_component_indices,
+ Instruction* scalar_var,
+ std::unordered_map<Instruction*, Instruction*>*
+ loads_to_component_values);
+
+ // Assuming that |access_chain| is an access chain instruction whose Base
+ // operand is |base_access_chain|, replaces the operands of |access_chain|
+ // with operands of |base_access_chain| and Indexes operands of
+ // |access_chain|.
+ void UseBaseAccessChainForAccessChain(Instruction* access_chain,
+ Instruction* base_access_chain);
+
+ // Creates composite construct instructions for load instructions that are the
+ // keys of |loads_to_component_values| if no such composite construct
+ // instructions exist. Adds a component of the composite as an operand of the
+ // created composite construct instruction. Each value of
+ // |loads_to_component_values| is the component. Returns the created composite
+ // construct instructions using |loads_to_composites|. |depth_to_component| is
+ // the number of recursive access steps to get the component from the
+ // composite.
+ void AddComponentsToCompositesForLoads(
+ const std::unordered_map<Instruction*, Instruction*>&
+ loads_to_component_values,
+ std::unordered_map<Instruction*, Instruction*>* loads_to_composites,
+ uint32_t depth_to_component);
+
+ // Creates a composite construct instruction for a component of the value of
+ // instruction |load| in |depth_to_component| th recursive depth and inserts
+ // it after |load|.
+ Instruction* CreateCompositeConstructForComponentOfLoad(
+ Instruction* load, uint32_t depth_to_component);
+
+ // Creates a new access chain instruction that points to variable |var| whose
+ // type is the instruction with |var_type_id| and inserts it before
+ // |insert_before|. The new access chain will have |index_ids| for Indexes
+ // operands. Returns the type id of the component that is pointed by the new
+ // access chain via |component_type_id|.
+ Instruction* CreateAccessChainToVar(uint32_t var_type_id, Instruction* var,
+ const std::vector<uint32_t>& index_ids,
+ Instruction* insert_before,
+ uint32_t* component_type_id);
+
+ // Returns the result id of OpTypeArray instrunction whose Element Type
+ // operand is |elem_type_id| and Length operand is |array_length|.
+ uint32_t GetArrayType(uint32_t elem_type_id, uint32_t array_length);
+
+ // Returns the result id of OpTypePointer instrunction whose Type
+ // operand is |type_id| and Storage Class operand is |storage_class|.
+ uint32_t GetPointerType(uint32_t type_id, SpvStorageClass storage_class);
+
+ // Kills an instrunction |inst| and its users.
+ void KillInstructionAndUsers(Instruction* inst);
+
+ // Kills a vector of instrunctions |insts| and their users.
+ void KillInstructionsAndUsers(const std::vector<Instruction*>& insts);
+
+ // Kills all OpDecorate instructions for Location and Component of the
+ // variable whose id is |var_id|.
+ void KillLocationAndComponentDecorations(uint32_t var_id);
+
+ // If |var| has the extra arrayness for an entry point, reports an error and
+ // returns true. Otherwise, returns false.
+ bool ReportErrorIfHasExtraArraynessForOtherEntry(Instruction* var);
+
+ // If |var| does not have the extra arrayness for an entry point, reports an
+ // error and returns true. Otherwise, returns false.
+ bool ReportErrorIfHasNoExtraArraynessForOtherEntry(Instruction* var);
+
+ // If |interface_var| has the extra arrayness for an entry point but it does
+ // not have one for another entry point, reports an error and returns false.
+ // Otherwise, returns true. |has_extra_arrayness| denotes whether it has an
+ // extra arrayness for an entry point or not.
+ bool CheckExtraArraynessConflictBetweenEntries(Instruction* interface_var,
+ bool has_extra_arrayness);
+
+ // Conducts the scalar replacement for the interface variables used by the
+ // |entry_point|.
+ Pass::Status ReplaceInterfaceVarsWithScalars(Instruction& entry_point);
+
+ // A set of interface variable ids that were already removed from operands of
+ // the entry point.
+ std::unordered_set<uint32_t>
+ interface_vars_removed_from_entry_point_operands_;
+
+ // A mapping from ids of new composite construct instructions that load
+ // instructions are replaced with to the recursive depth of the component of
+ // load that the new component construct instruction is used for.
+ std::unordered_map<uint32_t, uint32_t> composite_ids_to_component_depths;
+
+ // A set of interface variables with the extra arrayness for any of the entry
+ // points.
+ std::unordered_set<Instruction*> vars_with_extra_arrayness;
+
+ // A set of interface variables without the extra arrayness for any of the
+ // entry points.
+ std::unordered_set<Instruction*> vars_without_extra_arrayness;
+};
+
+} // namespace opt
+} // namespace spvtools
+
+#endif // SOURCE_OPT_INTERFACE_VAR_SROA_H_
diff --git a/source/opt/interp_fixup_pass.cpp b/source/opt/interp_fixup_pass.cpp
index ad29e6a7..e8cdd99f 100644
--- a/source/opt/interp_fixup_pass.cpp
+++ b/source/opt/interp_fixup_pass.cpp
@@ -31,13 +31,6 @@ namespace {
// Input Operand Indices
static const int kSpvVariableStorageClassInIdx = 0;
-// Avoid unused variable warning/error on Linux
-#ifndef NDEBUG
-#define USE_ASSERT(x) assert(x)
-#else
-#define USE_ASSERT(x) ((void)(x))
-#endif
-
// Folding rule function which attempts to replace |op(OpLoad(a),...)|
// by |op(a,...)|, where |op| is one of the GLSLstd450 InterpolateAt*
// instructions. Returns true if replaced, false otherwise.
diff --git a/source/opt/ir_builder.h b/source/opt/ir_builder.h
index 4433cf0d..9d4fa8fe 100644
--- a/source/opt/ir_builder.h
+++ b/source/opt/ir_builder.h
@@ -487,6 +487,15 @@ class InstructionBuilder {
return AddInstruction(std::move(new_inst));
}
+ Instruction* AddVariable(uint32_t type_id, uint32_t storage_class) {
+ std::vector<Operand> operands;
+ operands.push_back({SPV_OPERAND_TYPE_ID, {storage_class}});
+ std::unique_ptr<Instruction> new_inst(
+ new Instruction(GetContext(), SpvOpVariable, type_id,
+ GetContext()->TakeNextId(), operands));
+ return AddInstruction(std::move(new_inst));
+ }
+
Instruction* AddStore(uint32_t ptr_id, uint32_t obj_id) {
std::vector<Operand> operands;
operands.push_back({SPV_OPERAND_TYPE_ID, {ptr_id}});
diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp
index 5b0beeb2..c9c3f1b5 100644
--- a/source/opt/ir_context.cpp
+++ b/source/opt/ir_context.cpp
@@ -41,6 +41,8 @@ namespace spvtools {
namespace opt {
void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) {
+ set = Analysis(set & ~valid_analyses_);
+
if (set & kAnalysisDefUse) {
BuildDefUseManager();
}
@@ -924,6 +926,19 @@ bool IRContext::ProcessCallTreeFromRoots(ProcessFunction& pfn,
return modified;
}
+void IRContext::CollectCallTreeFromRoots(unsigned entryId,
+ std::unordered_set<uint32_t>* funcs) {
+ std::queue<uint32_t> roots;
+ roots.push(entryId);
+ while (!roots.empty()) {
+ const uint32_t fi = roots.front();
+ roots.pop();
+ funcs->insert(fi);
+ Function* fn = GetFunction(fi);
+ AddCalls(fn, &roots);
+ }
+}
+
void IRContext::EmitErrorMessage(std::string message, Instruction* inst) {
if (!consumer()) {
return;
diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h
index 274dd14e..2f27942b 100644
--- a/source/opt/ir_context.h
+++ b/source/opt/ir_context.h
@@ -411,6 +411,10 @@ class IRContext {
void CollectNonSemanticTree(Instruction* inst,
std::unordered_set<Instruction*>* to_kill);
+ // Collect function reachable from |entryId|, returns |funcs|
+ void CollectCallTreeFromRoots(unsigned entryId,
+ std::unordered_set<uint32_t>* funcs);
+
// Returns true if all of the given analyses are valid.
bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; }
@@ -867,8 +871,7 @@ inline IRContext::Analysis operator|(IRContext::Analysis lhs,
inline IRContext::Analysis& operator|=(IRContext::Analysis& lhs,
IRContext::Analysis rhs) {
- lhs = static_cast<IRContext::Analysis>(static_cast<int>(lhs) |
- static_cast<int>(rhs));
+ lhs = lhs | rhs;
return lhs;
}
@@ -1091,6 +1094,9 @@ void IRContext::AddDebug2Inst(std::unique_ptr<Instruction>&& d) {
id_to_name_->insert({d->GetSingleWordInOperand(0), d.get()});
}
}
+ if (AreAnalysesValid(kAnalysisDefUse)) {
+ get_def_use_mgr()->AnalyzeInstDefUse(d.get());
+ }
module()->AddDebug2Inst(std::move(d));
}
diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp
index a82b530e..734ad554 100644
--- a/source/opt/ir_loader.cpp
+++ b/source/opt/ir_loader.cpp
@@ -187,9 +187,12 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
module_->AddExtInstImport(std::move(spv_inst));
} else if (opcode == SpvOpMemoryModel) {
module_->SetMemoryModel(std::move(spv_inst));
+ } else if (opcode == SpvOpSamplerImageAddressingModeNV) {
+ module_->SetSampledImageAddressMode(std::move(spv_inst));
} else if (opcode == SpvOpEntryPoint) {
module_->AddEntryPoint(std::move(spv_inst));
- } else if (opcode == SpvOpExecutionMode) {
+ } else if (opcode == SpvOpExecutionMode ||
+ opcode == SpvOpExecutionModeId) {
module_->AddExecutionMode(std::move(spv_inst));
} else if (IsDebug1Inst(opcode)) {
module_->AddDebug1Inst(std::move(spv_inst));
diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp
index d2059f5c..d11682f3 100644
--- a/source/opt/local_access_chain_convert_pass.cpp
+++ b/source/opt/local_access_chain_convert_pass.cpp
@@ -28,8 +28,6 @@ namespace {
const uint32_t kStoreValIdInIdx = 1;
const uint32_t kAccessChainPtrIdInIdx = 0;
-const uint32_t kConstantValueInIdx = 0;
-const uint32_t kTypeIntWidthInIdx = 0;
} // anonymous namespace
@@ -67,7 +65,19 @@ void LocalAccessChainConvertPass::AppendConstantOperands(
ptrInst->ForEachInId([&iidIdx, &in_opnds, this](const uint32_t* iid) {
if (iidIdx > 0) {
const Instruction* cInst = get_def_use_mgr()->GetDef(*iid);
- uint32_t val = cInst->GetSingleWordInOperand(kConstantValueInIdx);
+ const auto* constant_value =
+ context()->get_constant_mgr()->GetConstantFromInst(cInst);
+ assert(constant_value != nullptr &&
+ "Expecting the index to be a constant.");
+
+ // We take the sign extended value because OpAccessChain interprets the
+ // index as signed.
+ int64_t long_value = constant_value->GetSignExtendedValue();
+ assert(long_value <= UINT32_MAX && long_value >= 0 &&
+ "The index value is too large for a composite insert or extract "
+ "instruction.");
+
+ uint32_t val = static_cast<uint32_t>(long_value);
in_opnds->push_back(
{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {val}});
}
@@ -169,13 +179,18 @@ bool LocalAccessChainConvertPass::GenAccessChainStoreReplacement(
return true;
}
-bool LocalAccessChainConvertPass::IsConstantIndexAccessChain(
+bool LocalAccessChainConvertPass::Is32BitConstantIndexAccessChain(
const Instruction* acp) const {
uint32_t inIdx = 0;
return acp->WhileEachInId([&inIdx, this](const uint32_t* tid) {
if (inIdx > 0) {
Instruction* opInst = get_def_use_mgr()->GetDef(*tid);
if (opInst->opcode() != SpvOpConstant) return false;
+ const auto* index =
+ context()->get_constant_mgr()->GetConstantFromInst(opInst);
+ int64_t index_value = index->GetSignExtendedValue();
+ if (index_value > UINT32_MAX) return false;
+ if (index_value < 0) return false;
}
++inIdx;
return true;
@@ -224,14 +239,21 @@ void LocalAccessChainConvertPass::FindTargetVars(Function* func) {
}
// Rule out variables with nested access chains
// TODO(): Convert nested access chains
- if (IsNonPtrAccessChain(op) && ptrInst->GetSingleWordInOperand(
+ bool is_non_ptr_access_chain = IsNonPtrAccessChain(op);
+ if (is_non_ptr_access_chain && ptrInst->GetSingleWordInOperand(
kAccessChainPtrIdInIdx) != varId) {
seen_non_target_vars_.insert(varId);
seen_target_vars_.erase(varId);
break;
}
// Rule out variables accessed with non-constant indices
- if (!IsConstantIndexAccessChain(ptrInst)) {
+ if (!Is32BitConstantIndexAccessChain(ptrInst)) {
+ seen_non_target_vars_.insert(varId);
+ seen_target_vars_.erase(varId);
+ break;
+ }
+
+ if (is_non_ptr_access_chain && AnyIndexIsOutOfBounds(ptrInst)) {
seen_non_target_vars_.insert(varId);
seen_target_vars_.erase(varId);
break;
@@ -349,12 +371,6 @@ bool LocalAccessChainConvertPass::AllExtensionsSupported() const {
}
Pass::Status LocalAccessChainConvertPass::ProcessImpl() {
- // If non-32-bit integer type in module, terminate processing
- // TODO(): Handle non-32-bit integer constants in access chains
- for (const Instruction& inst : get_module()->types_values())
- if (inst.opcode() == SpvOpTypeInt &&
- inst.GetSingleWordInOperand(kTypeIntWidthInIdx) != 32)
- return Status::SuccessWithoutChange;
// Do not process if module contains OpGroupDecorate. Additional
// support required in KillNamesAndDecorates().
// TODO(greg-lunarg): Add support for OpGroupDecorate
@@ -434,8 +450,47 @@ void LocalAccessChainConvertPass::InitExtensions() {
"SPV_KHR_integer_dot_product",
"SPV_EXT_shader_image_int64",
"SPV_KHR_non_semantic_info",
+ "SPV_KHR_uniform_group_instructions",
+ "SPV_KHR_fragment_shader_barycentric",
});
}
+bool LocalAccessChainConvertPass::AnyIndexIsOutOfBounds(
+ const Instruction* access_chain_inst) {
+ assert(IsNonPtrAccessChain(access_chain_inst->opcode()));
+
+ analysis::TypeManager* type_mgr = context()->get_type_mgr();
+ analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
+ auto constants = const_mgr->GetOperandConstants(access_chain_inst);
+ uint32_t base_pointer_id = access_chain_inst->GetSingleWordInOperand(0);
+ Instruction* base_pointer = get_def_use_mgr()->GetDef(base_pointer_id);
+ const analysis::Pointer* base_pointer_type =
+ type_mgr->GetType(base_pointer->type_id())->AsPointer();
+ assert(base_pointer_type != nullptr &&
+ "The base of the access chain is not a pointer.");
+ const analysis::Type* current_type = base_pointer_type->pointee_type();
+ for (uint32_t i = 1; i < access_chain_inst->NumInOperands(); ++i) {
+ if (IsIndexOutOfBounds(constants[i], current_type)) {
+ return true;
+ }
+
+ uint32_t index =
+ (constants[i]
+ ? static_cast<uint32_t>(constants[i]->GetZeroExtendedValue())
+ : 0);
+ current_type = type_mgr->GetMemberType(current_type, {index});
+ }
+
+ return false;
+}
+
+bool LocalAccessChainConvertPass::IsIndexOutOfBounds(
+ const analysis::Constant* index, const analysis::Type* type) const {
+ if (index == nullptr) {
+ return false;
+ }
+ return index->GetZeroExtendedValue() >= type->NumberOfComponents();
+}
+
} // namespace opt
} // namespace spvtools
diff --git a/source/opt/local_access_chain_convert_pass.h b/source/opt/local_access_chain_convert_pass.h
index a51660f1..c3731b1c 100644
--- a/source/opt/local_access_chain_convert_pass.h
+++ b/source/opt/local_access_chain_convert_pass.h
@@ -94,8 +94,9 @@ class LocalAccessChainConvertPass : public MemPass {
bool ReplaceAccessChainLoad(const Instruction* address_inst,
Instruction* original_load);
- // Return true if all indices of access chain |acp| are OpConstant integers
- bool IsConstantIndexAccessChain(const Instruction* acp) const;
+ // Return true if all indices of the access chain |acp| are OpConstant
+ // integers whose signed values can be represented as unsigned 32-bit values.
+ bool Is32BitConstantIndexAccessChain(const Instruction* acp) const;
// Identify all function scope variables of target type which are
// accessed only with loads, stores and access chains with constant
@@ -110,6 +111,17 @@ class LocalAccessChainConvertPass : public MemPass {
// Returns a status to indicate success or failure, and change or no change.
Status ConvertLocalAccessChains(Function* func);
+ // Returns true one of the indexes in the |access_chain_inst| is definitly out
+ // of bounds. If the size of the type or the value of the index is unknown,
+ // then it will be considered in-bounds.
+ bool AnyIndexIsOutOfBounds(const Instruction* access_chain_inst);
+
+ // Returns true if getting element |index| from |type| would be out-of-bounds.
+ // If |index| is nullptr or the size of the type are unknown, then it will be
+ // considered in-bounds.
+ bool IsIndexOutOfBounds(const analysis::Constant* index,
+ const analysis::Type* type) const;
+
// Initialize extensions allowlist
void InitExtensions();
diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp
index f48c56aa..a58e8e4c 100644
--- a/source/opt/local_single_block_elim_pass.cpp
+++ b/source/opt/local_single_block_elim_pass.cpp
@@ -286,6 +286,8 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
"SPV_KHR_integer_dot_product",
"SPV_EXT_shader_image_int64",
"SPV_KHR_non_semantic_info",
+ "SPV_KHR_uniform_group_instructions",
+ "SPV_KHR_fragment_shader_barycentric",
});
}
diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp
index 123d03bf..81648c7b 100644
--- a/source/opt/local_single_store_elim_pass.cpp
+++ b/source/opt/local_single_store_elim_pass.cpp
@@ -139,6 +139,8 @@ void LocalSingleStoreElimPass::InitExtensionAllowList() {
"SPV_KHR_integer_dot_product",
"SPV_EXT_shader_image_int64",
"SPV_KHR_non_semantic_info",
+ "SPV_KHR_uniform_group_instructions",
+ "SPV_KHR_fragment_shader_barycentric",
});
}
bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
@@ -173,29 +175,9 @@ bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
bool LocalSingleStoreElimPass::RewriteDebugDeclares(Instruction* store_inst,
uint32_t var_id) {
- std::unordered_set<Instruction*> invisible_decls;
uint32_t value_id = store_inst->GetSingleWordInOperand(1);
- bool modified =
- context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(
- store_inst, var_id, value_id, store_inst, &invisible_decls);
-
- // For cases like the argument passing for an inlined function, the value
- // assignment is out of DebugDeclare's scope, but we have to preserve the
- // value assignment information using DebugValue. Generally, we need
- // ssa-rewrite analysis to decide a proper value assignment but at this point
- // we confirm that |var_id| has a single store. We can safely add DebugValue.
- if (!invisible_decls.empty()) {
- BasicBlock* store_block = context()->get_instr_block(store_inst);
- DominatorAnalysis* dominator_analysis =
- context()->GetDominatorAnalysis(store_block->GetParent());
- for (auto* decl : invisible_decls) {
- if (dominator_analysis->Dominates(store_inst, decl)) {
- context()->get_debug_info_mgr()->AddDebugValueForDecl(decl, value_id,
- decl, store_inst);
- modified = true;
- }
- }
- }
+ bool modified = context()->get_debug_info_mgr()->AddDebugValueForVariable(
+ store_inst, var_id, value_id, store_inst);
modified |= context()->get_debug_info_mgr()->KillDebugDeclares(var_id);
return modified;
}
diff --git a/source/opt/loop_descriptor.cpp b/source/opt/loop_descriptor.cpp
index 9bc495e5..13982d18 100644
--- a/source/opt/loop_descriptor.cpp
+++ b/source/opt/loop_descriptor.cpp
@@ -497,7 +497,8 @@ void Loop::ComputeLoopStructuredOrder(
// continue blocks that must be copied to retain the structured order.
// The structured order will include these.
std::list<BasicBlock*> order;
- cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_, &order);
+ cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_,
+ loop_merge_, &order);
for (BasicBlock* bb : order) {
if (bb == GetMergeBlock()) {
break;
@@ -754,6 +755,10 @@ bool Loop::FindNumberOfIterations(const Instruction* induction,
// |step_value| is NOT cleanly divisible then we add one to the sum.
int64_t Loop::GetIterations(SpvOp condition, int64_t condition_value,
int64_t init_value, int64_t step_value) const {
+ if (step_value == 0) {
+ return 0;
+ }
+
int64_t diff = 0;
switch (condition) {
diff --git a/source/opt/loop_descriptor.h b/source/opt/loop_descriptor.h
index e88ff936..df012274 100644
--- a/source/opt/loop_descriptor.h
+++ b/source/opt/loop_descriptor.h
@@ -398,7 +398,8 @@ class Loop {
// Each different loop |condition| affects how we calculate the number of
// iterations using the |condition_value|, |init_value|, and |step_values| of
// the induction variable. This method will return the number of iterations in
- // a loop with those values for a given |condition|.
+ // a loop with those values for a given |condition|. Returns 0 if the number
+ // of iterations could not be computed.
int64_t GetIterations(SpvOp condition, int64_t condition_value,
int64_t init_value, int64_t step_value) const;
diff --git a/source/opt/loop_unroller.cpp b/source/opt/loop_unroller.cpp
index 28ff0729..6f4e6f41 100644
--- a/source/opt/loop_unroller.cpp
+++ b/source/opt/loop_unroller.cpp
@@ -384,6 +384,7 @@ void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
std::unique_ptr<Instruction> new_label{new Instruction(
context_, SpvOp::SpvOpLabel, 0, context_->TakeNextId(), {})};
std::unique_ptr<BasicBlock> new_exit_bb{new BasicBlock(std::move(new_label))};
+ new_exit_bb->SetParent(&function_);
// Save the id of the block before we move it.
uint32_t new_merge_id = new_exit_bb->id();
@@ -996,6 +997,20 @@ bool LoopUtils::CanPerformUnroll() {
if (!loop_->FindNumberOfIterations(induction, &*condition->ctail(), nullptr))
return false;
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ // ClusterFuzz/OSS-Fuzz is likely to yield examples with very high loop
+ // iteration counts. This can cause timeouts and memouts during fuzzing that
+ // are not classed as bugs. To avoid this noise, loop unrolling is not applied
+ // to loops with large iteration counts when fuzzing.
+ const size_t kFuzzerIterationLimit = 100;
+ size_t num_iterations;
+ loop_->FindNumberOfIterations(induction, &*condition->ctail(),
+ &num_iterations);
+ if (num_iterations > kFuzzerIterationLimit) {
+ return false;
+ }
+#endif
+
// Make sure the latch block is a unconditional branch to the header
// block.
const Instruction& branch = *loop_->GetLatchBlock()->ctail();
diff --git a/source/opt/merge_return_pass.cpp b/source/opt/merge_return_pass.cpp
index a962a7cc..7710deae 100644
--- a/source/opt/merge_return_pass.cpp
+++ b/source/opt/merge_return_pass.cpp
@@ -431,6 +431,7 @@ bool MergeReturnPass::BreakFromConstruct(
std::list<BasicBlock*>* order, Instruction* break_merge_inst) {
// Make sure the CFG is build here. If we don't then it becomes very hard
// to know which new blocks need to be updated.
+ context()->InvalidateAnalyses(IRContext::kAnalysisCFG);
context()->BuildInvalidAnalyses(IRContext::kAnalysisCFG);
// When predicating, be aware of whether this block is a header block, a
diff --git a/source/opt/merge_return_pass.h b/source/opt/merge_return_pass.h
index a35cf269..d15db2f6 100644
--- a/source/opt/merge_return_pass.h
+++ b/source/opt/merge_return_pass.h
@@ -118,8 +118,6 @@ class MergeReturnPass : public MemPass {
StructuredControlState(Instruction* break_merge, Instruction* merge)
: break_merge_(break_merge), current_merge_(merge) {}
- StructuredControlState(const StructuredControlState&) = default;
-
bool InBreakable() const { return break_merge_; }
bool InStructuredFlow() const { return CurrentMergeId() != 0; }
diff --git a/source/opt/module.cpp b/source/opt/module.cpp
index 5983abb1..c98af8f5 100644
--- a/source/opt/module.cpp
+++ b/source/opt/module.cpp
@@ -90,6 +90,8 @@ void Module::ForEachInst(const std::function<void(Instruction*)>& f,
DELEGATE(extensions_);
DELEGATE(ext_inst_imports_);
if (memory_model_) memory_model_->ForEachInst(f, run_on_debug_line_insts);
+ if (sampled_image_address_mode_)
+ sampled_image_address_mode_->ForEachInst(f, run_on_debug_line_insts);
DELEGATE(entry_points_);
DELEGATE(execution_modes_);
DELEGATE(debugs1_);
@@ -114,6 +116,9 @@ void Module::ForEachInst(const std::function<void(const Instruction*)>& f,
if (memory_model_)
static_cast<const Instruction*>(memory_model_.get())
->ForEachInst(f, run_on_debug_line_insts);
+ if (sampled_image_address_mode_)
+ static_cast<const Instruction*>(sampled_image_address_mode_.get())
+ ->ForEachInst(f, run_on_debug_line_insts);
for (auto& i : entry_points_) DELEGATE(i);
for (auto& i : execution_modes_) DELEGATE(i);
for (auto& i : debugs1_) DELEGATE(i);
diff --git a/source/opt/module.h b/source/opt/module.h
index 230be709..7a6be460 100644
--- a/source/opt/module.h
+++ b/source/opt/module.h
@@ -83,6 +83,9 @@ class Module {
// Set the memory model for this module.
inline void SetMemoryModel(std::unique_ptr<Instruction> m);
+ // Set the sampled image addressing mode for this module.
+ inline void SetSampledImageAddressMode(std::unique_ptr<Instruction> m);
+
// Appends an entry point instruction to this module.
inline void AddEntryPoint(std::unique_ptr<Instruction> e);
@@ -158,12 +161,20 @@ class Module {
inline IteratorRange<inst_iterator> ext_inst_imports();
inline IteratorRange<const_inst_iterator> ext_inst_imports() const;
- // Return the memory model instruction contained inthis module.
+ // Return the memory model instruction contained in this module.
inline Instruction* GetMemoryModel() { return memory_model_.get(); }
inline const Instruction* GetMemoryModel() const {
return memory_model_.get();
}
+ // Return the sampled image address mode instruction contained in this module.
+ inline Instruction* GetSampledImageAddressMode() {
+ return sampled_image_address_mode_.get();
+ }
+ inline const Instruction* GetSampledImageAddressMode() const {
+ return sampled_image_address_mode_.get();
+ }
+
// There are several kinds of debug instructions, according to where they can
// appear in the logical layout of a module:
// - Section 7a: OpString, OpSourceExtension, OpSource, OpSourceContinued
@@ -288,6 +299,8 @@ class Module {
InstructionList ext_inst_imports_;
// A module only has one memory model instruction.
std::unique_ptr<Instruction> memory_model_;
+ // A module can only have one optional sampled image addressing mode
+ std::unique_ptr<Instruction> sampled_image_address_mode_;
InstructionList entry_points_;
InstructionList execution_modes_;
InstructionList debugs1_;
@@ -326,6 +339,10 @@ inline void Module::SetMemoryModel(std::unique_ptr<Instruction> m) {
memory_model_ = std::move(m);
}
+inline void Module::SetSampledImageAddressMode(std::unique_ptr<Instruction> m) {
+ sampled_image_address_mode_ = std::move(m);
+}
+
inline void Module::AddEntryPoint(std::unique_ptr<Instruction> e) {
entry_points_.push_back(std::move(e));
}
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp
index 330093e4..381589b5 100644
--- a/source/opt/optimizer.cpp
+++ b/source/opt/optimizer.cpp
@@ -521,6 +521,12 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
RegisterPass(CreateAmdExtToKhrPass());
} else if (pass_name == "interpolate-fixup") {
RegisterPass(CreateInterpolateFixupPass());
+ } else if (pass_name == "remove-dont-inline") {
+ RegisterPass(CreateRemoveDontInlinePass());
+ } else if (pass_name == "eliminate-dead-input-components") {
+ RegisterPass(CreateEliminateDeadInputComponentsPass());
+ } else if (pass_name == "fix-func-call-param") {
+ RegisterPass(CreateFixFuncCallArgumentsPass());
} else if (pass_name == "convert-to-sampled-image") {
if (pass_args.size() > 0) {
auto descriptor_set_binding_pairs =
@@ -617,10 +623,16 @@ bool Optimizer::Run(const uint32_t* original_binary,
assert(optimized_binary_with_nop.size() == original_binary_size &&
"Binary size unexpectedly changed despite the optimizer saying "
"there was no change");
- assert(memcmp(optimized_binary_with_nop.data(), original_binary,
- original_binary_size) == 0 &&
- "Binary content unexpectedly changed despite the optimizer saying "
- "there was no change");
+
+ // Compare the magic number to make sure the binaries were encoded in the
+ // endianness. If not, the contents of the binaries will be different, so
+ // do not check the contents.
+ if (optimized_binary_with_nop[0] == original_binary[0]) {
+ assert(memcmp(optimized_binary_with_nop.data(), original_binary,
+ original_binary_size) == 0 &&
+ "Binary content unexpectedly changed despite the optimizer saying "
+ "there was no change");
+ }
}
#endif // !NDEBUG
@@ -1002,6 +1014,11 @@ Optimizer::PassToken CreateInterpolateFixupPass() {
MakeUnique<opt::InterpFixupPass>());
}
+Optimizer::PassToken CreateEliminateDeadInputComponentsPass() {
+ return MakeUnique<Optimizer::PassToken::Impl>(
+ MakeUnique<opt::EliminateDeadInputComponentsPass>());
+}
+
Optimizer::PassToken CreateConvertToSampledImagePass(
const std::vector<opt::DescriptorSetAndBinding>&
descriptor_set_binding_pairs) {
@@ -1009,4 +1026,18 @@ Optimizer::PassToken CreateConvertToSampledImagePass(
MakeUnique<opt::ConvertToSampledImagePass>(descriptor_set_binding_pairs));
}
+Optimizer::PassToken CreateInterfaceVariableScalarReplacementPass() {
+ return MakeUnique<Optimizer::PassToken::Impl>(
+ MakeUnique<opt::InterfaceVariableScalarReplacement>());
+}
+
+Optimizer::PassToken CreateRemoveDontInlinePass() {
+ return MakeUnique<Optimizer::PassToken::Impl>(
+ MakeUnique<opt::RemoveDontInline>());
+}
+
+Optimizer::PassToken CreateFixFuncCallArgumentsPass() {
+ return MakeUnique<Optimizer::PassToken::Impl>(
+ MakeUnique<opt::FixFuncCallArgumentsPass>());
+}
} // namespace spvtools
diff --git a/source/opt/pass.h b/source/opt/pass.h
index 4a8ea674..b2303e23 100644
--- a/source/opt/pass.h
+++ b/source/opt/pass.h
@@ -28,6 +28,13 @@
#include "spirv-tools/libspirv.hpp"
#include "types.h"
+// Avoid unused variable warning/error on Linux
+#ifndef NDEBUG
+#define USE_ASSERT(x) assert(x)
+#else
+#define USE_ASSERT(x) ((void)(x))
+#endif
+
namespace spvtools {
namespace opt {
diff --git a/source/opt/pass_manager.cpp b/source/opt/pass_manager.cpp
index a73ff7cf..d3c47e7f 100644
--- a/source/opt/pass_manager.cpp
+++ b/source/opt/pass_manager.cpp
@@ -39,7 +39,7 @@ Pass::Status PassManager::Run(IRContext* context) {
t.SetMessageConsumer(consumer());
std::string disassembly;
std::string pass_name = (pass ? pass->name() : "");
- if (!t.Disassemble(binary, &disassembly, 0)) {
+ if (!t.Disassemble(binary, &disassembly)) {
std::string msg = "Disassembly failed before pass ";
msg += pass_name + "\n";
spv_position_t null_pos{0, 0, 0};
diff --git a/source/opt/passes.h b/source/opt/passes.h
index d51c306e..21354c77 100644
--- a/source/opt/passes.h
+++ b/source/opt/passes.h
@@ -34,8 +34,10 @@
#include "source/opt/desc_sroa.h"
#include "source/opt/eliminate_dead_constant_pass.h"
#include "source/opt/eliminate_dead_functions_pass.h"
+#include "source/opt/eliminate_dead_input_components_pass.h"
#include "source/opt/eliminate_dead_members_pass.h"
#include "source/opt/empty_pass.h"
+#include "source/opt/fix_func_call_arguments.h"
#include "source/opt/fix_storage_class.h"
#include "source/opt/flatten_decoration_pass.h"
#include "source/opt/fold_spec_constant_op_and_composite_pass.h"
@@ -47,6 +49,7 @@
#include "source/opt/inst_bindless_check_pass.h"
#include "source/opt/inst_buff_addr_check_pass.h"
#include "source/opt/inst_debug_printf_pass.h"
+#include "source/opt/interface_var_sroa.h"
#include "source/opt/interp_fixup_pass.h"
#include "source/opt/licm_pass.h"
#include "source/opt/local_access_chain_convert_pass.h"
@@ -64,6 +67,7 @@
#include "source/opt/reduce_load_size.h"
#include "source/opt/redundancy_elimination.h"
#include "source/opt/relax_float_ops_pass.h"
+#include "source/opt/remove_dontinline_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"
diff --git a/source/opt/private_to_local_pass.cpp b/source/opt/private_to_local_pass.cpp
index 12a226d5..80fb4c53 100644
--- a/source/opt/private_to_local_pass.cpp
+++ b/source/opt/private_to_local_pass.cpp
@@ -135,7 +135,7 @@ bool PrivateToLocalPass::MoveVariable(Instruction* variable,
// Place the variable at the start of the first basic block.
context()->AnalyzeUses(variable);
context()->set_instr_block(variable, &*function->begin());
- function->begin()->begin()->InsertBefore(move(var));
+ function->begin()->begin()->InsertBefore(std::move(var));
// Update uses where the type may have changed.
return UpdateUses(variable);
diff --git a/source/opt/reduce_load_size.cpp b/source/opt/reduce_load_size.cpp
index e9b80874..56491b2f 100644
--- a/source/opt/reduce_load_size.cpp
+++ b/source/opt/reduce_load_size.cpp
@@ -161,8 +161,15 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) {
case analysis::Type::kArray: {
const analysis::Constant* size_const =
const_mgr->FindDeclaredConstant(load_type->AsArray()->LengthId());
- assert(size_const->AsIntConstant());
- total_size = size_const->GetU32();
+
+ if (size_const) {
+ assert(size_const->AsIntConstant());
+ total_size = size_const->GetU32();
+ } else {
+ // The size is spec constant, so it is unknown at this time. Assume
+ // it is very large.
+ total_size = UINT32_MAX;
+ }
} break;
case analysis::Type::kStruct:
total_size = static_cast<uint32_t>(
diff --git a/source/opt/remove_dontinline_pass.cpp b/source/opt/remove_dontinline_pass.cpp
new file mode 100644
index 00000000..4dd1cd4f
--- /dev/null
+++ b/source/opt/remove_dontinline_pass.cpp
@@ -0,0 +1,49 @@
+// Copyright (c) 2022 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/remove_dontinline_pass.h"
+
+namespace spvtools {
+namespace opt {
+
+Pass::Status RemoveDontInline::Process() {
+ bool modified = false;
+ modified = ClearDontInlineFunctionControl();
+ return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
+}
+
+bool RemoveDontInline::ClearDontInlineFunctionControl() {
+ bool modified = false;
+ for (auto& func : *get_module()) {
+ ClearDontInlineFunctionControl(&func);
+ }
+ return modified;
+}
+
+bool RemoveDontInline::ClearDontInlineFunctionControl(Function* function) {
+ constexpr uint32_t kFunctionControlInOperandIdx = 0;
+ Instruction* function_inst = &function->DefInst();
+ uint32_t function_control =
+ function_inst->GetSingleWordInOperand(kFunctionControlInOperandIdx);
+
+ if ((function_control & SpvFunctionControlDontInlineMask) == 0) {
+ return false;
+ }
+ function_control &= ~SpvFunctionControlDontInlineMask;
+ function_inst->SetInOperand(kFunctionControlInOperandIdx, {function_control});
+ return true;
+}
+
+} // namespace opt
+} // namespace spvtools
diff --git a/source/opt/remove_dontinline_pass.h b/source/opt/remove_dontinline_pass.h
new file mode 100644
index 00000000..16243199
--- /dev/null
+++ b/source/opt/remove_dontinline_pass.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2022 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_REMOVE_DONTINLINE_PASS_H_
+#define SOURCE_OPT_REMOVE_DONTINLINE_PASS_H_
+
+#include "source/opt/pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// See optimizer.hpp for documentation.
+class RemoveDontInline : public Pass {
+ public:
+ const char* name() const override { return "remove-dont-inline"; }
+ Status Process() override;
+
+ private:
+ // Clears the DontInline function control from every function in the module.
+ // Returns true of a change was made.
+ bool ClearDontInlineFunctionControl();
+
+ // Clears the DontInline function control from |function|.
+ // Returns true of a change was made.
+ bool ClearDontInlineFunctionControl(Function* function);
+};
+
+} // namespace opt
+} // namespace spvtools
+
+#endif // SOURCE_OPT_REMOVE_DONTINLINE_PASS_H_
diff --git a/source/opt/replace_desc_array_access_using_var_index.cpp b/source/opt/replace_desc_array_access_using_var_index.cpp
index 1082e679..e97593ef 100644
--- a/source/opt/replace_desc_array_access_using_var_index.cpp
+++ b/source/opt/replace_desc_array_access_using_var_index.cpp
@@ -95,7 +95,7 @@ void ReplaceDescArrayAccessUsingVarIndex::ReplaceUsersOfAccessChain(
CollectRecursiveUsersWithConcreteType(access_chain, &final_users);
for (auto* inst : final_users) {
std::deque<Instruction*> insts_to_be_cloned =
- CollectRequiredImageInsts(inst);
+ CollectRequiredImageAndAccessInsts(inst);
ReplaceNonUniformAccessWithSwitchCase(
inst, access_chain, number_of_elements, insts_to_be_cloned);
}
@@ -121,8 +121,8 @@ void ReplaceDescArrayAccessUsingVarIndex::CollectRecursiveUsersWithConcreteType(
}
std::deque<Instruction*>
-ReplaceDescArrayAccessUsingVarIndex::CollectRequiredImageInsts(
- Instruction* user_of_image_insts) const {
+ReplaceDescArrayAccessUsingVarIndex::CollectRequiredImageAndAccessInsts(
+ Instruction* user) const {
std::unordered_set<uint32_t> seen_inst_ids;
std::queue<Instruction*> work_list;
@@ -131,21 +131,23 @@ ReplaceDescArrayAccessUsingVarIndex::CollectRequiredImageInsts(
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)) {
+ (HasImageOrImagePtrType(operand) ||
+ operand->opcode() == SpvOpAccessChain ||
+ operand->opcode() == SpvOpInBoundsAccessChain)) {
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);
+ std::deque<Instruction*> required_insts;
+ required_insts.push_front(user);
+ user->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);
+ required_insts.push_front(inst_from_work_list);
inst_from_work_list->ForEachInId(decision_to_include_operand);
}
- return required_image_insts;
+ return required_insts;
}
bool ReplaceDescArrayAccessUsingVarIndex::HasImageOrImagePtrType(
@@ -253,8 +255,12 @@ 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);
+ // If the instruction does not belong to a block (i.e. in the case of
+ // OpDecorate), no replacement is needed.
+ if (!block) return;
+
+ // Create merge block and add terminator
auto* merge_block = SeparateInstructionsIntoNewBlock(
block, access_chain_final_user->NextNode());
diff --git a/source/opt/replace_desc_array_access_using_var_index.h b/source/opt/replace_desc_array_access_using_var_index.h
index 0c97f7eb..51817c15 100644
--- a/source/opt/replace_desc_array_access_using_var_index.h
+++ b/source/opt/replace_desc_array_access_using_var_index.h
@@ -76,11 +76,12 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass {
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;
+ // Recursively collects the operands of |user| (and operands of the operands)
+ // whose result types are images/samplers (or pointers/arrays/ structs of
+ // them) and access chains instructions and returns them. The returned
+ // collection includes |user|.
+ std::deque<Instruction*> CollectRequiredImageAndAccessInsts(
+ Instruction* user) const;
// Returns whether result type of |inst| is an image/sampler/pointer of image
// or sampler or not.
diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp
index 4d6a7aad..e27c828b 100644
--- a/source/opt/scalar_replacement_pass.cpp
+++ b/source/opt/scalar_replacement_pass.cpp
@@ -24,6 +24,7 @@
#include "source/opt/reflect.h"
#include "source/opt/types.h"
#include "source/util/make_unique.h"
+#include "types.h"
static const uint32_t kDebugValueOperandValueIndex = 5;
static const uint32_t kDebugValueOperandExpressionIndex = 6;
@@ -395,7 +396,7 @@ bool ScalarReplacementPass::CreateReplacementVariables(
if (!components_used || components_used->count(elem)) {
CreateVariable(*id, inst, elem, replacements);
} else {
- replacements->push_back(CreateNullConstant(*id));
+ replacements->push_back(GetUndef(*id));
}
elem++;
});
@@ -406,8 +407,8 @@ bool ScalarReplacementPass::CreateReplacementVariables(
CreateVariable(type->GetSingleWordInOperand(0u), inst, i,
replacements);
} else {
- replacements->push_back(
- CreateNullConstant(type->GetSingleWordInOperand(0u)));
+ uint32_t element_type_id = type->GetSingleWordInOperand(0);
+ replacements->push_back(GetUndef(element_type_id));
}
}
break;
@@ -429,6 +430,10 @@ bool ScalarReplacementPass::CreateReplacementVariables(
replacements->end();
}
+Instruction* ScalarReplacementPass::GetUndef(uint32_t type_id) {
+ return get_def_use_mgr()->GetDef(Type2Undef(type_id));
+}
+
void ScalarReplacementPass::TransferAnnotations(
const Instruction* source, std::vector<Instruction*>* replacements) {
// Only transfer invariant and restrict decorations on the variable. There are
@@ -981,20 +986,6 @@ ScalarReplacementPass::GetUsedComponents(Instruction* inst) {
return result;
}
-Instruction* ScalarReplacementPass::CreateNullConstant(uint32_t type_id) {
- analysis::TypeManager* type_mgr = context()->get_type_mgr();
- analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
-
- const analysis::Type* type = type_mgr->GetType(type_id);
- const analysis::Constant* null_const = const_mgr->GetConstant(type, {});
- Instruction* null_inst =
- const_mgr->GetDefiningInstruction(null_const, type_id);
- if (null_inst != nullptr) {
- context()->UpdateDefUse(null_inst);
- }
- return null_inst;
-}
-
uint64_t ScalarReplacementPass::GetMaxLegalIndex(
const Instruction* var_inst) const {
assert(var_inst->opcode() == SpvOpVariable &&
diff --git a/source/opt/scalar_replacement_pass.h b/source/opt/scalar_replacement_pass.h
index 0928830c..6a66dfb8 100644
--- a/source/opt/scalar_replacement_pass.h
+++ b/source/opt/scalar_replacement_pass.h
@@ -15,6 +15,7 @@
#ifndef SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_
#define SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_
+#include <cassert>
#include <cstdio>
#include <memory>
#include <queue>
@@ -23,23 +24,34 @@
#include <vector>
#include "source/opt/function.h"
-#include "source/opt/pass.h"
+#include "source/opt/mem_pass.h"
#include "source/opt/type_manager.h"
namespace spvtools {
namespace opt {
// Documented in optimizer.hpp
-class ScalarReplacementPass : public Pass {
+class ScalarReplacementPass : public MemPass {
private:
static const uint32_t kDefaultLimit = 100;
public:
ScalarReplacementPass(uint32_t limit = kDefaultLimit)
: max_num_elements_(limit) {
- name_[0] = '\0';
- strcat(name_, "scalar-replacement=");
- sprintf(&name_[strlen(name_)], "%d", max_num_elements_);
+ const auto num_to_write = snprintf(
+ name_, sizeof(name_), "scalar-replacement=%u", max_num_elements_);
+ assert(size_t(num_to_write) < sizeof(name_));
+ (void)num_to_write; // Mark as unused
+
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ // ClusterFuzz/OSS-Fuzz is likely to yield examples with very large arrays.
+ // This can cause timeouts and memouts during fuzzing that
+ // are not classed as bugs. To avoid this noise, we set the
+ // max_num_elements_ to a smaller value for fuzzing.
+ max_num_elements_ =
+ (max_num_elements_ > 0 && max_num_elements_ < 100 ? max_num_elements_
+ : 100);
+#endif
}
const char* name() const override { return name_; }
@@ -234,10 +246,8 @@ class ScalarReplacementPass : public Pass {
std::unique_ptr<std::unordered_set<int64_t>> GetUsedComponents(
Instruction* inst);
- // Returns an instruction defining a null constant with type |type_id|. If
- // one already exists, it is returned. Otherwise a new one is created.
- // Returns |nullptr| if the new constant could not be created.
- Instruction* CreateNullConstant(uint32_t type_id);
+ // Returns an instruction defining an undefined value type |type_id|.
+ Instruction* GetUndef(uint32_t type_id);
// Maps storage type to a pointer type enclosing that type.
std::unordered_map<uint32_t, uint32_t> pointee_to_pointer_;
@@ -255,7 +265,10 @@ class ScalarReplacementPass : public Pass {
// Limit on the number of members in an object that will be replaced.
// 0 means there is no limit.
uint32_t max_num_elements_;
- char name_[55];
+ // This has to be big enough to fit "scalar-replacement=" followed by a
+ // uint32_t number written in decimal (so 10 digits), and then a
+ // terminating nul.
+ char name_[30];
};
} // namespace opt
diff --git a/source/opt/spread_volatile_semantics.cpp b/source/opt/spread_volatile_semantics.cpp
index 17a4c725..b61fd0f4 100644
--- a/source/opt/spread_volatile_semantics.cpp
+++ b/source/opt/spread_volatile_semantics.cpp
@@ -68,34 +68,12 @@ bool HasVolatileDecoration(analysis::DecorationManager* decoration_manager,
return decoration_manager->HasDecoration(var_id, SpvDecorationVolatile);
}
-bool HasOnlyEntryPointsAsFunctions(IRContext* context, Module* module) {
- std::unordered_set<uint32_t> entry_function_ids;
- for (Instruction& entry_point : module->entry_points()) {
- entry_function_ids.insert(
- entry_point.GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint));
- }
- for (auto& function : *module) {
- if (entry_function_ids.find(function.result_id()) ==
- entry_function_ids.end()) {
- std::string message(
- "Functions of SPIR-V for spread-volatile-semantics pass input must "
- "be inlined except entry points");
- message += "\n " + function.DefInst().PrettyPrint(
- SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
- context->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str());
- return false;
- }
- }
- return true;
-}
-
} // namespace
Pass::Status SpreadVolatileSemantics::Process() {
- if (!HasOnlyEntryPointsAsFunctions(context(), get_module())) {
- return Status::Failure;
+ if (HasNoExecutionModel()) {
+ return Status::SuccessWithoutChange;
}
-
const bool is_vk_memory_model_enabled =
context()->get_feature_mgr()->HasCapability(
SpvCapabilityVulkanMemoryModel);
@@ -138,6 +116,8 @@ bool SpreadVolatileSemantics::IsTargetUsedByNonVolatileLoadInEntryPoint(
uint32_t var_id, Instruction* entry_point) {
uint32_t entry_function_id =
entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint);
+ std::unordered_set<uint32_t> funcs;
+ context()->CollectCallTreeFromRoots(entry_function_id, &funcs);
return !VisitLoadsOfPointersToVariableInEntries(
var_id,
[](Instruction* load) {
@@ -150,7 +130,7 @@ bool SpreadVolatileSemantics::IsTargetUsedByNonVolatileLoadInEntryPoint(
load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands);
return (memory_operands & SpvMemoryAccessVolatileMask) != 0;
},
- {entry_function_id});
+ funcs);
}
bool SpreadVolatileSemantics::HasInterfaceInConflictOfVolatileSemantics() {
@@ -221,7 +201,7 @@ void SpreadVolatileSemantics::DecorateVarWithVolatile(Instruction* var) {
bool SpreadVolatileSemantics::VisitLoadsOfPointersToVariableInEntries(
uint32_t var_id, const std::function<bool(Instruction*)>& handle_load,
- const std::unordered_set<uint32_t>& entry_function_ids) {
+ const std::unordered_set<uint32_t>& function_ids) {
std::vector<uint32_t> worklist({var_id});
auto* def_use_mgr = context()->get_def_use_mgr();
while (!worklist.empty()) {
@@ -229,11 +209,11 @@ bool SpreadVolatileSemantics::VisitLoadsOfPointersToVariableInEntries(
worklist.pop_back();
bool finish_traversal = !def_use_mgr->WhileEachUser(
ptr_id, [this, &worklist, &ptr_id, handle_load,
- &entry_function_ids](Instruction* user) {
+ &function_ids](Instruction* user) {
BasicBlock* block = context()->get_instr_block(user);
if (block == nullptr ||
- entry_function_ids.find(block->GetParent()->result_id()) ==
- entry_function_ids.end()) {
+ function_ids.find(block->GetParent()->result_id()) ==
+ function_ids.end()) {
return true;
}
@@ -262,21 +242,25 @@ void SpreadVolatileSemantics::SetVolatileForLoadsInEntries(
Instruction* var, const std::unordered_set<uint32_t>& entry_function_ids) {
// Set Volatile memory operand for all load instructions if they do not have
// it.
- VisitLoadsOfPointersToVariableInEntries(
- var->result_id(),
- [](Instruction* load) {
- if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) {
- load->AddOperand(
- {SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessVolatileMask}});
+ for (auto entry_id : entry_function_ids) {
+ std::unordered_set<uint32_t> funcs;
+ context()->CollectCallTreeFromRoots(entry_id, &funcs);
+ VisitLoadsOfPointersToVariableInEntries(
+ var->result_id(),
+ [](Instruction* load) {
+ if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) {
+ load->AddOperand({SPV_OPERAND_TYPE_MEMORY_ACCESS,
+ {SpvMemoryAccessVolatileMask}});
+ return true;
+ }
+ uint32_t memory_operands =
+ load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands);
+ memory_operands |= SpvMemoryAccessVolatileMask;
+ load->SetInOperand(kOpLoadInOperandMemoryOperands, {memory_operands});
return true;
- }
- uint32_t memory_operands =
- load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands);
- memory_operands |= SpvMemoryAccessVolatileMask;
- load->SetInOperand(kOpLoadInOperandMemoryOperands, {memory_operands});
- return true;
- },
- entry_function_ids);
+ },
+ funcs);
+ }
}
bool SpreadVolatileSemantics::IsTargetForVolatileSemantics(
diff --git a/source/opt/spread_volatile_semantics.h b/source/opt/spread_volatile_semantics.h
index 3d0a1839..014858d3 100644
--- a/source/opt/spread_volatile_semantics.h
+++ b/source/opt/spread_volatile_semantics.h
@@ -35,6 +35,13 @@ class SpreadVolatileSemantics : public Pass {
}
private:
+ // Returns true if it does not have an execution model. Linkage shaders do not
+ // have an execution model.
+ bool HasNoExecutionModel() {
+ return get_module()->entry_points().empty() &&
+ context()->get_feature_mgr()->HasCapability(SpvCapabilityLinkage);
+ }
+
// Iterates interface variables and spreads the Volatile semantics if it has
// load instructions for the Volatile semantics.
Pass::Status SpreadVolatileSemanticsToVariables(
@@ -65,15 +72,14 @@ class SpreadVolatileSemantics : public Pass {
Instruction* entry_point);
// Visits load instructions of pointers to variable whose result id is
- // |var_id| if the load instructions are in entry points whose
- // function id is one of |entry_function_ids|. |handle_load| is a function to
- // do some actions for the load instructions. Finishes the traversal and
- // returns false if |handle_load| returns false for a load instruction.
- // Otherwise, returns true after running |handle_load| for all the load
- // instructions.
+ // |var_id| if the load instructions are in reachable functions from entry
+ // points. |handle_load| is a function to do some actions for the load
+ // instructions. Finishes the traversal and returns false if |handle_load|
+ // returns false for a load instruction. Otherwise, returns true after running
+ // |handle_load| for all the load instructions.
bool VisitLoadsOfPointersToVariableInEntries(
uint32_t var_id, const std::function<bool(Instruction*)>& handle_load,
- const std::unordered_set<uint32_t>& entry_function_ids);
+ const std::unordered_set<uint32_t>& function_ids);
// Sets Memory Operands of OpLoad instructions that load |var| or pointers
// of |var| as Volatile if the function id of the OpLoad instruction is
diff --git a/source/opt/ssa_rewrite_pass.cpp b/source/opt/ssa_rewrite_pass.cpp
index 29ab6123..22d81104 100644
--- a/source/opt/ssa_rewrite_pass.cpp
+++ b/source/opt/ssa_rewrite_pass.cpp
@@ -67,7 +67,6 @@ namespace opt {
namespace {
const uint32_t kStoreValIdInIdx = 1;
const uint32_t kVariableInitIdInIdx = 1;
-const uint32_t kDebugDeclareOperandVariableIdx = 5;
} // namespace
std::string SSARewriter::PhiCandidate::PrettyPrint(const CFG* cfg) const {
@@ -315,8 +314,8 @@ void SSARewriter::ProcessStore(Instruction* inst, BasicBlock* bb) {
}
if (pass_->IsTargetVar(var_id)) {
WriteVariable(var_id, bb, val_id);
- pass_->context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(
- inst, var_id, val_id, inst, &decls_invisible_to_value_assignment_);
+ pass_->context()->get_debug_info_mgr()->AddDebugValueForVariable(
+ inst, var_id, val_id, inst);
#if SSA_REWRITE_DEBUGGING_LEVEL > 1
std::cerr << "\tFound store '%" << var_id << " = %" << val_id << "': "
@@ -559,9 +558,9 @@ bool SSARewriter::ApplyReplacements() {
// Add DebugValue for the new OpPhi instruction.
insert_it->SetDebugScope(local_var->GetDebugScope());
- pass_->context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(
+ pass_->context()->get_debug_info_mgr()->AddDebugValueForVariable(
&*insert_it, phi_candidate->var_id(), phi_candidate->result_id(),
- &*insert_it, &decls_invisible_to_value_assignment_);
+ &*insert_it);
modified = true;
}
@@ -650,62 +649,6 @@ void SSARewriter::FinalizePhiCandidates() {
}
}
-Pass::Status SSARewriter::AddDebugValuesForInvisibleDebugDecls(Function* fp) {
- // For the cases the value assignment is invisible to DebugDeclare e.g.,
- // the argument passing for an inlined function.
- //
- // Before inlining foo(int x):
- // a = 3;
- // foo(3);
- // After inlining:
- // a = 3;
- // foo and x disappeared but we want to specify "DebugValue: %x = %int_3".
- //
- // We want to specify the value for the variable using |defs_at_block_[bb]|,
- // where |bb| is the basic block contains the decl.
- DominatorAnalysis* dom_tree = pass_->context()->GetDominatorAnalysis(fp);
- Pass::Status status = Pass::Status::SuccessWithoutChange;
- for (auto* decl : decls_invisible_to_value_assignment_) {
- uint32_t var_id =
- decl->GetSingleWordOperand(kDebugDeclareOperandVariableIdx);
- auto* var = pass_->get_def_use_mgr()->GetDef(var_id);
- if (var->opcode() == SpvOpFunctionParameter) continue;
-
- auto* bb = pass_->context()->get_instr_block(decl);
- uint32_t value_id = GetValueAtBlock(var_id, bb);
- Instruction* value = nullptr;
- if (value_id) value = pass_->get_def_use_mgr()->GetDef(value_id);
-
- // If |value| is defined before the function body, it dominates |decl|.
- // If |value| dominates |decl|, we can set it as DebugValue.
- if (value && (pass_->context()->get_instr_block(value) == nullptr ||
- dom_tree->Dominates(value, decl))) {
- if (pass_->context()->get_debug_info_mgr()->AddDebugValueForDecl(
- decl, value->result_id(), decl, value) == nullptr) {
- return Pass::Status::Failure;
- }
- } else {
- // If |value| in the same basic block does not dominate |decl|, we can
- // assign the value in the immediate dominator.
- value_id = GetValueAtBlock(var_id, dom_tree->ImmediateDominator(bb));
- if (value_id) value = pass_->get_def_use_mgr()->GetDef(value_id);
- if (value_id &&
- pass_->context()->get_debug_info_mgr()->AddDebugValueForDecl(
- decl, value_id, decl, value) == nullptr) {
- return Pass::Status::Failure;
- }
- }
-
- // DebugDeclares of target variables will be removed by
- // SSARewritePass::Process().
- if (!pass_->IsTargetVar(var_id)) {
- pass_->context()->get_debug_info_mgr()->KillDebugDeclares(var_id);
- }
- status = Pass::Status::SuccessWithChange;
- }
- return status;
-}
-
Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) {
#if SSA_REWRITE_DEBUGGING_LEVEL > 0
std::cerr << "Function before SSA rewrite:\n"
@@ -735,12 +678,6 @@ Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) {
// Finally, apply all the replacements in the IR.
bool modified = ApplyReplacements();
- auto status = AddDebugValuesForInvisibleDebugDecls(fp);
- if (status == Pass::Status::SuccessWithChange ||
- status == Pass::Status::Failure) {
- return status;
- }
-
#if SSA_REWRITE_DEBUGGING_LEVEL > 0
std::cerr << "\n\n\nFunction after SSA rewrite:\n"
<< fp->PrettyPrint(0) << "\n";
diff --git a/source/opt/ssa_rewrite_pass.h b/source/opt/ssa_rewrite_pass.h
index 1f4cd24c..2470f85f 100644
--- a/source/opt/ssa_rewrite_pass.h
+++ b/source/opt/ssa_rewrite_pass.h
@@ -253,11 +253,6 @@ class SSARewriter {
// candidates.
void FinalizePhiCandidates();
- // Adds DebugValues for DebugDeclares in
- // |decls_invisible_to_value_assignment_|. Returns whether the function was
- // modified or not, and whether or not the conversion was successful.
- Pass::Status AddDebugValuesForInvisibleDebugDecls(Function* fp);
-
// Prints the table of Phi candidates to std::cerr.
void PrintPhiCandidates() const;
@@ -295,10 +290,6 @@ class SSARewriter {
// Memory pass requesting the SSA rewriter.
MemPass* pass_;
-
- // Set of DebugDeclare instructions that are not added as DebugValue because
- // they are invisible to the store or phi instructions.
- std::unordered_set<Instruction*> decls_invisible_to_value_assignment_;
};
class SSARewritePass : public MemPass {
diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp
index 6da4b57b..a0006f55 100644
--- a/source/opt/type_manager.cpp
+++ b/source/opt/type_manager.cpp
@@ -235,6 +235,7 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
DefineParameterlessCase(PipeStorage);
DefineParameterlessCase(NamedBarrier);
DefineParameterlessCase(AccelerationStructureNV);
+ DefineParameterlessCase(RayQueryKHR);
#undef DefineParameterlessCase
case Type::kInteger:
typeInst = MakeUnique<Instruction>(
@@ -527,6 +528,7 @@ Type* TypeManager::RebuildType(const Type& type) {
DefineNoSubtypeCase(PipeStorage);
DefineNoSubtypeCase(NamedBarrier);
DefineNoSubtypeCase(AccelerationStructureNV);
+ DefineNoSubtypeCase(RayQueryKHR);
#undef DefineNoSubtypeCase
case Type::kVector: {
const Vector* vec_ty = type.AsVector();
diff --git a/source/opt/types.cpp b/source/opt/types.cpp
index ea4baadb..056acebb 100644
--- a/source/opt/types.cpp
+++ b/source/opt/types.cpp
@@ -16,11 +16,13 @@
#include <algorithm>
#include <cassert>
+#include <climits>
#include <cstdint>
#include <sstream>
#include <string>
#include <unordered_set>
+#include "source/util/hash_combine.h"
#include "source/util/make_unique.h"
#include "spirv/unified1/spirv.h"
@@ -28,6 +30,7 @@ namespace spvtools {
namespace opt {
namespace analysis {
+using spvtools::utils::hash_combine;
using U32VecVec = std::vector<std::vector<uint32_t>>;
namespace {
@@ -182,23 +185,26 @@ bool Type::operator==(const Type& other) const {
}
}
-void Type::GetHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* seen) const {
- if (!seen->insert(this).second) {
- return;
+size_t Type::ComputeHashValue(size_t hash, SeenTypes* seen) const {
+ // Linear search through a dense, cache coherent vector is faster than O(log
+ // n) search in a complex data structure (eg std::set) for the generally small
+ // number of nodes. It also skips the overhead of an new/delete per Type
+ // (when inserting/removing from a set).
+ if (std::find(seen->begin(), seen->end(), this) != seen->end()) {
+ return hash;
}
- words->push_back(kind_);
+ seen->push_back(this);
+
+ hash = hash_combine(hash, uint32_t(kind_));
for (const auto& d : decorations_) {
- for (auto w : d) {
- words->push_back(w);
- }
+ hash = hash_combine(hash, d);
}
switch (kind_) {
-#define DeclareKindCase(type) \
- case k##type: \
- As##type()->GetExtraHashWords(words, seen); \
+#define DeclareKindCase(type) \
+ case k##type: \
+ hash = As##type()->ComputeExtraStateHash(hash, seen); \
break
DeclareKindCase(Void);
DeclareKindCase(Bool);
@@ -232,18 +238,42 @@ void Type::GetHashWords(std::vector<uint32_t>* words,
break;
}
- seen->erase(this);
+ seen->pop_back();
+ return hash;
}
size_t Type::HashValue() const {
- std::u32string h;
- std::vector<uint32_t> words;
- GetHashWords(&words);
- for (auto w : words) {
- h.push_back(w);
+ SeenTypes seen;
+ return ComputeHashValue(0, &seen);
+}
+
+uint64_t Type::NumberOfComponents() const {
+ switch (kind()) {
+ case kVector:
+ return AsVector()->element_count();
+ case kMatrix:
+ return AsMatrix()->element_count();
+ case kArray: {
+ Array::LengthInfo length_info = AsArray()->length_info();
+ if (length_info.words[0] != Array::LengthInfo::kConstant) {
+ return UINT64_MAX;
+ }
+ assert(length_info.words.size() <= 3 &&
+ "The size of the array could not fit size_t.");
+ uint64_t length = 0;
+ length |= length_info.words[1];
+ if (length_info.words.size() > 2) {
+ length |= static_cast<uint64_t>(length_info.words[2]) << 32;
+ }
+ return length;
+ }
+ case kRuntimeArray:
+ return UINT64_MAX;
+ case kStruct:
+ return AsStruct()->element_types().size();
+ default:
+ return 0;
}
-
- return std::hash<std::u32string>()(h);
}
bool Integer::IsSameImpl(const Type* that, IsSameCache*) const {
@@ -258,10 +288,8 @@ std::string Integer::str() const {
return oss.str();
}
-void Integer::GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>*) const {
- words->push_back(width_);
- words->push_back(signed_);
+size_t Integer::ComputeExtraStateHash(size_t hash, SeenTypes*) const {
+ return hash_combine(hash, width_, signed_);
}
bool Float::IsSameImpl(const Type* that, IsSameCache*) const {
@@ -275,9 +303,8 @@ std::string Float::str() const {
return oss.str();
}
-void Float::GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>*) const {
- words->push_back(width_);
+size_t Float::ComputeExtraStateHash(size_t hash, SeenTypes*) const {
+ return hash_combine(hash, width_);
}
Vector::Vector(const Type* type, uint32_t count)
@@ -299,10 +326,11 @@ std::string Vector::str() const {
return oss.str();
}
-void Vector::GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* seen) const {
- element_type_->GetHashWords(words, seen);
- words->push_back(count_);
+size_t Vector::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
+ // prefer form that doesn't require push/pop from stack: add state and
+ // make tail call.
+ hash = hash_combine(hash, count_);
+ return element_type_->ComputeHashValue(hash, seen);
}
Matrix::Matrix(const Type* type, uint32_t count)
@@ -324,10 +352,9 @@ std::string Matrix::str() const {
return oss.str();
}
-void Matrix::GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* seen) const {
- element_type_->GetHashWords(words, seen);
- words->push_back(count_);
+size_t Matrix::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
+ hash = hash_combine(hash, count_);
+ return element_type_->ComputeHashValue(hash, seen);
}
Image::Image(Type* type, SpvDim dimen, uint32_t d, bool array, bool multisample,
@@ -362,16 +389,10 @@ std::string Image::str() const {
return oss.str();
}
-void Image::GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* seen) const {
- sampled_type_->GetHashWords(words, seen);
- words->push_back(dim_);
- words->push_back(depth_);
- words->push_back(arrayed_);
- words->push_back(ms_);
- words->push_back(sampled_);
- words->push_back(format_);
- words->push_back(access_qualifier_);
+size_t Image::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
+ hash = hash_combine(hash, uint32_t(dim_), depth_, arrayed_, ms_, sampled_,
+ uint32_t(format_), uint32_t(access_qualifier_));
+ return sampled_type_->ComputeHashValue(hash, seen);
}
bool SampledImage::IsSameImpl(const Type* that, IsSameCache* seen) const {
@@ -387,9 +408,8 @@ std::string SampledImage::str() const {
return oss.str();
}
-void SampledImage::GetExtraHashWords(
- std::vector<uint32_t>* words, std::unordered_set<const Type*>* seen) const {
- image_type_->GetHashWords(words, seen);
+size_t SampledImage::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
+ return image_type_->ComputeHashValue(hash, seen);
}
Array::Array(const Type* type, const Array::LengthInfo& length_info_arg)
@@ -422,15 +442,19 @@ std::string Array::str() const {
return oss.str();
}
-void Array::GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* seen) const {
- element_type_->GetHashWords(words, seen);
- words->insert(words->end(), length_info_.words.begin(),
- length_info_.words.end());
+size_t Array::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
+ hash = hash_combine(hash, length_info_.words);
+ return element_type_->ComputeHashValue(hash, seen);
}
void Array::ReplaceElementType(const Type* type) { element_type_ = type; }
+Array::LengthInfo Array::GetConstantLengthInfo(uint32_t const_id,
+ uint32_t length) const {
+ std::vector<uint32_t> extra_words{LengthInfo::Case::kConstant, length};
+ return {const_id, extra_words};
+}
+
RuntimeArray::RuntimeArray(const Type* type)
: Type(kRuntimeArray), element_type_(type) {
assert(!type->AsVoid());
@@ -449,9 +473,8 @@ std::string RuntimeArray::str() const {
return oss.str();
}
-void RuntimeArray::GetExtraHashWords(
- std::vector<uint32_t>* words, std::unordered_set<const Type*>* seen) const {
- element_type_->GetHashWords(words, seen);
+size_t RuntimeArray::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
+ return element_type_->ComputeHashValue(hash, seen);
}
void RuntimeArray::ReplaceElementType(const Type* type) {
@@ -508,19 +531,14 @@ std::string Struct::str() const {
return oss.str();
}
-void Struct::GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* seen) const {
+size_t Struct::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
for (auto* t : element_types_) {
- t->GetHashWords(words, seen);
+ hash = t->ComputeHashValue(hash, seen);
}
for (const auto& pair : element_decorations_) {
- words->push_back(pair.first);
- for (const auto& d : pair.second) {
- for (auto w : d) {
- words->push_back(w);
- }
- }
+ hash = hash_combine(hash, pair.first, pair.second);
}
+ return hash;
}
bool Opaque::IsSameImpl(const Type* that, IsSameCache*) const {
@@ -535,11 +553,8 @@ std::string Opaque::str() const {
return oss.str();
}
-void Opaque::GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>*) const {
- for (auto c : name_) {
- words->push_back(static_cast<char32_t>(c));
- }
+size_t Opaque::ComputeExtraStateHash(size_t hash, SeenTypes*) const {
+ return hash_combine(hash, name_);
}
Pointer::Pointer(const Type* type, SpvStorageClass sc)
@@ -568,10 +583,9 @@ std::string Pointer::str() const {
return os.str();
}
-void Pointer::GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* seen) const {
- pointee_type_->GetHashWords(words, seen);
- words->push_back(storage_class_);
+size_t Pointer::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
+ hash = hash_combine(hash, uint32_t(storage_class_));
+ return pointee_type_->ComputeHashValue(hash, seen);
}
void Pointer::SetPointeeType(const Type* type) { pointee_type_ = type; }
@@ -605,12 +619,11 @@ std::string Function::str() const {
return oss.str();
}
-void Function::GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* seen) const {
- return_type_->GetHashWords(words, seen);
+size_t Function::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
for (const auto* t : param_types_) {
- t->GetHashWords(words, seen);
+ hash = t->ComputeHashValue(hash, seen);
}
+ return return_type_->ComputeHashValue(hash, seen);
}
void Function::SetReturnType(const Type* type) { return_type_ = type; }
@@ -627,9 +640,8 @@ std::string Pipe::str() const {
return oss.str();
}
-void Pipe::GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>*) const {
- words->push_back(access_qualifier_);
+size_t Pipe::ComputeExtraStateHash(size_t hash, SeenTypes*) const {
+ return hash_combine(hash, uint32_t(access_qualifier_));
}
bool ForwardPointer::IsSameImpl(const Type* that, IsSameCache*) const {
@@ -652,11 +664,11 @@ std::string ForwardPointer::str() const {
return oss.str();
}
-void ForwardPointer::GetExtraHashWords(
- std::vector<uint32_t>* words, std::unordered_set<const Type*>* seen) const {
- words->push_back(target_id_);
- words->push_back(storage_class_);
- if (pointer_) pointer_->GetHashWords(words, seen);
+size_t ForwardPointer::ComputeExtraStateHash(size_t hash,
+ SeenTypes* seen) const {
+ hash = hash_combine(hash, target_id_, uint32_t(storage_class_));
+ if (pointer_) hash = pointer_->ComputeHashValue(hash, seen);
+ return hash;
}
CooperativeMatrixNV::CooperativeMatrixNV(const Type* type, const uint32_t scope,
@@ -680,12 +692,10 @@ std::string CooperativeMatrixNV::str() const {
return oss.str();
}
-void CooperativeMatrixNV::GetExtraHashWords(
- std::vector<uint32_t>* words, std::unordered_set<const Type*>* pSet) const {
- component_type_->GetHashWords(words, pSet);
- words->push_back(scope_id_);
- words->push_back(rows_id_);
- words->push_back(columns_id_);
+size_t CooperativeMatrixNV::ComputeExtraStateHash(size_t hash,
+ SeenTypes* seen) const {
+ hash = hash_combine(hash, scope_id_, rows_id_, columns_id_);
+ return component_type_->ComputeHashValue(hash, seen);
}
bool CooperativeMatrixNV::IsSameImpl(const Type* that,
diff --git a/source/opt/types.h b/source/opt/types.h
index 9ecd41a6..a92669e9 100644
--- a/source/opt/types.h
+++ b/source/opt/types.h
@@ -28,6 +28,7 @@
#include "source/latest_version_spirv_header.h"
#include "source/opt/instruction.h"
+#include "source/util/small_vector.h"
#include "spirv-tools/libspirv.h"
namespace spvtools {
@@ -67,6 +68,8 @@ class Type {
public:
typedef std::set<std::pair<const Pointer*, const Pointer*>> IsSameCache;
+ using SeenTypes = spvtools::utils::SmallVector<const Type*, 8>;
+
// Available subtypes.
//
// When adding a new derived class of Type, please add an entry to the enum.
@@ -96,7 +99,8 @@ class Type {
kNamedBarrier,
kAccelerationStructureNV,
kCooperativeMatrixNV,
- kRayQueryKHR
+ kRayQueryKHR,
+ kLast
};
Type(Kind k) : kind_(k) {}
@@ -154,21 +158,11 @@ class Type {
// Returns the hash value of this type.
size_t HashValue() const;
- // Adds the necessary words to compute a hash value of this type to |words|.
- void GetHashWords(std::vector<uint32_t>* words) const {
- std::unordered_set<const Type*> seen;
- GetHashWords(words, &seen);
- }
-
- // Adds the necessary words to compute a hash value of this type to |words|.
- void GetHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* seen) const;
+ size_t ComputeHashValue(size_t hash, SeenTypes* seen) const;
- // Adds necessary extra words for a subtype to calculate a hash value into
- // |words|.
- virtual void GetExtraHashWords(
- std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* pSet) const = 0;
+ // Returns the number of components in a composite type. Returns 0 for a
+ // non-composite type.
+ uint64_t NumberOfComponents() const;
// A bunch of methods for casting this type to a given type. Returns this if the
// cast can be done, nullptr otherwise.
@@ -204,6 +198,10 @@ class Type {
DeclareCastMethod(RayQueryKHR)
#undef DeclareCastMethod
+protected:
+ // Add any type-specific state to |hash| and returns new hash.
+ virtual size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const = 0;
+
protected:
// Decorations attached to this type. Each decoration is encoded as a vector
// of uint32_t numbers. The first uint32_t number is the decoration value,
@@ -232,8 +230,7 @@ class Integer : public Type {
uint32_t width() const { return width_; }
bool IsSigned() const { return signed_; }
- void GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* pSet) const override;
+ size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
private:
bool IsSameImpl(const Type* that, IsSameCache*) const override;
@@ -253,8 +250,7 @@ class Float : public Type {
const Float* AsFloat() const override { return this; }
uint32_t width() const { return width_; }
- void GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* pSet) const override;
+ size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
private:
bool IsSameImpl(const Type* that, IsSameCache*) const override;
@@ -274,8 +270,7 @@ class Vector : public Type {
Vector* AsVector() override { return this; }
const Vector* AsVector() const override { return this; }
- void GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* pSet) const override;
+ size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
private:
bool IsSameImpl(const Type* that, IsSameCache*) const override;
@@ -296,8 +291,7 @@ class Matrix : public Type {
Matrix* AsMatrix() override { return this; }
const Matrix* AsMatrix() const override { return this; }
- void GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* pSet) const override;
+ size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
private:
bool IsSameImpl(const Type* that, IsSameCache*) const override;
@@ -327,8 +321,7 @@ class Image : public Type {
SpvImageFormat format() const { return format_; }
SpvAccessQualifier access_qualifier() const { return access_qualifier_; }
- void GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* pSet) const override;
+ size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
private:
bool IsSameImpl(const Type* that, IsSameCache*) const override;
@@ -355,8 +348,7 @@ class SampledImage : public Type {
const Type* image_type() const { return image_type_; }
- void GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* pSet) const override;
+ size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
private:
bool IsSameImpl(const Type* that, IsSameCache*) const override;
@@ -399,10 +391,10 @@ class Array : public Type {
Array* AsArray() override { return this; }
const Array* AsArray() const override { return this; }
- void GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* pSet) const override;
+ size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
void ReplaceElementType(const Type* element_type);
+ LengthInfo GetConstantLengthInfo(uint32_t const_id, uint32_t length) const;
private:
bool IsSameImpl(const Type* that, IsSameCache*) const override;
@@ -422,8 +414,7 @@ class RuntimeArray : public Type {
RuntimeArray* AsRuntimeArray() override { return this; }
const RuntimeArray* AsRuntimeArray() const override { return this; }
- void GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* pSet) const override;
+ size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
void ReplaceElementType(const Type* element_type);
@@ -459,8 +450,7 @@ class Struct : public Type {
Struct* AsStruct() override { return this; }
const Struct* AsStruct() const override { return this; }
- void GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* pSet) const override;
+ size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
private:
bool IsSameImpl(const Type* that, IsSameCache*) const override;
@@ -491,8 +481,7 @@ class Opaque : public Type {
const std::string& name() const { return name_; }
- void GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* pSet) const override;
+ size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
private:
bool IsSameImpl(const Type* that, IsSameCache*) const override;
@@ -512,8 +501,7 @@ class Pointer : public Type {
Pointer* AsPointer() override { return this; }
const Pointer* AsPointer() const override { return this; }
- void GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* pSet) const override;
+ size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
void SetPointeeType(const Type* type);
@@ -539,8 +527,7 @@ class Function : public Type {
const std::vector<const Type*>& param_types() const { return param_types_; }
std::vector<const Type*>& param_types() { return param_types_; }
- void GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>*) const override;
+ size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
void SetReturnType(const Type* type);
@@ -564,8 +551,7 @@ class Pipe : public Type {
SpvAccessQualifier access_qualifier() const { return access_qualifier_; }
- void GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* pSet) const override;
+ size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
private:
bool IsSameImpl(const Type* that, IsSameCache*) const override;
@@ -592,8 +578,7 @@ class ForwardPointer : public Type {
ForwardPointer* AsForwardPointer() override { return this; }
const ForwardPointer* AsForwardPointer() const override { return this; }
- void GetExtraHashWords(std::vector<uint32_t>* words,
- std::unordered_set<const Type*>* pSet) const override;
+ size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
private:
bool IsSameImpl(const Type* that, IsSameCache*) const override;
@@ -616,8 +601,7 @@ class CooperativeMatrixNV : public Type {
return this;
}
- void GetExtraHashWords(std::vector<uint32_t>*,
- std::unordered_set<const Type*>*) const override;
+ size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
const Type* component_type() const { return component_type_; }
uint32_t scope_id() const { return scope_id_; }
@@ -633,24 +617,25 @@ class CooperativeMatrixNV : public Type {
const uint32_t columns_id_;
};
-#define DefineParameterlessType(type, name) \
- class type : public Type { \
- public: \
- type() : Type(k##type) {} \
- type(const type&) = default; \
- \
- std::string str() const override { return #name; } \
- \
- type* As##type() override { return this; } \
- const type* As##type() const override { return this; } \
- \
- void GetExtraHashWords(std::vector<uint32_t>*, \
- std::unordered_set<const Type*>*) const override {} \
- \
- private: \
- bool IsSameImpl(const Type* that, IsSameCache*) const override { \
- return that->As##type() && HasSameDecorations(that); \
- } \
+#define DefineParameterlessType(type, name) \
+ class type : public Type { \
+ public: \
+ type() : Type(k##type) {} \
+ type(const type&) = default; \
+ \
+ std::string str() const override { return #name; } \
+ \
+ type* As##type() override { return this; } \
+ const type* As##type() const override { return this; } \
+ \
+ size_t ComputeExtraStateHash(size_t hash, SeenTypes*) const override { \
+ return hash; \
+ } \
+ \
+ private: \
+ bool IsSameImpl(const Type* that, IsSameCache*) const override { \
+ return that->As##type() && HasSameDecorations(that); \
+ } \
}
DefineParameterlessType(Void, void);
DefineParameterlessType(Bool, bool);
diff --git a/source/parsed_operand.cpp b/source/parsed_operand.cpp
index 7ad369cd..5f8e94db 100644
--- a/source/parsed_operand.cpp
+++ b/source/parsed_operand.cpp
@@ -24,7 +24,9 @@ namespace spvtools {
void EmitNumericLiteral(std::ostream* out, const spv_parsed_instruction_t& inst,
const spv_parsed_operand_t& operand) {
if (operand.type != SPV_OPERAND_TYPE_LITERAL_INTEGER &&
- operand.type != SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER)
+ operand.type != SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER &&
+ operand.type != SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER &&
+ operand.type != SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER)
return;
if (operand.num_words < 1) return;
// TODO(dneto): Support more than 64-bits at a time.
diff --git a/source/print.cpp b/source/print.cpp
index 2418c5bc..6c94e2b7 100644
--- a/source/print.cpp
+++ b/source/print.cpp
@@ -16,7 +16,8 @@
#if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \
defined(SPIRV_IOS) || defined(SPIRV_TVOS) || defined(SPIRV_FREEBSD) || \
- defined(SPIRV_EMSCRIPTEN) || defined(SPIRV_FUCHSIA)
+ defined(SPIRV_OPENBSD) || defined(SPIRV_EMSCRIPTEN) || \
+ defined(SPIRV_FUCHSIA) || defined(SPIRV_GNU)
namespace spvtools {
clr::reset::operator const char*() { return "\x1b[0m"; }
diff --git a/source/text.cpp b/source/text.cpp
index 415c059d..90f69c52 100644
--- a/source/text.cpp
+++ b/source/text.cpp
@@ -403,9 +403,10 @@ spv_result_t spvTextEncodeOperand(const spvtools::AssemblyGrammar& grammar,
case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: {
uint32_t value;
- if (grammar.parseMaskOperand(type, textValue, &value)) {
- return context->diagnostic() << "Invalid " << spvOperandTypeStr(type)
- << " operand '" << textValue << "'.";
+ if (auto error = grammar.parseMaskOperand(type, textValue, &value)) {
+ return context->diagnostic(error)
+ << "Invalid " << spvOperandTypeStr(type) << " operand '"
+ << textValue << "'.";
}
if (auto error = context->binaryEncodeU32(value, pInst)) return error;
// Prepare to parse the operands for this logical operand.
@@ -622,7 +623,8 @@ spv_result_t spvTextEncodeOpcode(const spvtools::AssemblyGrammar& grammar,
break;
} else {
return context->diagnostic()
- << "Expected operand, found end of stream.";
+ << "Expected operand for " << opcodeName
+ << " instruction, but found the end of the stream.";
}
}
assert(error == SPV_SUCCESS && "Somebody added another way to fail");
@@ -632,7 +634,8 @@ spv_result_t spvTextEncodeOpcode(const spvtools::AssemblyGrammar& grammar,
break;
} else {
return context->diagnostic()
- << "Expected operand, found next instruction instead.";
+ << "Expected operand for " << opcodeName
+ << " instruction, but found the next instruction instead.";
}
}
@@ -666,7 +669,7 @@ spv_result_t spvTextEncodeOpcode(const spvtools::AssemblyGrammar& grammar,
if (pInst->words.size() > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) {
return context->diagnostic()
- << "Instruction too long: " << pInst->words.size()
+ << opcodeName << " Instruction too long: " << pInst->words.size()
<< " words, but the limit is "
<< SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX;
}
@@ -769,8 +772,8 @@ spv_result_t spvTextToBinaryInternal(const spvtools::AssemblyGrammar& grammar,
instructions.push_back({});
spv_instruction_t& inst = instructions.back();
- if (spvTextEncodeOpcode(grammar, &context, &inst)) {
- return SPV_ERROR_INVALID_TEXT;
+ if (auto error = spvTextEncodeOpcode(grammar, &context, &inst)) {
+ return error;
}
if (context.advance()) break;
diff --git a/source/text_handler.cpp b/source/text_handler.cpp
index fe12a26e..15c1741f 100644
--- a/source/text_handler.cpp
+++ b/source/text_handler.cpp
@@ -62,28 +62,29 @@ spv_result_t advanceLine(spv_text text, spv_position position) {
// parameters, its the users responsibility to ensure these are non null.
spv_result_t advance(spv_text text, spv_position position) {
// NOTE: Consume white space, otherwise don't advance.
- if (position->index >= text->length) return SPV_END_OF_STREAM;
- switch (text->str[position->index]) {
- case '\0':
- return SPV_END_OF_STREAM;
- case ';':
- if (spv_result_t error = advanceLine(text, position)) return error;
- return advance(text, position);
- case ' ':
- case '\t':
- case '\r':
- position->column++;
- position->index++;
- return advance(text, position);
- case '\n':
- position->column = 0;
- position->line++;
- position->index++;
- return advance(text, position);
- default:
- break;
+ while (true) {
+ if (position->index >= text->length) return SPV_END_OF_STREAM;
+ switch (text->str[position->index]) {
+ case '\0':
+ return SPV_END_OF_STREAM;
+ case ';':
+ if (spv_result_t error = advanceLine(text, position)) return error;
+ continue;
+ case ' ':
+ case '\t':
+ case '\r':
+ position->column++;
+ position->index++;
+ continue;
+ case '\n':
+ position->column = 0;
+ position->line++;
+ position->index++;
+ continue;
+ default:
+ return SPV_SUCCESS;
+ }
}
- return SPV_SUCCESS;
}
// Fetches the next word from the given text stream starting from the given
diff --git a/source/util/hash_combine.h b/source/util/hash_combine.h
new file mode 100644
index 00000000..1a2dbc33
--- /dev/null
+++ b/source/util/hash_combine.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+//
+// 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_UTIL_HASH_COMBINE_H_
+#define SOURCE_UTIL_HASH_COMBINE_H_
+
+#include <cstddef>
+#include <functional>
+#include <vector>
+
+namespace spvtools {
+namespace utils {
+
+// Helpers for incrementally computing hashes.
+// For reference, see
+// http://open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf
+
+template <typename T>
+inline size_t hash_combine(std::size_t seed, const T& val) {
+ return seed ^ (std::hash<T>()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2));
+}
+
+template <typename T>
+inline size_t hash_combine(std::size_t hash, const std::vector<T>& vals) {
+ for (const T& val : vals) {
+ hash = hash_combine(hash, val);
+ }
+ return hash;
+}
+
+inline size_t hash_combine(std::size_t hash) { return hash; }
+
+template <typename T, typename... Types>
+inline size_t hash_combine(std::size_t hash, const T& val,
+ const Types&... args) {
+ return hash_combine(hash_combine(hash, val), args...);
+}
+
+} // namespace utils
+} // namespace spvtools
+
+#endif // SOURCE_UTIL_HASH_COMBINE_H_
diff --git a/source/util/hex_float.h b/source/util/hex_float.h
index 903b6288..06e3c575 100644
--- a/source/util/hex_float.h
+++ b/source/util/hex_float.h
@@ -209,9 +209,10 @@ std::istream& operator>>(std::istream& is, FloatProxy<T>& value) {
// be the default for any non-specialized type.
template <typename T>
struct HexFloatTraits {
- // Integer type that can store this hex-float.
+ // Integer type that can store the bit representation of this hex-float.
using uint_type = void;
- // Signed integer type that can store this hex-float.
+ // Signed integer type that can store the bit representation of this
+ // hex-float.
using int_type = void;
// The numerical type that this HexFloat represents.
using underlying_type = void;
@@ -958,9 +959,15 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
// This "looks" like a hex-float so treat it as one.
bool seen_p = false;
bool seen_dot = false;
- uint_type fraction_index = 0;
+ // The mantissa bits, without the most significant 1 bit, and with the
+ // the most recently read bits in the least significant positions.
uint_type fraction = 0;
+ // The number of mantissa bits that have been read, including the leading 1
+ // bit that is not written into 'fraction'.
+ uint_type fraction_index = 0;
+
+ // TODO(dneto): handle overflow and underflow
int_type exponent = HF::exponent_bias;
// Strip off leading zeros so we don't have to special-case them later.
@@ -968,11 +975,13 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
is.get();
}
- bool is_denorm =
- true; // Assume denorm "representation" until we hear otherwise.
- // NB: This does not mean the value is actually denorm,
- // it just means that it was written 0.
+ // Does the mantissa, as written, have non-zero digits to the left of
+ // the decimal point. Assume no until proven otherwise.
+ bool has_integer_part = false;
bool bits_written = false; // Stays false until we write a bit.
+
+ // Scan the mantissa hex digits until we see a '.' or the 'p' that
+ // starts the exponent.
while (!seen_p && !seen_dot) {
// Handle characters that are left of the fractional part.
if (next_char == '.') {
@@ -980,9 +989,8 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
} else if (next_char == 'p') {
seen_p = true;
} else if (::isxdigit(next_char)) {
- // We know this is not denormalized since we have stripped all leading
- // zeroes and we are not a ".".
- is_denorm = false;
+ // We have stripped all leading zeroes and we have not yet seen a ".".
+ has_integer_part = true;
int number = get_nibble_from_character(next_char);
for (int i = 0; i < 4; ++i, number <<= 1) {
uint_type write_bit = (number & 0x8) ? 0x1 : 0x0;
@@ -993,8 +1001,12 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
fraction |
static_cast<uint_type>(
write_bit << (HF::top_bit_left_shift - fraction_index++)));
+ // TODO(dneto): Avoid overflow. Testing would require
+ // parameterization.
exponent = static_cast<int_type>(exponent + 1);
}
+ // Since this updated after setting fraction bits, this effectively
+ // drops the leading 1 bit.
bits_written |= write_bit != 0;
}
} else {
@@ -1018,10 +1030,12 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
for (int i = 0; i < 4; ++i, number <<= 1) {
uint_type write_bit = (number & 0x8) ? 0x01 : 0x00;
bits_written |= write_bit != 0;
- if (is_denorm && !bits_written) {
+ if ((!has_integer_part) && !bits_written) {
// Handle modifying the exponent here this way we can handle
// an arbitrary number of hex values without overflowing our
// integer.
+ // TODO(dneto): Handle underflow. Testing would require extra
+ // parameterization.
exponent = static_cast<int_type>(exponent - 1);
} else {
fraction = static_cast<uint_type>(
@@ -1043,25 +1057,40 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
// Finished reading the part preceding 'p'.
// In hex floats syntax, the binary exponent is required.
- bool seen_sign = false;
+ bool seen_exponent_sign = false;
int8_t exponent_sign = 1;
bool seen_written_exponent_digits = false;
+ // The magnitude of the exponent, as written, or the sentinel value to signal
+ // overflow.
int_type written_exponent = 0;
+ // A sentinel value signalling overflow of the magnitude of the written
+ // exponent. We'll assume that -written_exponent_overflow is valid for the
+ // type. Later we may add 1 or subtract 1 from the adjusted exponent, so leave
+ // room for an extra 1.
+ const int_type written_exponent_overflow =
+ std::numeric_limits<int_type>::max() - 1;
while (true) {
if (!seen_written_exponent_digits &&
(next_char == '-' || next_char == '+')) {
- if (seen_sign) {
+ if (seen_exponent_sign) {
is.setstate(std::ios::failbit);
return is;
}
- seen_sign = true;
+ seen_exponent_sign = true;
exponent_sign = (next_char == '-') ? -1 : 1;
} else if (::isdigit(next_char)) {
seen_written_exponent_digits = true;
// Hex-floats express their exponent as decimal.
- written_exponent = static_cast<int_type>(written_exponent * 10);
- written_exponent =
- static_cast<int_type>(written_exponent + (next_char - '0'));
+ int_type digit =
+ static_cast<int_type>(static_cast<int_type>(next_char) - '0');
+ if (written_exponent >= (written_exponent_overflow - digit) / 10) {
+ // The exponent is very big. Saturate rather than overflow the exponent.
+ // signed integer, which would be undefined behaviour.
+ written_exponent = written_exponent_overflow;
+ } else {
+ written_exponent = static_cast<int_type>(
+ static_cast<int_type>(written_exponent * 10) + digit);
+ }
} else {
break;
}
@@ -1075,10 +1104,29 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
}
written_exponent = static_cast<int_type>(written_exponent * exponent_sign);
- exponent = static_cast<int_type>(exponent + written_exponent);
+ // Now fold in the exponent bias into the written exponent, updating exponent.
+ // But avoid undefined behaviour that would result from overflowing int_type.
+ if (written_exponent >= 0 && exponent >= 0) {
+ // Saturate up to written_exponent_overflow.
+ if (written_exponent_overflow - exponent > written_exponent) {
+ exponent = static_cast<int_type>(written_exponent + exponent);
+ } else {
+ exponent = written_exponent_overflow;
+ }
+ } else if (written_exponent < 0 && exponent < 0) {
+ // Saturate down to -written_exponent_overflow.
+ if (written_exponent_overflow + exponent > -written_exponent) {
+ exponent = static_cast<int_type>(written_exponent + exponent);
+ } else {
+ exponent = static_cast<int_type>(-written_exponent_overflow);
+ }
+ } else {
+ // They're of opposing sign, so it's safe to add.
+ exponent = static_cast<int_type>(written_exponent + exponent);
+ }
- bool is_zero = is_denorm && (fraction == 0);
- if (is_denorm && !is_zero) {
+ bool is_zero = (!has_integer_part) && (fraction == 0);
+ if ((!has_integer_part) && !is_zero) {
fraction = static_cast<uint_type>(fraction << 1);
exponent = static_cast<int_type>(exponent - 1);
} else if (is_zero) {
@@ -1095,7 +1143,7 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
const int_type max_exponent =
SetBits<uint_type, 0, HF::num_exponent_bits>::get;
- // Handle actual denorm numbers
+ // Handle denorm numbers
while (exponent < 0 && !is_zero) {
fraction = static_cast<uint_type>(fraction >> 1);
exponent = static_cast<int_type>(exponent + 1);
diff --git a/source/util/ilist.h b/source/util/ilist.h
index b7ecf01e..42d5e62b 100644
--- a/source/util/ilist.h
+++ b/source/util/ilist.h
@@ -348,6 +348,7 @@ void IntrusiveList<NodeType>::Check(NodeType* start) {
p = p->next_node_;
} while (p != start);
assert(sentinel_count == 1 && "List should have exactly 1 sentinel node.");
+ (void)sentinel_count;
p = start;
do {
diff --git a/source/util/small_vector.h b/source/util/small_vector.h
index f1762a9f..648a3482 100644
--- a/source/util/small_vector.h
+++ b/source/util/small_vector.h
@@ -64,6 +64,11 @@ class SmallVector {
}
}
+ template <class InputIt>
+ SmallVector(InputIt first, InputIt last) : SmallVector() {
+ insert(end(), first, last);
+ }
+
SmallVector(std::vector<T>&& vec) : SmallVector() {
if (vec.size() > small_size) {
large_data_ = MakeUnique<std::vector<T>>(std::move(vec));
@@ -328,6 +333,15 @@ class SmallVector {
++size_;
}
+ void pop_back() {
+ if (large_data_) {
+ large_data_->pop_back();
+ } else {
+ --size_;
+ small_data_[size_].~T();
+ }
+ }
+
template <class InputIt>
iterator insert(iterator pos, InputIt first, InputIt last) {
size_t element_idx = (pos - begin());
diff --git a/source/val/basic_block.cpp b/source/val/basic_block.cpp
index b2a8793d..da05db3a 100644
--- a/source/val/basic_block.cpp
+++ b/source/val/basic_block.cpp
@@ -24,11 +24,13 @@ namespace val {
BasicBlock::BasicBlock(uint32_t label_id)
: id_(label_id),
immediate_dominator_(nullptr),
- immediate_post_dominator_(nullptr),
+ immediate_structural_dominator_(nullptr),
+ immediate_structural_post_dominator_(nullptr),
predecessors_(),
successors_(),
type_(0),
reachable_(false),
+ structurally_reachable_(false),
label_(nullptr),
terminator_(nullptr) {}
@@ -36,21 +38,32 @@ void BasicBlock::SetImmediateDominator(BasicBlock* dom_block) {
immediate_dominator_ = dom_block;
}
-void BasicBlock::SetImmediatePostDominator(BasicBlock* pdom_block) {
- immediate_post_dominator_ = pdom_block;
+void BasicBlock::SetImmediateStructuralDominator(BasicBlock* dom_block) {
+ immediate_structural_dominator_ = dom_block;
+}
+
+void BasicBlock::SetImmediateStructuralPostDominator(BasicBlock* pdom_block) {
+ immediate_structural_post_dominator_ = pdom_block;
}
const BasicBlock* BasicBlock::immediate_dominator() const {
return immediate_dominator_;
}
-const BasicBlock* BasicBlock::immediate_post_dominator() const {
- return immediate_post_dominator_;
+const BasicBlock* BasicBlock::immediate_structural_dominator() const {
+ return immediate_structural_dominator_;
+}
+
+const BasicBlock* BasicBlock::immediate_structural_post_dominator() const {
+ return immediate_structural_post_dominator_;
}
BasicBlock* BasicBlock::immediate_dominator() { return immediate_dominator_; }
-BasicBlock* BasicBlock::immediate_post_dominator() {
- return immediate_post_dominator_;
+BasicBlock* BasicBlock::immediate_structural_dominator() {
+ return immediate_structural_dominator_;
+}
+BasicBlock* BasicBlock::immediate_structural_post_dominator() {
+ return immediate_structural_post_dominator_;
}
void BasicBlock::RegisterSuccessors(
@@ -58,6 +71,10 @@ void BasicBlock::RegisterSuccessors(
for (auto& block : next_blocks) {
block->predecessors_.push_back(this);
successors_.push_back(block);
+
+ // Register structural successors/predecessors too.
+ block->structural_predecessors_.push_back(this);
+ structural_successors_.push_back(block);
}
}
@@ -67,10 +84,16 @@ bool BasicBlock::dominates(const BasicBlock& other) const {
std::find(other.dom_begin(), other.dom_end(), this));
}
-bool BasicBlock::postdominates(const BasicBlock& other) const {
- return (this == &other) ||
- !(other.pdom_end() ==
- std::find(other.pdom_begin(), other.pdom_end(), this));
+bool BasicBlock::structurally_dominates(const BasicBlock& other) const {
+ return (this == &other) || !(other.structural_dom_end() ==
+ std::find(other.structural_dom_begin(),
+ other.structural_dom_end(), this));
+}
+
+bool BasicBlock::structurally_postdominates(const BasicBlock& other) const {
+ return (this == &other) || !(other.structural_pdom_end() ==
+ std::find(other.structural_pdom_begin(),
+ other.structural_pdom_end(), this));
}
BasicBlock::DominatorIterator::DominatorIterator() : current_(nullptr) {}
@@ -107,21 +130,43 @@ BasicBlock::DominatorIterator BasicBlock::dom_end() {
return DominatorIterator();
}
-const BasicBlock::DominatorIterator BasicBlock::pdom_begin() const {
- return DominatorIterator(
- this, [](const BasicBlock* b) { return b->immediate_post_dominator(); });
+const BasicBlock::DominatorIterator BasicBlock::structural_dom_begin() const {
+ return DominatorIterator(this, [](const BasicBlock* b) {
+ return b->immediate_structural_dominator();
+ });
}
-BasicBlock::DominatorIterator BasicBlock::pdom_begin() {
- return DominatorIterator(
- this, [](const BasicBlock* b) { return b->immediate_post_dominator(); });
+BasicBlock::DominatorIterator BasicBlock::structural_dom_begin() {
+ return DominatorIterator(this, [](const BasicBlock* b) {
+ return b->immediate_structural_dominator();
+ });
+}
+
+const BasicBlock::DominatorIterator BasicBlock::structural_dom_end() const {
+ return DominatorIterator();
+}
+
+BasicBlock::DominatorIterator BasicBlock::structural_dom_end() {
+ return DominatorIterator();
+}
+
+const BasicBlock::DominatorIterator BasicBlock::structural_pdom_begin() const {
+ return DominatorIterator(this, [](const BasicBlock* b) {
+ return b->immediate_structural_post_dominator();
+ });
+}
+
+BasicBlock::DominatorIterator BasicBlock::structural_pdom_begin() {
+ return DominatorIterator(this, [](const BasicBlock* b) {
+ return b->immediate_structural_post_dominator();
+ });
}
-const BasicBlock::DominatorIterator BasicBlock::pdom_end() const {
+const BasicBlock::DominatorIterator BasicBlock::structural_pdom_end() const {
return DominatorIterator();
}
-BasicBlock::DominatorIterator BasicBlock::pdom_end() {
+BasicBlock::DominatorIterator BasicBlock::structural_pdom_end() {
return DominatorIterator();
}
diff --git a/source/val/basic_block.h b/source/val/basic_block.h
index 47cd06d0..be5657ea 100644
--- a/source/val/basic_block.h
+++ b/source/val/basic_block.h
@@ -64,9 +64,32 @@ class BasicBlock {
/// Returns the successors of the BasicBlock
std::vector<BasicBlock*>* successors() { return &successors_; }
- /// Returns true if the block is reachable in the CFG
+ /// Returns the structural successors of the BasicBlock
+ std::vector<BasicBlock*>* structural_predecessors() {
+ return &structural_predecessors_;
+ }
+
+ /// Returns the structural predecessors of the BasicBlock
+ const std::vector<BasicBlock*>* structural_predecessors() const {
+ return &structural_predecessors_;
+ }
+
+ /// Returns the structural successors of the BasicBlock
+ std::vector<BasicBlock*>* structural_successors() {
+ return &structural_successors_;
+ }
+
+ /// Returns the structural predecessors of the BasicBlock
+ const std::vector<BasicBlock*>* structural_successors() const {
+ return &structural_successors_;
+ }
+
+ /// Returns true if the block is reachable in the CFG.
bool reachable() const { return reachable_; }
+ /// Returns true if the block is structurally reachable in the CFG.
+ bool structurally_reachable() const { return structurally_reachable_; }
+
/// Returns true if BasicBlock is of the given type
bool is_type(BlockType type) const {
if (type == kBlockTypeUndefined) return type_.none();
@@ -76,6 +99,11 @@ class BasicBlock {
/// Sets the reachability of the basic block in the CFG
void set_reachable(bool reachability) { reachable_ = reachability; }
+ /// Sets the structural reachability of the basic block in the CFG
+ void set_structurally_reachable(bool reachability) {
+ structurally_reachable_ = reachability;
+ }
+
/// Sets the type of the BasicBlock
void set_type(BlockType type) {
if (type == kBlockTypeUndefined)
@@ -89,10 +117,15 @@ class BasicBlock {
/// @param[in] dom_block The dominator block
void SetImmediateDominator(BasicBlock* dom_block);
+ /// Sets the immediate dominator of this basic block
+ ///
+ /// @param[in] dom_block The dominator block
+ void SetImmediateStructuralDominator(BasicBlock* dom_block);
+
/// Sets the immediate post dominator of this basic block
///
/// @param[in] pdom_block The post dominator block
- void SetImmediatePostDominator(BasicBlock* pdom_block);
+ void SetImmediateStructuralPostDominator(BasicBlock* pdom_block);
/// Returns the immediate dominator of this basic block
BasicBlock* immediate_dominator();
@@ -100,11 +133,17 @@ class BasicBlock {
/// Returns the immediate dominator of this basic block
const BasicBlock* immediate_dominator() const;
+ /// Returns the immediate dominator of this basic block
+ BasicBlock* immediate_structural_dominator();
+
+ /// Returns the immediate dominator of this basic block
+ const BasicBlock* immediate_structural_dominator() const;
+
/// Returns the immediate post dominator of this basic block
- BasicBlock* immediate_post_dominator();
+ BasicBlock* immediate_structural_post_dominator();
/// Returns the immediate post dominator of this basic block
- const BasicBlock* immediate_post_dominator() const;
+ const BasicBlock* immediate_structural_post_dominator() const;
/// Returns the label instruction for the block, or nullptr if not set.
const Instruction* label() const { return label_; }
@@ -132,9 +171,18 @@ class BasicBlock {
/// Assumes dominators have been computed.
bool dominates(const BasicBlock& other) const;
- /// Returns true if this block postdominates the other block.
- /// Assumes dominators have been computed.
- bool postdominates(const BasicBlock& other) const;
+ /// Returns true if this block structurally dominates the other block.
+ /// Assumes structural dominators have been computed.
+ bool structurally_dominates(const BasicBlock& other) const;
+
+ /// Returns true if this block structurally postdominates the other block.
+ /// Assumes structural dominators have been computed.
+ bool structurally_postdominates(const BasicBlock& other) const;
+
+ void RegisterStructuralSuccessor(BasicBlock* block) {
+ block->structural_predecessors_.push_back(this);
+ structural_successors_.push_back(block);
+ }
/// @brief A BasicBlock dominator iterator class
///
@@ -191,18 +239,32 @@ class BasicBlock {
/// block
DominatorIterator dom_end();
+ /// Returns a dominator iterator which points to the current block
+ const DominatorIterator structural_dom_begin() const;
+
+ /// Returns a dominator iterator which points to the current block
+ DominatorIterator structural_dom_begin();
+
+ /// Returns a dominator iterator which points to one element past the first
+ /// block
+ const DominatorIterator structural_dom_end() const;
+
+ /// Returns a dominator iterator which points to one element past the first
+ /// block
+ DominatorIterator structural_dom_end();
+
/// Returns a post dominator iterator which points to the current block
- const DominatorIterator pdom_begin() const;
+ const DominatorIterator structural_pdom_begin() const;
/// Returns a post dominator iterator which points to the current block
- DominatorIterator pdom_begin();
+ DominatorIterator structural_pdom_begin();
/// Returns a post dominator iterator which points to one element past the
/// last block
- const DominatorIterator pdom_end() const;
+ const DominatorIterator structural_pdom_end() const;
/// Returns a post dominator iterator which points to one element past the
/// last block
- DominatorIterator pdom_end();
+ DominatorIterator structural_pdom_end();
private:
/// Id of the BasicBlock
@@ -211,8 +273,11 @@ class BasicBlock {
/// Pointer to the immediate dominator of the BasicBlock
BasicBlock* immediate_dominator_;
- /// Pointer to the immediate dominator of the BasicBlock
- BasicBlock* immediate_post_dominator_;
+ /// Pointer to the immediate structural dominator of the BasicBlock
+ BasicBlock* immediate_structural_dominator_;
+
+ /// Pointer to the immediate structural post dominator of the BasicBlock
+ BasicBlock* immediate_structural_post_dominator_;
/// The set of predecessors of the BasicBlock
std::vector<BasicBlock*> predecessors_;
@@ -226,11 +291,17 @@ class BasicBlock {
/// True if the block is reachable in the CFG
bool reachable_;
+ /// True if the block is structurally reachable in the CFG
+ bool structurally_reachable_;
+
/// label of this block, if any.
const Instruction* label_;
/// Terminator of this block.
const Instruction* terminator_;
+
+ std::vector<BasicBlock*> structural_predecessors_;
+ std::vector<BasicBlock*> structural_successors_;
};
/// @brief Returns true if the iterators point to the same element or if both
diff --git a/source/val/construct.cpp b/source/val/construct.cpp
index 251e2bba..52e61d55 100644
--- a/source/val/construct.cpp
+++ b/source/val/construct.cpp
@@ -70,60 +70,45 @@ BasicBlock* Construct::exit_block() { return exit_block_; }
void Construct::set_exit(BasicBlock* block) { exit_block_ = block; }
-Construct::ConstructBlockSet Construct::blocks(Function* function) const {
- auto header = entry_block();
- auto merge = exit_block();
- assert(header);
- int header_depth = function->GetBlockDepth(const_cast<BasicBlock*>(header));
- ConstructBlockSet construct_blocks;
- std::unordered_set<BasicBlock*> corresponding_headers;
- for (auto& other : corresponding_constructs()) {
- // The corresponding header can be the same block as this construct's
- // header for loops with no loop construct. In those cases, don't add the
- // loop header as it prevents finding any blocks in the construct.
- if (type() != ConstructType::kContinue || other->entry_block() != header) {
- corresponding_headers.insert(other->entry_block());
- }
+Construct::ConstructBlockSet Construct::blocks(Function* /*function*/) const {
+ const auto header = entry_block();
+ const auto exit = exit_block();
+ const bool is_continue = type() == ConstructType::kContinue;
+ const bool is_loop = type() == ConstructType::kLoop;
+ const BasicBlock* continue_header = nullptr;
+ if (is_loop) {
+ // The only corresponding construct for a loop is the continue.
+ continue_header = (*corresponding_constructs().begin())->entry_block();
}
std::vector<BasicBlock*> stack;
stack.push_back(const_cast<BasicBlock*>(header));
+ ConstructBlockSet construct_blocks;
while (!stack.empty()) {
- BasicBlock* block = stack.back();
+ auto* block = stack.back();
stack.pop_back();
- if (merge == block && ExitBlockIsMergeBlock()) {
- // Merge block is not part of the construct.
- continue;
- }
-
- if (corresponding_headers.count(block)) {
- // Entered a corresponding construct.
- continue;
- }
-
- int block_depth = function->GetBlockDepth(block);
- if (block_depth < header_depth) {
- // Broke to outer construct.
- continue;
- }
-
- // In a loop, the continue target is at a depth of the loop construct + 1.
- // A selection construct nested directly within the loop construct is also
- // at the same depth. It is valid, however, to branch directly to the
- // continue target from within the selection construct.
- if (block != header && block_depth == header_depth &&
- type() == ConstructType::kSelection &&
- block->is_type(kBlockTypeContinue)) {
- // Continued to outer construct.
- continue;
- }
-
- if (!construct_blocks.insert(block).second) continue;
+ if (header->structurally_dominates(*block)) {
+ bool include = false;
+ if (is_continue && exit->structurally_postdominates(*block)) {
+ // Continue construct include blocks dominated by the continue target
+ // and post-dominated by the back-edge block.
+ include = true;
+ } else if (!exit->structurally_dominates(*block)) {
+ // Selection and loop constructs include blocks dominated by the header
+ // and not dominated by the merge.
+ include = true;
+ if (is_loop && continue_header->structurally_dominates(*block)) {
+ // Loop constructs have an additional constraint that they do not
+ // include blocks dominated by the continue construct. Since all
+ // blocks in the continue construct are dominated by the continue
+ // target, we just test for dominance by continue target.
+ include = false;
+ }
+ }
+ if (include) {
+ if (!construct_blocks.insert(block).second) continue;
- if (merge != block) {
- for (auto succ : *block->successors()) {
- // All blocks in the construct must be dominated by the header.
- if (header->dominates(*succ)) {
+ for (auto succ : *block->structural_successors()) {
stack.push_back(succ);
}
}
@@ -181,11 +166,12 @@ bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const {
for (auto& use : block->label()->uses()) {
if ((use.first->opcode() == SpvOpLoopMerge ||
use.first->opcode() == SpvOpSelectionMerge) &&
- use.second == 1 && use.first->block()->dominates(*block)) {
+ use.second == 1 &&
+ use.first->block()->structurally_dominates(*block)) {
return use.first->block();
}
}
- return block->immediate_dominator();
+ return block->immediate_structural_dominator();
};
bool seen_switch = false;
@@ -201,7 +187,7 @@ bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const {
terminator->opcode() == SpvOpSwitch)) {
auto merge_target = merge_inst->GetOperandAs<uint32_t>(0u);
auto merge_block = merge_inst->function()->GetBlock(merge_target).first;
- if (merge_block->dominates(*header)) {
+ if (merge_block->structurally_dominates(*header)) {
block = NextBlock(block);
continue;
}
diff --git a/source/val/decoration.h b/source/val/decoration.h
index ed3320f8..4f53f207 100644
--- a/source/val/decoration.h
+++ b/source/val/decoration.h
@@ -69,6 +69,15 @@ class Decoration {
std::vector<uint32_t>& params() { return params_; }
const std::vector<uint32_t>& params() const { return params_; }
+ inline bool operator<(const Decoration& rhs) const {
+ // Note: Sort by struct_member_index_ first, then type, so look up can be
+ // efficient using lower_bound() and upper_bound().
+ if (struct_member_index_ < rhs.struct_member_index_) return true;
+ if (rhs.struct_member_index_ < struct_member_index_) return false;
+ if (dec_type_ < rhs.dec_type_) return true;
+ if (rhs.dec_type_ < dec_type_) return false;
+ return params_ < rhs.params_;
+ }
inline bool operator==(const Decoration& rhs) const {
return (dec_type_ == rhs.dec_type_ && params_ == rhs.params_ &&
struct_member_index_ == rhs.struct_member_index_);
diff --git a/source/val/function.cpp b/source/val/function.cpp
index f3292b0e..fc7ccd06 100644
--- a/source/val/function.cpp
+++ b/source/val/function.cpp
@@ -73,6 +73,8 @@ spv_result_t Function::RegisterLoopMerge(uint32_t merge_id,
BasicBlock& continue_target_block = blocks_.at(continue_id);
assert(current_block_ &&
"RegisterLoopMerge must be called when called within a block");
+ current_block_->RegisterStructuralSuccessor(&merge_block);
+ current_block_->RegisterStructuralSuccessor(&continue_target_block);
current_block_->set_type(kBlockTypeLoop);
merge_block.set_type(kBlockTypeMerge);
@@ -101,6 +103,7 @@ spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) {
current_block_->set_type(kBlockTypeSelection);
merge_block.set_type(kBlockTypeMerge);
merge_block_header_[&merge_block] = current_block_;
+ current_block_->RegisterStructuralSuccessor(&merge_block);
AddConstruct({ConstructType::kSelection, current_block(), &merge_block});
@@ -251,29 +254,43 @@ Function::GetBlocksFunction Function::AugmentedCFGSuccessorsFunction() const {
};
}
-Function::GetBlocksFunction
-Function::AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const {
+Function::GetBlocksFunction Function::AugmentedCFGPredecessorsFunction() const {
+ return [this](const BasicBlock* block) {
+ auto where = augmented_predecessors_map_.find(block);
+ return where == augmented_predecessors_map_.end() ? block->predecessors()
+ : &(*where).second;
+ };
+}
+
+Function::GetBlocksFunction Function::AugmentedStructuralCFGSuccessorsFunction()
+ const {
return [this](const BasicBlock* block) {
- auto where = loop_header_successors_plus_continue_target_map_.find(block);
- return where == loop_header_successors_plus_continue_target_map_.end()
- ? AugmentedCFGSuccessorsFunction()(block)
+ auto where = augmented_successors_map_.find(block);
+ return where == augmented_successors_map_.end()
+ ? block->structural_successors()
: &(*where).second;
};
}
-Function::GetBlocksFunction Function::AugmentedCFGPredecessorsFunction() const {
+Function::GetBlocksFunction
+Function::AugmentedStructuralCFGPredecessorsFunction() const {
return [this](const BasicBlock* block) {
auto where = augmented_predecessors_map_.find(block);
- return where == augmented_predecessors_map_.end() ? block->predecessors()
- : &(*where).second;
+ return where == augmented_predecessors_map_.end()
+ ? block->structural_predecessors()
+ : &(*where).second;
};
}
void Function::ComputeAugmentedCFG() {
// Compute the successors of the pseudo-entry block, and
// the predecessors of the pseudo exit block.
- auto succ_func = [](const BasicBlock* b) { return b->successors(); };
- auto pred_func = [](const BasicBlock* b) { return b->predecessors(); };
+ auto succ_func = [](const BasicBlock* b) {
+ return b->structural_successors();
+ };
+ auto pred_func = [](const BasicBlock* b) {
+ return b->structural_predecessors();
+ };
CFA<BasicBlock>::ComputeAugmentedCFG(
ordered_blocks_, &pseudo_entry_block_, &pseudo_exit_block_,
&augmented_successors_map_, &augmented_predecessors_map_, succ_func,
diff --git a/source/val/function.h b/source/val/function.h
index 2fe30bdc..126b1dc7 100644
--- a/source/val/function.h
+++ b/source/val/function.h
@@ -184,12 +184,12 @@ class Function {
std::function<const std::vector<BasicBlock*>*(const BasicBlock*)>;
/// Returns the block successors function for the augmented CFG.
GetBlocksFunction AugmentedCFGSuccessorsFunction() const;
- /// Like AugmentedCFGSuccessorsFunction, but also includes a forward edge from
- /// a loop header block to its continue target, if they are different blocks.
- GetBlocksFunction
- AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const;
/// Returns the block predecessors function for the augmented CFG.
GetBlocksFunction AugmentedCFGPredecessorsFunction() const;
+ /// Returns the block structural successors function for the augmented CFG.
+ GetBlocksFunction AugmentedStructuralCFGSuccessorsFunction() const;
+ /// Returns the block structural predecessors function for the augmented CFG.
+ GetBlocksFunction AugmentedStructuralCFGPredecessorsFunction() const;
/// Returns the control flow nesting depth of the given basic block.
/// This function only works when you have structured control flow.
diff --git a/source/val/validate.cpp b/source/val/validate.cpp
index ecc9fdb6..9a685f22 100644
--- a/source/val/validate.cpp
+++ b/source/val/validate.cpp
@@ -293,6 +293,11 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr)
<< "Missing OpFunctionEnd at end of module.";
+ if (vstate->HasCapability(SpvCapabilityBindlessTextureNV) &&
+ !vstate->has_samplerimage_variable_address_mode_specified())
+ return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr)
+ << "Missing required OpSamplerImageAddressingModeNV instruction.";
+
// Catch undefined forward references before performing further checks.
if (auto error = ValidateForwardDecls(*vstate)) return error;
@@ -345,6 +350,8 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
if (auto error = NonUniformPass(*vstate, &instruction)) return error;
if (auto error = LiteralsPass(*vstate, &instruction)) return error;
+ if (auto error = RayQueryPass(*vstate, &instruction)) return error;
+ if (auto error = RayTracingPass(*vstate, &instruction)) return error;
}
// Validate the preconditions involving adjacent instructions. e.g. SpvOpPhi
diff --git a/source/val/validate.h b/source/val/validate.h
index cb1d05a5..85c32d38 100644
--- a/source/val/validate.h
+++ b/source/val/validate.h
@@ -197,6 +197,12 @@ spv_result_t FunctionPass(ValidationState_t& _, const Instruction* inst);
/// Validates correctness of miscellaneous instructions.
spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst);
+/// Validates correctness of ray query instructions.
+spv_result_t RayQueryPass(ValidationState_t& _, const Instruction* inst);
+
+/// Validates correctness of ray tracing instructions.
+spv_result_t RayTracingPass(ValidationState_t& _, const Instruction* inst);
+
/// Calculates the reachability of basic blocks.
void ReachabilityPass(ValidationState_t& _);
diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp
index 0614e162..112c7cda 100644
--- a/source/val/validate_annotation.cpp
+++ b/source/val/validate_annotation.cpp
@@ -22,138 +22,6 @@ namespace spvtools {
namespace val {
namespace {
-std::string LogStringForDecoration(uint32_t decoration) {
- switch (decoration) {
- case SpvDecorationRelaxedPrecision:
- return "RelaxedPrecision";
- case SpvDecorationSpecId:
- return "SpecId";
- case SpvDecorationBlock:
- return "Block";
- case SpvDecorationBufferBlock:
- return "BufferBlock";
- case SpvDecorationRowMajor:
- return "RowMajor";
- case SpvDecorationColMajor:
- return "ColMajor";
- case SpvDecorationArrayStride:
- return "ArrayStride";
- case SpvDecorationMatrixStride:
- return "MatrixStride";
- case SpvDecorationGLSLShared:
- return "GLSLShared";
- case SpvDecorationGLSLPacked:
- return "GLSLPacked";
- case SpvDecorationCPacked:
- return "CPacked";
- case SpvDecorationBuiltIn:
- return "BuiltIn";
- case SpvDecorationNoPerspective:
- return "NoPerspective";
- case SpvDecorationFlat:
- return "Flat";
- case SpvDecorationPatch:
- return "Patch";
- case SpvDecorationCentroid:
- return "Centroid";
- case SpvDecorationSample:
- return "Sample";
- case SpvDecorationInvariant:
- return "Invariant";
- case SpvDecorationRestrict:
- return "Restrict";
- case SpvDecorationAliased:
- return "Aliased";
- case SpvDecorationVolatile:
- return "Volatile";
- case SpvDecorationConstant:
- return "Constant";
- case SpvDecorationCoherent:
- return "Coherent";
- case SpvDecorationNonWritable:
- return "NonWritable";
- case SpvDecorationNonReadable:
- return "NonReadable";
- case SpvDecorationUniform:
- return "Uniform";
- case SpvDecorationSaturatedConversion:
- return "SaturatedConversion";
- case SpvDecorationStream:
- return "Stream";
- case SpvDecorationLocation:
- return "Location";
- case SpvDecorationComponent:
- return "Component";
- case SpvDecorationIndex:
- return "Index";
- case SpvDecorationBinding:
- return "Binding";
- case SpvDecorationDescriptorSet:
- return "DescriptorSet";
- case SpvDecorationOffset:
- return "Offset";
- case SpvDecorationXfbBuffer:
- return "XfbBuffer";
- case SpvDecorationXfbStride:
- return "XfbStride";
- case SpvDecorationFuncParamAttr:
- return "FuncParamAttr";
- case SpvDecorationFPRoundingMode:
- return "FPRoundingMode";
- case SpvDecorationFPFastMathMode:
- return "FPFastMathMode";
- case SpvDecorationLinkageAttributes:
- return "LinkageAttributes";
- case SpvDecorationNoContraction:
- return "NoContraction";
- case SpvDecorationInputAttachmentIndex:
- return "InputAttachmentIndex";
- case SpvDecorationAlignment:
- return "Alignment";
- case SpvDecorationMaxByteOffset:
- return "MaxByteOffset";
- case SpvDecorationAlignmentId:
- return "AlignmentId";
- case SpvDecorationMaxByteOffsetId:
- return "MaxByteOffsetId";
- case SpvDecorationNoSignedWrap:
- return "NoSignedWrap";
- case SpvDecorationNoUnsignedWrap:
- return "NoUnsignedWrap";
- case SpvDecorationExplicitInterpAMD:
- return "ExplicitInterpAMD";
- case SpvDecorationOverrideCoverageNV:
- return "OverrideCoverageNV";
- case SpvDecorationPassthroughNV:
- return "PassthroughNV";
- case SpvDecorationViewportRelativeNV:
- return "ViewportRelativeNV";
- case SpvDecorationSecondaryViewportRelativeNV:
- return "SecondaryViewportRelativeNV";
- case SpvDecorationPerPrimitiveNV:
- return "PerPrimitiveNV";
- case SpvDecorationPerViewNV:
- return "PerViewNV";
- case SpvDecorationPerTaskNV:
- return "PerTaskNV";
- case SpvDecorationPerVertexNV:
- return "PerVertexNV";
- case SpvDecorationNonUniform:
- return "NonUniform";
- case SpvDecorationRestrictPointer:
- return "RestrictPointer";
- case SpvDecorationAliasedPointer:
- return "AliasedPointer";
- case SpvDecorationCounterBuffer:
- return "CounterBuffer";
- case SpvDecorationHlslSemanticGOOGLE:
- return "HlslSemanticGOOGLE";
- default:
- break;
- }
- return "Unknown";
-}
-
// Returns true if the decoration takes ID parameters.
// TODO(dneto): This can be generated from the grammar.
bool DecorationTakesIdParameters(SpvDecoration type) {
@@ -233,7 +101,7 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec,
auto fail = [&_, dec, inst, target](uint32_t vuid) -> DiagnosticStream {
DiagnosticStream ds = std::move(
_.diag(SPV_ERROR_INVALID_ID, inst)
- << _.VkErrorID(vuid) << LogStringForDecoration(dec)
+ << _.VkErrorID(vuid) << _.SpvDecorationString(dec)
<< " decoration on target <id> '" << _.getIdName(target->id()) << "' ");
return ds;
};
@@ -326,17 +194,20 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec,
case SpvDecorationLocation:
case SpvDecorationComponent:
// Location is used for input, output and ray tracing stages.
- if (sc == SpvStorageClassStorageBuffer ||
- sc == SpvStorageClassUniform ||
- sc == SpvStorageClassUniformConstant ||
- sc == SpvStorageClassWorkgroup || sc == SpvStorageClassPrivate ||
- sc == SpvStorageClassFunction) {
+ if (sc != SpvStorageClassInput && sc != SpvStorageClassOutput &&
+ sc != SpvStorageClassRayPayloadKHR &&
+ sc != SpvStorageClassIncomingRayPayloadKHR &&
+ sc != SpvStorageClassHitAttributeKHR &&
+ sc != SpvStorageClassCallableDataKHR &&
+ sc != SpvStorageClassIncomingCallableDataKHR &&
+ sc != SpvStorageClassShaderRecordBufferKHR) {
return _.diag(SPV_ERROR_INVALID_ID, target)
- << LogStringForDecoration(dec)
+ << _.VkErrorID(6672) << _.SpvDecorationString(dec)
<< " decoration must not be applied to this storage class";
}
break;
case SpvDecorationIndex:
+ // Langauge from SPIR-V definition of Index
if (sc != SpvStorageClassOutput) {
return fail(0) << "must be in the Output storage class";
}
@@ -346,13 +217,13 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec,
if (sc != SpvStorageClassStorageBuffer &&
sc != SpvStorageClassUniform &&
sc != SpvStorageClassUniformConstant) {
- return fail(0) << "must be in the StorageBuffer, Uniform, or "
- "UniformConstant storage class";
+ return fail(6491) << "must be in the StorageBuffer, Uniform, or "
+ "UniformConstant storage class";
}
break;
case SpvDecorationInputAttachmentIndex:
if (sc != SpvStorageClassUniformConstant) {
- return fail(0) << "must be in the UniformConstant storage class";
+ return fail(6678) << "must be in the UniformConstant storage class";
}
break;
case SpvDecorationFlat:
@@ -363,6 +234,11 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec,
return fail(4670) << "storage class must be Input or Output";
}
break;
+ case SpvDecorationPerVertexKHR:
+ if (sc != SpvStorageClassInput) {
+ return fail(6777) << "storage class must be Input";
+ }
+ break;
default:
break;
}
@@ -383,7 +259,7 @@ spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
(decoration == SpvDecorationGLSLPacked)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< _.VkErrorID(4669) << "OpDecorate decoration '"
- << LogStringForDecoration(decoration)
+ << _.SpvDecorationString(decoration)
<< "' is not valid for the Vulkan execution environment.";
}
}
@@ -397,7 +273,7 @@ spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
if (target->opcode() != SpvOpDecorationGroup) {
if (IsMemberDecorationOnly(decoration)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << LogStringForDecoration(decoration)
+ << _.SpvDecorationString(decoration)
<< " can only be applied to structure members";
}
@@ -450,7 +326,7 @@ spv_result_t ValidateMemberDecorate(ValidationState_t& _,
const auto decoration = inst->GetOperandAs<SpvDecoration>(2);
if (IsNotMemberDecoration(decoration)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << LogStringForDecoration(decoration)
+ << _.SpvDecorationString(decoration)
<< " cannot be applied to structure members";
}
@@ -573,7 +449,7 @@ spv_result_t RegisterDecorations(ValidationState_t& _,
// Word 1 is the group <id>. All subsequent words are target <id>s that
// are going to be decorated with the decorations.
const uint32_t decoration_group_id = inst->word(1);
- std::vector<Decoration>& group_decorations =
+ std::set<Decoration>& group_decorations =
_.id_decorations(decoration_group_id);
for (size_t i = 2; i < inst->words().size(); ++i) {
const uint32_t target_id = inst->word(i);
@@ -587,7 +463,7 @@ spv_result_t RegisterDecorations(ValidationState_t& _,
// pairs. All decorations of the group should be applied to all the struct
// members that are specified in the instructions.
const uint32_t decoration_group_id = inst->word(1);
- std::vector<Decoration>& group_decorations =
+ std::set<Decoration>& group_decorations =
_.id_decorations(decoration_group_id);
// Grammar checks ensures that the number of arguments to this instruction
// is an odd number: 1 decoration group + (id,literal) pairs.
diff --git a/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp
index cfa15d9f..bf565c31 100644
--- a/source/val/validate_atomics.cpp
+++ b/source/val/validate_atomics.cpp
@@ -39,7 +39,8 @@ bool IsStorageClassAllowedByUniversalRules(uint32_t storage_class) {
case SpvStorageClassAtomicCounter:
case SpvStorageClassImage:
case SpvStorageClassFunction:
- case SpvStorageClassPhysicalStorageBufferEXT:
+ case SpvStorageClassPhysicalStorageBuffer:
+ case SpvStorageClassTaskPayloadWorkgroupEXT:
return true;
break;
default:
@@ -206,12 +207,13 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
(storage_class != SpvStorageClassStorageBuffer) &&
(storage_class != SpvStorageClassWorkgroup) &&
(storage_class != SpvStorageClassImage) &&
- (storage_class != SpvStorageClassPhysicalStorageBuffer)) {
+ (storage_class != SpvStorageClassPhysicalStorageBuffer) &&
+ (storage_class != SpvStorageClassTaskPayloadWorkgroupEXT)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< _.VkErrorID(4686) << spvOpcodeString(opcode)
<< ": Vulkan spec only allows storage classes for atomic to "
- "be: Uniform, Workgroup, Image, StorageBuffer, or "
- "PhysicalStorageBuffer.";
+ "be: Uniform, Workgroup, Image, StorageBuffer, "
+ "PhysicalStorageBuffer or TaskPayloadWorkgroupEXT.";
}
} else if (storage_class == SpvStorageClassFunction) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
diff --git a/source/val/validate_barriers.cpp b/source/val/validate_barriers.cpp
index 3a9e3e7c..03225d86 100644
--- a/source/val/validate_barriers.cpp
+++ b/source/val/validate_barriers.cpp
@@ -50,7 +50,8 @@ spv_result_t BarriersPass(ValidationState_t& _, const Instruction* inst) {
*message =
"OpControlBarrier requires one of the following "
"Execution "
- "Models: TessellationControl, GLCompute or Kernel";
+ "Models: TessellationControl, GLCompute, Kernel, "
+ "MeshNV or TaskNV";
}
return false;
}
diff --git a/source/val/validate_bitwise.cpp b/source/val/validate_bitwise.cpp
index d46b3fca..e6e97c4a 100644
--- a/source/val/validate_bitwise.cpp
+++ b/source/val/validate_bitwise.cpp
@@ -14,16 +14,48 @@
// Validates correctness of bitwise instructions.
-#include "source/val/validate.h"
-
#include "source/diagnostic.h"
#include "source/opcode.h"
+#include "source/spirv_target_env.h"
#include "source/val/instruction.h"
+#include "source/val/validate.h"
#include "source/val/validation_state.h"
namespace spvtools {
namespace val {
+// Validates when base and result need to be the same type
+spv_result_t ValidateBaseType(ValidationState_t& _, const Instruction* inst,
+ const uint32_t base_type) {
+ const SpvOp opcode = inst->opcode();
+
+ if (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << _.VkErrorID(4781)
+ << "Expected int scalar or vector type for Base operand: "
+ << spvOpcodeString(opcode);
+ }
+
+ // Vulkan has a restriction to 32 bit for base
+ if (spvIsVulkanEnv(_.context()->target_env)) {
+ if (_.GetBitWidth(base_type) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << _.VkErrorID(4781)
+ << "Expected 32-bit int type for Base operand: "
+ << spvOpcodeString(opcode);
+ }
+ }
+
+ // OpBitCount just needs same number of components
+ if (base_type != inst->type_id() && opcode != SpvOpBitCount) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Expected Base Type to be equal to Result Type: "
+ << spvOpcodeString(opcode);
+ }
+
+ return SPV_SUCCESS;
+}
+
// Validates correctness of bitwise instructions.
spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) {
const SpvOp opcode = inst->opcode();
@@ -109,20 +141,14 @@ spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) {
}
case SpvOpBitFieldInsert: {
- if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type))
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected int scalar or vector type as Result Type: "
- << spvOpcodeString(opcode);
-
const uint32_t base_type = _.GetOperandTypeId(inst, 2);
const uint32_t insert_type = _.GetOperandTypeId(inst, 3);
const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
const uint32_t count_type = _.GetOperandTypeId(inst, 5);
- if (base_type != result_type)
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Base Type to be equal to Result Type: "
- << spvOpcodeString(opcode);
+ if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
+ return error;
+ }
if (insert_type != result_type)
return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -143,19 +169,13 @@ spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) {
case SpvOpBitFieldSExtract:
case SpvOpBitFieldUExtract: {
- if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type))
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected int scalar or vector type as Result Type: "
- << spvOpcodeString(opcode);
-
const uint32_t base_type = _.GetOperandTypeId(inst, 2);
const uint32_t offset_type = _.GetOperandTypeId(inst, 3);
const uint32_t count_type = _.GetOperandTypeId(inst, 4);
- if (base_type != result_type)
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Base Type to be equal to Result Type: "
- << spvOpcodeString(opcode);
+ if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
+ return error;
+ }
if (!offset_type || !_.IsIntScalarType(offset_type))
return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -170,17 +190,12 @@ spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) {
}
case SpvOpBitReverse: {
- if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type))
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected int scalar or vector type as Result Type: "
- << spvOpcodeString(opcode);
-
const uint32_t base_type = _.GetOperandTypeId(inst, 2);
- if (base_type != result_type)
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Base Type to be equal to Result Type: "
- << spvOpcodeString(opcode);
+ if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
+ return error;
+ }
+
break;
}
@@ -191,15 +206,13 @@ spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) {
<< spvOpcodeString(opcode);
const uint32_t base_type = _.GetOperandTypeId(inst, 2);
- if (!base_type ||
- (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type)))
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Base Type to be int scalar or vector: "
- << spvOpcodeString(opcode);
-
const uint32_t base_dimension = _.GetDimension(base_type);
const uint32_t result_dimension = _.GetDimension(result_type);
+ if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
+ return error;
+ }
+
if (base_dimension != result_dimension)
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Expected Base dimension to be equal to Result Type "
diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp
index 57dde8ad..d5b89eb1 100644
--- a/source/val/validate_builtins.cpp
+++ b/source/val/validate_builtins.cpp
@@ -120,7 +120,7 @@ typedef enum VUIDError_ {
VUIDErrorMax,
} VUIDError;
-const static uint32_t NumVUIDBuiltins = 33;
+const static uint32_t NumVUIDBuiltins = 36;
typedef struct {
SpvBuiltIn builtIn;
@@ -162,6 +162,9 @@ std::array<BuiltinVUIDMapping, NumVUIDBuiltins> builtinVUIDInfo = {{
{SpvBuiltInFragSizeEXT, {4220, 4221, 4222}},
{SpvBuiltInFragStencilRefEXT, {4223, 4224, 4225}},
{SpvBuiltInFullyCoveredEXT, {4232, 4233, 4234}},
+ {SpvBuiltInCullMaskKHR, {6735, 6736, 6737}},
+ {SpvBuiltInBaryCoordKHR, {4154, 4155, 4156}},
+ {SpvBuiltInBaryCoordNoPerspKHR, {4160, 4161, 4162}},
// clang-format off
} };
@@ -208,6 +211,7 @@ bool IsExecutionModelValidForRtBuiltIn(SpvBuiltIn builtin,
case SpvBuiltInRayTmaxKHR:
case SpvBuiltInWorldRayDirectionKHR:
case SpvBuiltInWorldRayOriginKHR:
+ case SpvBuiltInCullMaskKHR:
switch (stage) {
case SpvExecutionModelIntersectionKHR:
case SpvExecutionModelAnyHitKHR:
@@ -331,7 +335,9 @@ class BuiltInsValidator {
const Decoration& decoration, const Instruction& inst);
spv_result_t ValidateSMBuiltinsAtDefinition(const Decoration& decoration,
const Instruction& inst);
-
+ // Used for BaryCoord, BaryCoordNoPersp.
+ spv_result_t ValidateFragmentShaderF32Vec3InputAtDefinition(
+ const Decoration& decoration, const Instruction& inst);
// Used for SubgroupEqMask, SubgroupGeMask, SubgroupGtMask, SubgroupLtMask,
// SubgroupLeMask.
spv_result_t ValidateI32Vec4InputAtDefinition(const Decoration& decoration,
@@ -509,6 +515,13 @@ class BuiltInsValidator {
const Decoration& decoration, const Instruction& built_in_inst,
const Instruction& referenced_inst,
const Instruction& referenced_from_inst);
+
+ // Used for BaryCoord, BaryCoordNoPersp.
+ spv_result_t ValidateFragmentShaderF32Vec3InputAtReference(
+ const Decoration& decoration, const Instruction& built_in_inst,
+ const Instruction& referenced_inst,
+ const Instruction& referenced_from_inst);
+
// Used for SubgroupId and NumSubgroups.
spv_result_t ValidateComputeI32InputAtReference(
const Decoration& decoration, const Instruction& built_in_inst,
@@ -1166,9 +1179,16 @@ spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtReference(
&BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
"Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
"used for variables with Input storage class if execution model is "
- "Vertex.",
+ "MeshNV.",
SpvExecutionModelMeshNV, decoration, built_in_inst,
referenced_from_inst, std::placeholders::_1));
+ id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
+ &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
+ "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
+ "used for variables with Input storage class if execution model is "
+ "MeshEXT.",
+ SpvExecutionModelMeshEXT, decoration, built_in_inst,
+ referenced_from_inst, std::placeholders::_1));
}
if (storage_class == SpvStorageClassOutput) {
@@ -1211,7 +1231,8 @@ spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtReference(
case SpvExecutionModelTessellationControl:
case SpvExecutionModelTessellationEvaluation:
case SpvExecutionModelGeometry:
- case SpvExecutionModelMeshNV: {
+ case SpvExecutionModelMeshNV:
+ case SpvExecutionModelMeshEXT: {
if (decoration.struct_member_index() != Decoration::kInvalidMember) {
// The outer level of array is applied on the variable.
if (spv_result_t error = ValidateF32Arr(
@@ -1843,7 +1864,8 @@ spv_result_t BuiltInsValidator::ValidatePointSizeAtReference(
case SpvExecutionModelTessellationControl:
case SpvExecutionModelTessellationEvaluation:
case SpvExecutionModelGeometry:
- case SpvExecutionModelMeshNV: {
+ case SpvExecutionModelMeshNV:
+ case SpvExecutionModelMeshEXT: {
// PointSize can be a per-vertex variable for tessellation control,
// tessellation evaluation and geometry shader stages. In such cases
// variables will have an array of 32-bit floats.
@@ -1944,6 +1966,13 @@ spv_result_t BuiltInsValidator::ValidatePositionAtReference(
"with Input storage class if execution model is MeshNV.",
SpvExecutionModelMeshNV, decoration, built_in_inst,
referenced_from_inst, std::placeholders::_1));
+ id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
+ &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4319,
+ "Vulkan spec doesn't allow BuiltIn Position to be used "
+ "for variables "
+ "with Input storage class if execution model is MeshEXT.",
+ SpvExecutionModelMeshEXT, decoration, built_in_inst,
+ referenced_from_inst, std::placeholders::_1));
}
for (const SpvExecutionModel execution_model : execution_models_) {
@@ -1967,7 +1996,8 @@ spv_result_t BuiltInsValidator::ValidatePositionAtReference(
case SpvExecutionModelGeometry:
case SpvExecutionModelTessellationControl:
case SpvExecutionModelTessellationEvaluation:
- case SpvExecutionModelMeshNV: {
+ case SpvExecutionModelMeshNV:
+ case SpvExecutionModelMeshEXT: {
// Position can be a per-vertex variable for tessellation control,
// tessellation evaluation, geometry and mesh shader stages. In such
// cases variables will have an array of 4-component 32-bit float
@@ -2138,6 +2168,7 @@ spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtReference(
case SpvExecutionModelTessellationEvaluation:
case SpvExecutionModelGeometry:
case SpvExecutionModelMeshNV:
+ case SpvExecutionModelMeshEXT:
case SpvExecutionModelIntersectionKHR:
case SpvExecutionModelAnyHitKHR:
case SpvExecutionModelClosestHitKHR: {
@@ -2150,9 +2181,8 @@ spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtReference(
<< _.VkErrorID(4330)
<< "Vulkan spec allows BuiltIn PrimitiveId to be used only "
"with Fragment, TessellationControl, "
- "TessellationEvaluation, Geometry, MeshNV, "
- "IntersectionKHR, "
- "AnyHitKHR, and ClosestHitKHR execution models. "
+ "TessellationEvaluation, Geometry, MeshNV, MeshEXT, "
+ "IntersectionKHR, AnyHitKHR, and ClosestHitKHR execution models. "
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
referenced_from_inst, execution_model);
}
@@ -2700,7 +2730,8 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference(
assert(function_id_ == 0);
for (const auto em :
{SpvExecutionModelVertex, SpvExecutionModelTessellationEvaluation,
- SpvExecutionModelGeometry, SpvExecutionModelMeshNV}) {
+ SpvExecutionModelGeometry, SpvExecutionModelMeshNV,
+ SpvExecutionModelMeshEXT}) {
id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel,
this, ((operand == SpvBuiltInLayer) ? 4274 : 4406),
@@ -2708,7 +2739,7 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference(
"ViewportIndex to be "
"used for variables with Input storage class if "
"execution model is Vertex, TessellationEvaluation, "
- "Geometry, or MeshNV.",
+ "Geometry, MeshNV or MeshEXT.",
em, decoration, built_in_inst, referenced_from_inst,
std::placeholders::_1));
}
@@ -2733,6 +2764,7 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference(
case SpvExecutionModelGeometry:
case SpvExecutionModelFragment:
case SpvExecutionModelMeshNV:
+ case SpvExecutionModelMeshEXT:
// Ok.
break;
case SpvExecutionModelVertex:
@@ -2788,6 +2820,80 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference(
return SPV_SUCCESS;
}
+spv_result_t BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtDefinition(
+ const Decoration& decoration, const Instruction& inst) {
+ if (spvIsVulkanEnv(_.context()->target_env)) {
+ const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+ if (spv_result_t error = ValidateF32Vec(
+ decoration, inst, 3,
+ [this, &inst, builtin](const std::string& message) -> spv_result_t {
+ uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
+ return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+ << _.VkErrorID(vuid) << "According to the "
+ << spvLogStringForEnv(_.context()->target_env)
+ << " spec BuiltIn "
+ << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
+ builtin)
+ << " variable needs to be a 3-component 32-bit float "
+ "vector. "
+ << message;
+ })) {
+ return error;
+ }
+ }
+
+ // Seed at reference checks with this built-in.
+ return ValidateFragmentShaderF32Vec3InputAtReference(decoration, inst, inst,
+ inst);
+}
+
+spv_result_t BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtReference(
+ const Decoration& decoration, const Instruction& built_in_inst,
+ const Instruction& referenced_inst,
+ const Instruction& referenced_from_inst) {
+
+ if (spvIsVulkanEnv(_.context()->target_env)) {
+ const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+ const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
+ if (storage_class != SpvStorageClassMax &&
+ storage_class != SpvStorageClassInput) {
+ uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
+ return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+ << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
+ << " spec allows BuiltIn "
+ << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+ << " to be only used for variables with Input storage class. "
+ << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
+ referenced_from_inst)
+ << " " << GetStorageClassDesc(referenced_from_inst);
+ }
+
+ for (const SpvExecutionModel execution_model : execution_models_) {
+ if (execution_model != SpvExecutionModelFragment) {
+ uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
+ return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+ << _.VkErrorID(vuid)
+ << spvLogStringForEnv(_.context()->target_env)
+ << " spec allows BuiltIn "
+ << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+ << " to be used only with Fragment execution model. "
+ << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
+ referenced_from_inst, execution_model);
+ }
+ }
+ }
+
+ if (function_id_ == 0) {
+ // Propagate this rule to all dependant ids in the global scope.
+ id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
+ &BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtReference, this,
+ decoration, built_in_inst, referenced_from_inst,
+ std::placeholders::_1));
+ }
+
+ return SPV_SUCCESS;
+}
+
spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtDefinition(
const Decoration& decoration, const Instruction& inst) {
if (spvIsVulkanEnv(_.context()->target_env)) {
@@ -2838,7 +2944,10 @@ spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference(
for (const SpvExecutionModel execution_model : execution_models_) {
bool has_vulkan_model = execution_model == SpvExecutionModelGLCompute ||
execution_model == SpvExecutionModelTaskNV ||
- execution_model == SpvExecutionModelMeshNV;
+ execution_model == SpvExecutionModelMeshNV ||
+ execution_model == SpvExecutionModelTaskEXT ||
+ execution_model == SpvExecutionModelMeshEXT;
+
if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) {
uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
@@ -2846,7 +2955,8 @@ spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference(
<< spvLogStringForEnv(_.context()->target_env)
<< " spec allows BuiltIn "
<< _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
- << " to be used only with GLCompute, MeshNV, or TaskNV execution model. "
+ << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or"
+ << " TaskEXT execution model. "
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
referenced_from_inst, execution_model);
}
@@ -2920,7 +3030,9 @@ spv_result_t BuiltInsValidator::ValidateComputeI32InputAtReference(
for (const SpvExecutionModel execution_model : execution_models_) {
bool has_vulkan_model = execution_model == SpvExecutionModelGLCompute ||
execution_model == SpvExecutionModelTaskNV ||
- execution_model == SpvExecutionModelMeshNV;
+ execution_model == SpvExecutionModelMeshNV ||
+ execution_model == SpvExecutionModelTaskEXT ||
+ execution_model == SpvExecutionModelMeshEXT;
if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) {
uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
@@ -2928,7 +3040,8 @@ spv_result_t BuiltInsValidator::ValidateComputeI32InputAtReference(
<< spvLogStringForEnv(_.context()->target_env)
<< " spec allows BuiltIn "
<< _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
- << " to be used only with GLCompute, MeshNV, or TaskNV execution model. "
+ << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or "
+ << "TaskEXT execution model. "
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
referenced_from_inst, execution_model);
}
@@ -3072,14 +3185,17 @@ spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtReference(
for (const SpvExecutionModel execution_model : execution_models_) {
if (execution_model != SpvExecutionModelGLCompute &&
execution_model != SpvExecutionModelTaskNV &&
- execution_model != SpvExecutionModelMeshNV) {
+ execution_model != SpvExecutionModelMeshNV &&
+ execution_model != SpvExecutionModelTaskEXT &&
+ execution_model != SpvExecutionModelMeshEXT) {
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
<< _.VkErrorID(4425)
<< spvLogStringForEnv(_.context()->target_env)
<< " spec allows BuiltIn "
<< _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
decoration.params()[0])
- << " to be used only with GLCompute, MeshNV, or TaskNV execution model. "
+ << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or "
+ << "TaskEXT execution model. "
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
referenced_from_inst, execution_model);
}
@@ -3210,12 +3326,15 @@ spv_result_t BuiltInsValidator::ValidateDrawIndexAtReference(
for (const SpvExecutionModel execution_model : execution_models_) {
if (execution_model != SpvExecutionModelVertex &&
execution_model != SpvExecutionModelMeshNV &&
- execution_model != SpvExecutionModelTaskNV) {
+ execution_model != SpvExecutionModelTaskNV &&
+ execution_model != SpvExecutionModelMeshEXT &&
+ execution_model != SpvExecutionModelTaskEXT) {
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
<< _.VkErrorID(4207) << "Vulkan spec allows BuiltIn "
<< _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
operand)
- << " to be used only with Vertex, MeshNV, or TaskNV execution "
+ << " to be used only with Vertex, MeshNV, TaskNV , MeshEXT or"
+ << " TaskEXT execution "
"model. "
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
referenced_from_inst, execution_model);
@@ -3731,6 +3850,7 @@ spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateAtReference(
case SpvExecutionModelVertex:
case SpvExecutionModelGeometry:
case SpvExecutionModelMeshNV:
+ case SpvExecutionModelMeshEXT:
break;
default: {
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
@@ -3851,6 +3971,7 @@ spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtDefinition(
case SpvBuiltInInstanceId:
case SpvBuiltInRayGeometryIndexKHR:
case SpvBuiltInIncomingRayFlagsKHR:
+ case SpvBuiltInCullMaskKHR:
// i32 scalar
if (spv_result_t error = ValidateI32(
decoration, inst,
@@ -4027,6 +4148,10 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
case SpvBuiltInWorkgroupId: {
return ValidateComputeShaderI32Vec3InputAtDefinition(decoration, inst);
}
+ case SpvBuiltInBaryCoordKHR:
+ case SpvBuiltInBaryCoordNoPerspKHR: {
+ return ValidateFragmentShaderF32Vec3InputAtDefinition(decoration, inst);
+ }
case SpvBuiltInHelperInvocation: {
return ValidateHelperInvocationAtDefinition(decoration, inst);
}
@@ -4151,49 +4276,19 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
case SpvBuiltInObjectToWorldKHR: // alias SpvBuiltInObjectToWorldNV
case SpvBuiltInWorldToObjectKHR: // alias SpvBuiltInWorldToObjectNV
case SpvBuiltInIncomingRayFlagsKHR: // alias SpvBuiltInIncomingRayFlagsNV
- case SpvBuiltInRayGeometryIndexKHR: { // NOT present in NV
+ case SpvBuiltInRayGeometryIndexKHR: // NOT present in NV
+ case SpvBuiltInCullMaskKHR: {
return ValidateRayTracingBuiltinsAtDefinition(decoration, inst);
}
- case SpvBuiltInWorkDim:
- case SpvBuiltInGlobalSize:
- case SpvBuiltInEnqueuedWorkgroupSize:
- case SpvBuiltInGlobalOffset:
- case SpvBuiltInGlobalLinearId:
- case SpvBuiltInSubgroupMaxSize:
- case SpvBuiltInNumEnqueuedSubgroups:
- case SpvBuiltInBaryCoordNoPerspAMD:
- case SpvBuiltInBaryCoordNoPerspCentroidAMD:
- case SpvBuiltInBaryCoordNoPerspSampleAMD:
- case SpvBuiltInBaryCoordSmoothAMD:
- case SpvBuiltInBaryCoordSmoothCentroidAMD:
- case SpvBuiltInBaryCoordSmoothSampleAMD:
- case SpvBuiltInBaryCoordPullModelAMD:
- case SpvBuiltInViewportMaskNV:
- case SpvBuiltInSecondaryPositionNV:
- case SpvBuiltInSecondaryViewportMaskNV:
- case SpvBuiltInPositionPerViewNV:
- case SpvBuiltInViewportMaskPerViewNV:
- case SpvBuiltInMax:
- case SpvBuiltInTaskCountNV:
- case SpvBuiltInPrimitiveCountNV:
- case SpvBuiltInPrimitiveIndicesNV:
- case SpvBuiltInClipDistancePerViewNV:
- case SpvBuiltInCullDistancePerViewNV:
- case SpvBuiltInLayerPerViewNV:
- case SpvBuiltInMeshViewCountNV:
- case SpvBuiltInMeshViewIndicesNV:
- case SpvBuiltInBaryCoordNV:
- case SpvBuiltInBaryCoordNoPerspNV:
- case SpvBuiltInCurrentRayTimeNV:
- // No validation rules (for the moment).
- break;
-
case SpvBuiltInPrimitiveShadingRateKHR: {
return ValidatePrimitiveShadingRateAtDefinition(decoration, inst);
}
case SpvBuiltInShadingRateKHR: {
return ValidateShadingRateAtDefinition(decoration, inst);
}
+ default:
+ // No validation rules (for the moment).
+ break;
}
return SPV_SUCCESS;
}
diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp
index 88abd754..c684a991 100644
--- a/source/val/validate_cfg.cpp
+++ b/source/val/validate_cfg.cpp
@@ -55,8 +55,7 @@ spv_result_t ValidatePhi(ValidationState_t& _, const Instruction* inst) {
}
if (_.IsPointerType(inst->type_id()) &&
_.addressing_model() == SpvAddressingModelLogical) {
- if (!_.features().variable_pointers &&
- !_.features().variable_pointers_storage_buffer) {
+ if (!_.features().variable_pointers) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Using pointers with OpPhi requires capability "
<< "VariablePointers or VariablePointersStorageBuffer";
@@ -67,7 +66,8 @@ spv_result_t ValidatePhi(ValidationState_t& _, const Instruction* inst) {
assert(type_inst);
const SpvOp type_opcode = type_inst->opcode();
- if (!_.options()->before_hlsl_legalization) {
+ if (!_.options()->before_hlsl_legalization &&
+ !_.HasCapability(SpvCapabilityBindlessTextureNV)) {
if (type_opcode == SpvOpTypeSampledImage ||
(_.HasCapability(SpvCapabilityShader) &&
(type_opcode == SpvOpTypeImage || type_opcode == SpvOpTypeSampler))) {
@@ -249,13 +249,9 @@ spv_result_t ValidateReturnValue(ValidationState_t& _,
<< _.getIdName(value->type_id()) << "' is missing or void.";
}
- const bool uses_variable_pointer =
- _.features().variable_pointers ||
- _.features().variable_pointers_storage_buffer;
-
if (_.addressing_model() == SpvAddressingModelLogical &&
- SpvOpTypePointer == value_type->opcode() && !uses_variable_pointer &&
- !_.options()->relax_logical_pointer) {
+ SpvOpTypePointer == value_type->opcode() &&
+ !_.features().variable_pointers && !_.options()->relax_logical_pointer) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpReturnValue value's type <id> '"
<< _.getIdName(value->type_id())
@@ -471,7 +467,7 @@ spv_result_t FindCaseFallThrough(
std::vector<BasicBlock*> stack;
stack.push_back(target_block);
std::unordered_set<const BasicBlock*> visited;
- bool target_reachable = target_block->reachable();
+ bool target_reachable = target_block->structurally_reachable();
int target_depth = function->GetBlockDepth(target_block);
while (!stack.empty()) {
auto block = stack.back();
@@ -481,8 +477,8 @@ spv_result_t FindCaseFallThrough(
if (!visited.insert(block).second) continue;
- if (target_reachable && block->reachable() &&
- target_block->dominates(*block)) {
+ if (target_reachable && block->structurally_reachable() &&
+ target_block->structurally_dominates(*block)) {
// Still in the case construct.
for (auto successor : *block->successors()) {
stack.push_back(successor);
@@ -554,11 +550,12 @@ spv_result_t StructuredSwitchChecks(ValidationState_t& _, Function* function,
if (seen_iter == seen_to_fall_through.end()) {
const auto target_block = function->GetBlock(target).first;
// OpSwitch must dominate all its case constructs.
- if (header->reachable() && target_block->reachable() &&
- !header->dominates(*target_block)) {
+ if (header->structurally_reachable() &&
+ target_block->structurally_reachable() &&
+ !header->structurally_dominates(*target_block)) {
return _.diag(SPV_ERROR_INVALID_CFG, header->label())
<< "Selection header " << _.getIdName(header->id())
- << " does not dominate its case construct "
+ << " does not structurally dominate its case construct "
<< _.getIdName(target);
}
@@ -658,7 +655,7 @@ spv_result_t ValidateStructuredSelections(
}
// Skip unreachable blocks.
- if (!block->reachable()) continue;
+ if (!block->structurally_reachable()) continue;
if (terminator->opcode() == SpvOpBranchConditional) {
const auto true_label = terminator->GetOperandAs<uint32_t>(1);
@@ -713,7 +710,7 @@ spv_result_t StructuredControlFlowChecks(
// Check the loop headers have exactly one back-edge branching to it
for (BasicBlock* loop_header : function->ordered_blocks()) {
- if (!loop_header->reachable()) continue;
+ if (!loop_header->structurally_reachable()) continue;
if (!loop_header->is_type(kBlockTypeLoop)) continue;
auto loop_header_id = loop_header->id();
auto num_latch_blocks = loop_latch_blocks[loop_header_id].size();
@@ -728,9 +725,10 @@ spv_result_t StructuredControlFlowChecks(
// Check construct rules
for (const Construct& construct : function->constructs()) {
auto header = construct.entry_block();
+ if (!header->structurally_reachable()) continue;
auto merge = construct.exit_block();
- if (header->reachable() && !merge) {
+ if (!merge) {
std::string construct_name, header_name, exit_name;
std::tie(construct_name, header_name, exit_name) =
ConstructNames(construct.type());
@@ -740,32 +738,31 @@ spv_result_t StructuredControlFlowChecks(
exit_name + ". This may be a bug in the validator.";
}
- // If the exit block is reachable then it's dominated by the
- // header.
- if (merge && merge->reachable()) {
- if (!header->dominates(*merge)) {
- return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id()))
- << ConstructErrorString(construct, _.getIdName(header->id()),
- _.getIdName(merge->id()),
- "does not dominate");
- }
- // If it's really a merge block for a selection or loop, then it must be
- // *strictly* dominated by the header.
- if (construct.ExitBlockIsMergeBlock() && (header == merge)) {
- return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id()))
- << ConstructErrorString(construct, _.getIdName(header->id()),
- _.getIdName(merge->id()),
- "does not strictly dominate");
- }
+ // If the header is reachable, the merge is guaranteed to be structurally
+ // reachable.
+ if (!header->structurally_dominates(*merge)) {
+ return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id()))
+ << ConstructErrorString(construct, _.getIdName(header->id()),
+ _.getIdName(merge->id()),
+ "does not structurally dominate");
}
+ // If it's really a merge block for a selection or loop, then it must be
+ // *strictly* structrually dominated by the header.
+ if (construct.ExitBlockIsMergeBlock() && (header == merge)) {
+ return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id()))
+ << ConstructErrorString(construct, _.getIdName(header->id()),
+ _.getIdName(merge->id()),
+ "does not strictly structurally dominate");
+ }
+
// Check post-dominance for continue constructs. But dominance and
// post-dominance only make sense when the construct is reachable.
- if (header->reachable() && construct.type() == ConstructType::kContinue) {
- if (!merge->postdominates(*header)) {
+ if (construct.type() == ConstructType::kContinue) {
+ if (!merge->structurally_postdominates(*header)) {
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id()))
<< ConstructErrorString(construct, _.getIdName(header->id()),
_.getIdName(merge->id()),
- "is not post dominated by");
+ "is not structurally post dominated by");
}
}
@@ -776,7 +773,7 @@ spv_result_t StructuredControlFlowChecks(
for (auto block : construct_blocks) {
// Check that all exits from the construct are via structured exits.
for (auto succ : *block->successors()) {
- if (block->reachable() && !construct_blocks.count(succ) &&
+ if (!construct_blocks.count(succ) &&
!construct.IsStructuredExit(_, succ)) {
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
<< "block <ID> " << _.getIdName(block->id()) << " exits the "
@@ -789,7 +786,7 @@ spv_result_t StructuredControlFlowChecks(
// Check that for all non-header blocks, all predecessors are within this
// construct.
for (auto pred : *block->predecessors()) {
- if (pred->reachable() && !construct_blocks.count(pred)) {
+ if (pred->structurally_reachable() && !construct_blocks.count(pred)) {
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(pred->id()))
<< "block <ID> " << pred->id() << " branches to the "
<< construct_name << " construct, but not to the "
@@ -805,7 +802,7 @@ spv_result_t StructuredControlFlowChecks(
merge_inst.opcode() == SpvOpLoopMerge) {
uint32_t merge_id = merge_inst.GetOperandAs<uint32_t>(0);
auto merge_block = function->GetBlock(merge_id).first;
- if (merge_block->reachable() &&
+ if (merge_block->structurally_reachable() &&
!construct_blocks.count(merge_block)) {
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
<< "Header block " << _.getIdName(block->id())
@@ -818,6 +815,43 @@ spv_result_t StructuredControlFlowChecks(
}
}
+ if (construct.type() == ConstructType::kLoop) {
+ // If the continue target differs from the loop header, then check that
+ // all edges into the continue construct come from within the loop.
+ const auto index = header->terminator() - &_.ordered_instructions()[0];
+ const auto& merge_inst = _.ordered_instructions()[index - 1];
+ const auto continue_id = merge_inst.GetOperandAs<uint32_t>(1);
+ const auto* continue_inst = _.FindDef(continue_id);
+ // OpLabel instructions aren't stored as part of the basic block for
+ // legacy reaasons. Grab the next instruction and use it's block pointer
+ // instead.
+ const auto next_index =
+ (continue_inst - &_.ordered_instructions()[0]) + 1;
+ const auto& next_inst = _.ordered_instructions()[next_index];
+ const auto* continue_target = next_inst.block();
+ if (header->id() != continue_id) {
+ for (auto pred : *continue_target->predecessors()) {
+ // Ignore back-edges from within the continue construct.
+ bool is_back_edge = false;
+ for (auto back_edge : back_edges) {
+ uint32_t back_edge_block;
+ uint32_t header_block;
+ std::tie(back_edge_block, header_block) = back_edge;
+ if (header_block == continue_id && back_edge_block == pred->id())
+ is_back_edge = true;
+ }
+ if (!construct_blocks.count(pred) && !is_back_edge) {
+ return _.diag(SPV_ERROR_INVALID_CFG, pred->terminator())
+ << "Block " << _.getIdName(pred->id())
+ << " branches to the loop continue target "
+ << _.getIdName(continue_id)
+ << ", but is not contained in the associated loop construct "
+ << _.getIdName(header->id());
+ }
+ }
+ }
+ }
+
// Checks rules for case constructs.
if (construct.type() == ConstructType::kSelection &&
header->terminator()->opcode() == SpvOpSwitch) {
@@ -855,52 +889,27 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) {
<< _.getIdName(function.id());
}
- // Set each block's immediate dominator and immediate postdominator,
- // and find all back-edges.
+ // Set each block's immediate dominator.
//
// We want to analyze all the blocks in the function, even in degenerate
// control flow cases including unreachable blocks. So use the augmented
// CFG to ensure we cover all the blocks.
std::vector<const BasicBlock*> postorder;
- std::vector<const BasicBlock*> postdom_postorder;
- std::vector<std::pair<uint32_t, uint32_t>> back_edges;
auto ignore_block = [](const BasicBlock*) {};
- auto ignore_edge = [](const BasicBlock*, const BasicBlock*) {};
+ auto no_terminal_blocks = [](const BasicBlock*) { return false; };
if (!function.ordered_blocks().empty()) {
/// calculate dominators
CFA<BasicBlock>::DepthFirstTraversal(
function.first_block(), function.AugmentedCFGSuccessorsFunction(),
ignore_block, [&](const BasicBlock* b) { postorder.push_back(b); },
- ignore_edge);
+ no_terminal_blocks);
auto edges = CFA<BasicBlock>::CalculateDominators(
postorder, function.AugmentedCFGPredecessorsFunction());
for (auto edge : edges) {
if (edge.first != edge.second)
edge.first->SetImmediateDominator(edge.second);
}
-
- /// calculate post dominators
- CFA<BasicBlock>::DepthFirstTraversal(
- function.pseudo_exit_block(),
- function.AugmentedCFGPredecessorsFunction(), ignore_block,
- [&](const BasicBlock* b) { postdom_postorder.push_back(b); },
- ignore_edge);
- auto postdom_edges = CFA<BasicBlock>::CalculateDominators(
- postdom_postorder, function.AugmentedCFGSuccessorsFunction());
- for (auto edge : postdom_edges) {
- edge.first->SetImmediatePostDominator(edge.second);
- }
- /// calculate back edges.
- CFA<BasicBlock>::DepthFirstTraversal(
- function.pseudo_entry_block(),
- function
- .AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge(),
- ignore_block, ignore_block,
- [&](const BasicBlock* from, const BasicBlock* to) {
- back_edges.emplace_back(from->id(), to->id());
- });
}
- UpdateContinueConstructExitBlocks(function, back_edges);
auto& blocks = function.ordered_blocks();
if (!blocks.empty()) {
@@ -934,6 +943,52 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) {
/// Structured control flow checks are only required for shader capabilities
if (_.HasCapability(SpvCapabilityShader)) {
+ // Calculate structural dominance.
+ postorder.clear();
+ std::vector<const BasicBlock*> postdom_postorder;
+ std::vector<std::pair<uint32_t, uint32_t>> back_edges;
+ if (!function.ordered_blocks().empty()) {
+ /// calculate dominators
+ CFA<BasicBlock>::DepthFirstTraversal(
+ function.first_block(),
+ function.AugmentedStructuralCFGSuccessorsFunction(), ignore_block,
+ [&](const BasicBlock* b) { postorder.push_back(b); },
+ no_terminal_blocks);
+ auto edges = CFA<BasicBlock>::CalculateDominators(
+ postorder, function.AugmentedStructuralCFGPredecessorsFunction());
+ for (auto edge : edges) {
+ if (edge.first != edge.second)
+ edge.first->SetImmediateStructuralDominator(edge.second);
+ }
+
+ /// calculate post dominators
+ CFA<BasicBlock>::DepthFirstTraversal(
+ function.pseudo_exit_block(),
+ function.AugmentedStructuralCFGPredecessorsFunction(), ignore_block,
+ [&](const BasicBlock* b) { postdom_postorder.push_back(b); },
+ no_terminal_blocks);
+ auto postdom_edges = CFA<BasicBlock>::CalculateDominators(
+ postdom_postorder,
+ function.AugmentedStructuralCFGSuccessorsFunction());
+ for (auto edge : postdom_edges) {
+ edge.first->SetImmediateStructuralPostDominator(edge.second);
+ }
+ /// calculate back edges.
+ CFA<BasicBlock>::DepthFirstTraversal(
+ function.pseudo_entry_block(),
+ function.AugmentedStructuralCFGSuccessorsFunction(), ignore_block,
+ ignore_block,
+ [&](const BasicBlock* from, const BasicBlock* to) {
+ // A back edge must be a real edge. Since the augmented successors
+ // contain structural edges, filter those from consideration.
+ for (const auto* succ : *(from->successors())) {
+ if (succ == to) back_edges.emplace_back(from->id(), to->id());
+ }
+ },
+ no_terminal_blocks);
+ }
+ UpdateContinueConstructExitBlocks(function, back_edges);
+
if (auto error =
StructuredControlFlowChecks(_, &function, back_edges, postorder))
return error;
@@ -1011,6 +1066,7 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) {
case SpvOpTerminateInvocation:
case SpvOpIgnoreIntersectionKHR:
case SpvOpTerminateRayKHR:
+ case SpvOpEmitMeshTasksEXT:
_.current_function().RegisterBlockEnd(std::vector<uint32_t>());
if (opcode == SpvOpKill) {
_.current_function().RegisterExecutionModelLimitation(
@@ -1025,12 +1081,17 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) {
if (opcode == SpvOpIgnoreIntersectionKHR) {
_.current_function().RegisterExecutionModelLimitation(
SpvExecutionModelAnyHitKHR,
- "OpIgnoreIntersectionKHR requires AnyHit execution model");
+ "OpIgnoreIntersectionKHR requires AnyHitKHR execution model");
}
if (opcode == SpvOpTerminateRayKHR) {
_.current_function().RegisterExecutionModelLimitation(
SpvExecutionModelAnyHitKHR,
- "OpTerminateRayKHR requires AnyHit execution model");
+ "OpTerminateRayKHR requires AnyHitKHR execution model");
+ }
+ if (opcode == SpvOpEmitMeshTasksEXT) {
+ _.current_function().RegisterExecutionModelLimitation(
+ SpvExecutionModelTaskEXT,
+ "OpEmitMeshTasksEXT requires TaskEXT execution model");
}
break;
@@ -1059,6 +1120,26 @@ void ReachabilityPass(ValidationState_t& _) {
}
}
}
+
+ // Repeat for structural reachability.
+ for (auto& f : _.functions()) {
+ std::vector<BasicBlock*> stack;
+ auto entry = f.first_block();
+ // Skip function declarations.
+ if (entry) stack.push_back(entry);
+
+ while (!stack.empty()) {
+ auto block = stack.back();
+ stack.pop_back();
+
+ if (block->structurally_reachable()) continue;
+
+ block->set_structurally_reachable(true);
+ for (auto succ : *block->structural_successors()) {
+ stack.push_back(succ);
+ }
+ }
+ }
}
spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) {
diff --git a/source/val/validate_conversion.cpp b/source/val/validate_conversion.cpp
index b4e39cfe..dc6b1517 100644
--- a/source/val/validate_conversion.cpp
+++ b/source/val/validate_conversion.cpp
@@ -534,6 +534,24 @@ spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) {
break;
}
+ case SpvOpConvertUToAccelerationStructureKHR: {
+ if (!_.IsAccelerationStructureType(result_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Expected Result Type to be a Acceleration Structure: "
+ << spvOpcodeString(opcode);
+ }
+
+ const uint32_t input_type = _.GetOperandTypeId(inst, 2);
+ if (!input_type || !_.IsUnsigned64BitHandle(input_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Expected 64-bit uint scalar or 2-component 32-bit uint "
+ "vector as input: "
+ << spvOpcodeString(opcode);
+ }
+
+ break;
+ }
+
default:
break;
}
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index eb6caf0b..75058501 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -179,8 +179,9 @@ uint32_t align(uint32_t x, uint32_t alignment) {
}
// Returns base alignment of struct member. If |roundUp| is true, also
-// ensure that structs and arrays are aligned at least to a multiple of 16
-// bytes.
+// ensure that structs, arrays, and matrices are aligned at least to a
+// multiple of 16 bytes. (That is, when roundUp is true, this function
+// returns the *extended* alignment as it's called by the Vulkan spec.)
uint32_t getBaseAlignment(uint32_t member_id, bool roundUp,
const LayoutConstraints& inherited,
MemberConstraints& constraints,
@@ -190,6 +191,13 @@ uint32_t getBaseAlignment(uint32_t member_id, bool roundUp,
// Minimal alignment is byte-aligned.
uint32_t baseAlignment = 1;
switch (inst->opcode()) {
+ case SpvOpTypeSampledImage:
+ case SpvOpTypeSampler:
+ case SpvOpTypeImage:
+ if (vstate.HasCapability(SpvCapabilityBindlessTextureNV))
+ return baseAlignment = vstate.samplerimage_variable_address_mode() / 8;
+ assert(0);
+ return 0;
case SpvOpTypeInt:
case SpvOpTypeFloat:
baseAlignment = words[2] / 8;
@@ -219,6 +227,7 @@ uint32_t getBaseAlignment(uint32_t member_id, bool roundUp,
baseAlignment =
componentAlignment * (num_columns == 3 ? 4 : num_columns);
}
+ if (roundUp) baseAlignment = align(baseAlignment, 16u);
} break;
case SpvOpTypeArray:
case SpvOpTypeRuntimeArray:
@@ -256,6 +265,13 @@ uint32_t getScalarAlignment(uint32_t type_id, ValidationState_t& vstate) {
const auto inst = vstate.FindDef(type_id);
const auto& words = inst->words();
switch (inst->opcode()) {
+ case SpvOpTypeSampledImage:
+ case SpvOpTypeSampler:
+ case SpvOpTypeImage:
+ if (vstate.HasCapability(SpvCapabilityBindlessTextureNV))
+ return vstate.samplerimage_variable_address_mode() / 8;
+ assert(0);
+ return 0;
case SpvOpTypeInt:
case SpvOpTypeFloat:
return words[2] / 8;
@@ -296,6 +312,13 @@ uint32_t getSize(uint32_t member_id, const LayoutConstraints& inherited,
const auto inst = vstate.FindDef(member_id);
const auto& words = inst->words();
switch (inst->opcode()) {
+ case SpvOpTypeSampledImage:
+ case SpvOpTypeSampler:
+ case SpvOpTypeImage:
+ if (vstate.HasCapability(SpvCapabilityBindlessTextureNV))
+ return vstate.samplerimage_variable_address_mode() / 8;
+ assert(0);
+ return 0;
case SpvOpTypeInt:
case SpvOpTypeFloat:
return words[2] / 8;
@@ -346,10 +369,13 @@ uint32_t getSize(uint32_t member_id, const LayoutConstraints& inherited,
const auto& lastMember = members.back();
uint32_t offset = 0xffffffff;
// Find the offset of the last element and add the size.
- for (auto& decoration : vstate.id_decorations(member_id)) {
- if (SpvDecorationOffset == decoration.dec_type() &&
- decoration.struct_member_index() == (int)lastIdx) {
- offset = decoration.params()[0];
+ auto member_decorations =
+ vstate.id_member_decorations(member_id, lastIdx);
+ for (auto decoration = member_decorations.begin;
+ decoration != member_decorations.end; ++decoration) {
+ assert(decoration->struct_member_index() == (int)lastIdx);
+ if (SpvDecorationOffset == decoration->dec_type()) {
+ offset = decoration->params()[0];
}
}
// This check depends on the fact that all members have offsets. This
@@ -445,15 +471,17 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
memberIdx < numMembers; memberIdx++) {
uint32_t offset = 0xffffffff;
- for (auto& decoration : vstate.id_decorations(struct_id)) {
- if (decoration.struct_member_index() == (int)memberIdx) {
- switch (decoration.dec_type()) {
- case SpvDecorationOffset:
- offset = decoration.params()[0];
- break;
- default:
- break;
- }
+ auto member_decorations =
+ vstate.id_member_decorations(struct_id, memberIdx);
+ for (auto decoration = member_decorations.begin;
+ decoration != member_decorations.end; ++decoration) {
+ assert(decoration->struct_member_index() == (int)memberIdx);
+ switch (decoration->dec_type()) {
+ case SpvDecorationOffset:
+ offset = decoration->params()[0];
+ break;
+ default:
+ break;
}
}
member_offsets.push_back(
@@ -633,7 +661,8 @@ bool hasDecoration(uint32_t id, SpvDecoration decoration,
}
// Returns true if all ids of given type have a specified decoration.
-bool checkForRequiredDecoration(uint32_t struct_id, SpvDecoration decoration,
+bool checkForRequiredDecoration(uint32_t struct_id,
+ std::function<bool(SpvDecoration)> checker,
SpvOp type, ValidationState_t& vstate) {
const auto& members = getStructMembers(struct_id, vstate);
for (size_t memberIdx = 0; memberIdx < members.size(); memberIdx++) {
@@ -641,10 +670,10 @@ bool checkForRequiredDecoration(uint32_t struct_id, SpvDecoration decoration,
if (type != vstate.FindDef(id)->opcode()) continue;
bool found = false;
for (auto& dec : vstate.id_decorations(id)) {
- if (decoration == dec.dec_type()) found = true;
+ if (checker(dec.dec_type())) found = true;
}
for (auto& dec : vstate.id_decorations(struct_id)) {
- if (decoration == dec.dec_type() &&
+ if (checker(dec.dec_type()) &&
(int)memberIdx == dec.struct_member_index()) {
found = true;
}
@@ -654,7 +683,7 @@ bool checkForRequiredDecoration(uint32_t struct_id, SpvDecoration decoration,
}
}
for (auto id : getStructMembers(struct_id, SpvOpTypeStruct, vstate)) {
- if (!checkForRequiredDecoration(id, decoration, type, vstate)) {
+ if (!checkForRequiredDecoration(id, checker, type, vstate)) {
return false;
}
}
@@ -806,6 +835,56 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
++num_workgroup_variables_with_aliased;
}
}
+
+ if (spvIsVulkanEnv(vstate.context()->target_env)) {
+ const auto* models = vstate.GetExecutionModels(entry_point);
+ const bool has_frag =
+ models->find(SpvExecutionModelFragment) != models->end();
+ const bool has_vert =
+ models->find(SpvExecutionModelVertex) != models->end();
+ for (const auto& decoration :
+ vstate.id_decorations(var_instr->id())) {
+ if (decoration == SpvDecorationFlat ||
+ decoration == SpvDecorationNoPerspective ||
+ decoration == SpvDecorationSample ||
+ decoration == SpvDecorationCentroid) {
+ // VUID 04670 already validates these decorations are input/output
+ if (storage_class == SpvStorageClassInput &&
+ (models->size() > 1 || has_vert)) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
+ << vstate.VkErrorID(6202)
+ << "OpEntryPoint interfaces variable must not be vertex "
+ "execution model with an input storage class for "
+ "Entry Point id "
+ << entry_point << ".";
+ } else if (storage_class == SpvStorageClassOutput &&
+ (models->size() > 1 || has_frag)) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
+ << vstate.VkErrorID(6201)
+ << "OpEntryPoint interfaces variable must not be "
+ "fragment "
+ "execution model with an output storage class for "
+ "Entry Point id "
+ << entry_point << ".";
+ }
+ }
+ }
+
+ const bool has_flat =
+ hasDecoration(var_instr->id(), SpvDecorationFlat, vstate);
+ if (has_frag && storage_class == SpvStorageClassInput && !has_flat &&
+ ((vstate.IsFloatScalarType(type_id) &&
+ vstate.GetBitWidth(type_id) == 64) ||
+ vstate.IsIntScalarOrVectorType(type_id))) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
+ << vstate.VkErrorID(4744)
+ << "Fragment OpEntryPoint operand "
+ << interface << " with Input interfaces with integer or "
+ "float type must have a Flat decoration "
+ "for Entry Point id "
+ << entry_point << ".";
+ }
+ }
}
if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1) {
return vstate.diag(SPV_ERROR_INVALID_BINARY,
@@ -878,21 +957,23 @@ void ComputeMemberConstraintsForStruct(MemberConstraints* constraints,
LayoutConstraints& constraint =
(*constraints)[std::make_pair(struct_id, memberIdx)];
constraint = inherited;
- for (auto& decoration : vstate.id_decorations(struct_id)) {
- if (decoration.struct_member_index() == (int)memberIdx) {
- switch (decoration.dec_type()) {
- case SpvDecorationRowMajor:
- constraint.majorness = kRowMajor;
- break;
- case SpvDecorationColMajor:
- constraint.majorness = kColumnMajor;
- break;
- case SpvDecorationMatrixStride:
- constraint.matrix_stride = decoration.params()[0];
- break;
- default:
- break;
- }
+ auto member_decorations =
+ vstate.id_member_decorations(struct_id, memberIdx);
+ for (auto decoration = member_decorations.begin;
+ decoration != member_decorations.end; ++decoration) {
+ assert(decoration->struct_member_index() == (int)memberIdx);
+ switch (decoration->dec_type()) {
+ case SpvDecorationRowMajor:
+ constraint.majorness = kRowMajor;
+ break;
+ case SpvDecorationColMajor:
+ constraint.majorness = kColumnMajor;
+ break;
+ case SpvDecorationMatrixStride:
+ constraint.matrix_stride = decoration->params()[0];
+ break;
+ default:
+ break;
}
}
@@ -956,41 +1037,41 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
const bool storage_buffer = storageClass == SpvStorageClassStorageBuffer;
if (spvIsVulkanEnv(vstate.context()->target_env)) {
- // Vulkan 14.5.1: There must be no more than one PushConstant block
- // per entry point.
+ // Vulkan: There must be no more than one PushConstant block per entry
+ // point.
if (push_constant) {
auto entry_points = vstate.EntryPointReferences(var_id);
for (auto ep_id : entry_points) {
const bool already_used = !uses_push_constant.insert(ep_id).second;
if (already_used) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
- << "Entry point id '" << ep_id
+ << vstate.VkErrorID(6674) << "Entry point id '" << ep_id
<< "' uses more than one PushConstant interface.\n"
- << "From Vulkan spec, section 14.5.1:\n"
+ << "From Vulkan spec:\n"
<< "There must be no more than one push constant block "
<< "statically used per shader entry point.";
}
}
}
- // Vulkan 14.5.2: Check DescriptorSet and Binding decoration for
+ // Vulkan: Check DescriptorSet and Binding decoration for
// UniformConstant which cannot be a struct.
if (uniform_constant) {
auto entry_points = vstate.EntryPointReferences(var_id);
if (!entry_points.empty() &&
!hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
- << "UniformConstant id '" << var_id
+ << vstate.VkErrorID(6677) << "UniformConstant id '" << var_id
<< "' is missing DescriptorSet decoration.\n"
- << "From Vulkan spec, section 14.5.2:\n"
+ << "From Vulkan spec:\n"
<< "These variables must have DescriptorSet and Binding "
"decorations specified";
}
if (!entry_points.empty() &&
!hasDecoration(var_id, SpvDecorationBinding, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
- << "UniformConstant id '" << var_id
+ << vstate.VkErrorID(6677) << "UniformConstant id '" << var_id
<< "' is missing Binding decoration.\n"
- << "From Vulkan spec, section 14.5.2:\n"
+ << "From Vulkan spec:\n"
<< "These variables must have DescriptorSet and Binding "
"decorations specified";
}
@@ -1017,7 +1098,7 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
}
const bool phys_storage_buffer =
- storageClass == SpvStorageClassPhysicalStorageBufferEXT;
+ storageClass == SpvStorageClassPhysicalStorageBuffer;
const bool workgroup =
storageClass == SpvStorageClassWorkgroup &&
vstate.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayoutKHR);
@@ -1051,55 +1132,55 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
hasDecoration(id, SpvDecorationBufferBlock, vstate);
if (storage_buffer && buffer_block) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
- << "Storage buffer id '" << var_id
+ << vstate.VkErrorID(6675) << "Storage buffer id '" << var_id
<< " In Vulkan, BufferBlock is disallowed on variables in "
"the StorageBuffer storage class";
}
- // Vulkan 14.5.1/2: Check Block decoration for PushConstant, Uniform
+ // Vulkan: Check Block decoration for PushConstant, Uniform
// and StorageBuffer variables. Uniform can also use BufferBlock.
if (push_constant && !block) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
- << "PushConstant id '" << id
+ << vstate.VkErrorID(6675) << "PushConstant id '" << id
<< "' is missing Block decoration.\n"
- << "From Vulkan spec, section 14.5.1:\n"
+ << "From Vulkan spec:\n"
<< "Such variables must be identified with a Block "
"decoration";
}
if (storage_buffer && !block) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
- << "StorageBuffer id '" << id
+ << vstate.VkErrorID(6675) << "StorageBuffer id '" << id
<< "' is missing Block decoration.\n"
- << "From Vulkan spec, section 14.5.2:\n"
+ << "From Vulkan spec:\n"
<< "Such variables must be identified with a Block "
"decoration";
}
if (uniform && !block && !buffer_block) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
- << "Uniform id '" << id
+ << vstate.VkErrorID(6676) << "Uniform id '" << id
<< "' is missing Block or BufferBlock decoration.\n"
- << "From Vulkan spec, section 14.5.2:\n"
+ << "From Vulkan spec:\n"
<< "Such variables must be identified with a Block or "
"BufferBlock decoration";
}
- // Vulkan 14.5.2: Check DescriptorSet and Binding decoration for
+ // Vulkan: Check DescriptorSet and Binding decoration for
// Uniform and StorageBuffer variables.
if (uniform || storage_buffer) {
auto entry_points = vstate.EntryPointReferences(var_id);
if (!entry_points.empty() &&
!hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
- << sc_str << " id '" << var_id
+ << vstate.VkErrorID(6677) << sc_str << " id '" << var_id
<< "' is missing DescriptorSet decoration.\n"
- << "From Vulkan spec, section 14.5.2:\n"
+ << "From Vulkan spec:\n"
<< "These variables must have DescriptorSet and Binding "
"decorations specified";
}
if (!entry_points.empty() &&
!hasDecoration(var_id, SpvDecorationBinding, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
- << sc_str << " id '" << var_id
+ << vstate.VkErrorID(6677) << sc_str << " id '" << var_id
<< "' is missing Binding decoration.\n"
- << "From Vulkan spec, section 14.5.2:\n"
+ << "From Vulkan spec:\n"
<< "These variables must have DescriptorSet and Binding "
"decorations specified";
}
@@ -1144,30 +1225,48 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "Structure id " << id << " decorated as " << deco_str
<< " must not use GLSLPacked decoration.";
- } else if (!checkForRequiredDecoration(id, SpvDecorationArrayStride,
- SpvOpTypeArray, vstate)) {
+ } else if (!checkForRequiredDecoration(
+ id,
+ [](SpvDecoration d) {
+ return d == SpvDecorationArrayStride;
+ },
+ SpvOpTypeArray, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "Structure id " << id << " decorated as " << deco_str
<< " must be explicitly laid out with ArrayStride "
"decorations.";
- } else if (!checkForRequiredDecoration(id,
- SpvDecorationMatrixStride,
- SpvOpTypeMatrix, vstate)) {
+ } else if (!checkForRequiredDecoration(
+ id,
+ [](SpvDecoration d) {
+ return d == SpvDecorationMatrixStride;
+ },
+ SpvOpTypeMatrix, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "Structure id " << id << " decorated as " << deco_str
<< " must be explicitly laid out with MatrixStride "
"decorations.";
+ } else if (!checkForRequiredDecoration(
+ id,
+ [](SpvDecoration d) {
+ return d == SpvDecorationRowMajor ||
+ d == SpvDecorationColMajor;
+ },
+ SpvOpTypeMatrix, vstate)) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
+ << "Structure id " << id << " decorated as " << deco_str
+ << " must be explicitly laid out with RowMajor or "
+ "ColMajor decorations.";
} else if (blockRules &&
- (SPV_SUCCESS != (recursive_status = checkLayout(
- id, sc_str, deco_str, true,
- scalar_block_layout, 0,
- constraints, vstate)))) {
+ (SPV_SUCCESS !=
+ (recursive_status = checkLayout(
+ id, sc_str, deco_str, true, scalar_block_layout, 0,
+ constraints, vstate)))) {
return recursive_status;
} else if (bufferRules &&
- (SPV_SUCCESS != (recursive_status = checkLayout(
- id, sc_str, deco_str, false,
- scalar_block_layout, 0,
- constraints, vstate)))) {
+ (SPV_SUCCESS !=
+ (recursive_status = checkLayout(
+ id, sc_str, deco_str, false, scalar_block_layout,
+ 0, constraints, vstate)))) {
return recursive_status;
}
}
@@ -1197,32 +1296,6 @@ bool AtMostOncePerMember(SpvDecoration decoration) {
}
}
-// Returns the string name for |decoration|.
-const char* GetDecorationName(SpvDecoration decoration) {
- switch (decoration) {
- case SpvDecorationAliased:
- return "Aliased";
- case SpvDecorationRestrict:
- return "Restrict";
- case SpvDecorationArrayStride:
- return "ArrayStride";
- case SpvDecorationOffset:
- return "Offset";
- case SpvDecorationMatrixStride:
- return "MatrixStride";
- case SpvDecorationRowMajor:
- return "RowMajor";
- case SpvDecorationColMajor:
- return "ColMajor";
- case SpvDecorationBlock:
- return "Block";
- case SpvDecorationBufferBlock:
- return "BufferBlock";
- default:
- return "";
- }
-}
-
spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) {
using PerIDKey = std::tuple<SpvDecoration, uint32_t>;
using PerMemberKey = std::tuple<SpvDecoration, uint32_t, uint32_t>;
@@ -1255,7 +1328,7 @@ spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) {
if (already_used && AtMostOncePerId(dec_type)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "ID '" << id << "' decorated with "
- << GetDecorationName(dec_type)
+ << vstate.SpvDecorationString(dec_type)
<< " multiple times is not allowed.";
}
// Verify certain mutually exclusive decorations are not both applied on
@@ -1275,8 +1348,9 @@ spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) {
if (seen_per_id.find(excl_k) != seen_per_id.end()) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "ID '" << id << "' decorated with both "
- << GetDecorationName(dec_type) << " and "
- << GetDecorationName(excl_dec_type) << " is not allowed.";
+ << vstate.SpvDecorationString(dec_type) << " and "
+ << vstate.SpvDecorationString(excl_dec_type)
+ << " is not allowed.";
}
}
} else if (SpvOpMemberDecorate == inst.opcode()) {
@@ -1288,7 +1362,7 @@ spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) {
if (already_used && AtMostOncePerMember(dec_type)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "ID '" << id << "', member '" << member_id
- << "' decorated with " << GetDecorationName(dec_type)
+ << "' decorated with " << vstate.SpvDecorationString(dec_type)
<< " multiple times is not allowed.";
}
// Verify certain mutually exclusive decorations are not both applied on
@@ -1308,8 +1382,9 @@ spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) {
if (seen_per_member.find(excl_k) != seen_per_member.end()) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "ID '" << id << "', member '" << member_id
- << "' decorated with both " << GetDecorationName(dec_type)
- << " and " << GetDecorationName(excl_dec_type)
+ << "' decorated with both "
+ << vstate.SpvDecorationString(dec_type) << " and "
+ << vstate.SpvDecorationString(excl_dec_type)
<< " is not allowed.";
}
}
@@ -1407,11 +1482,11 @@ spv_result_t CheckFPRoundingModeForShaders(ValidationState_t& vstate,
storage != SpvStorageClassUniform &&
storage != SpvStorageClassPushConstant &&
storage != SpvStorageClassInput && storage != SpvStorageClassOutput &&
- storage != SpvStorageClassPhysicalStorageBufferEXT) {
+ storage != SpvStorageClassPhysicalStorageBuffer) {
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
<< "FPRoundingMode decoration can be applied only to the "
"Object operand of an OpStore in the StorageBuffer, "
- "PhysicalStorageBufferEXT, Uniform, PushConstant, Input, or "
+ "PhysicalStorageBuffer, Uniform, PushConstant, Input, or "
"Output Storage Classes.";
}
}
@@ -1654,6 +1729,24 @@ spv_result_t CheckLocationDecoration(ValidationState_t& vstate,
"of a structure type";
}
+spv_result_t CheckRelaxPrecisionDecoration(ValidationState_t& vstate,
+ const Instruction& inst,
+ const Decoration& decoration) {
+ // This is not the most precise check, but the rules for RelaxPrecision are
+ // very general, and it will be difficult to implement precisely. For now,
+ // I will only check for the cases that cause problems for the optimizer.
+ if (!spvOpcodeGeneratesType(inst.opcode())) {
+ return SPV_SUCCESS;
+ }
+
+ if (decoration.struct_member_index() != Decoration::kInvalidMember &&
+ inst.opcode() == SpvOpTypeStruct) {
+ return SPV_SUCCESS;
+ }
+ return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+ << "RelaxPrecision decoration cannot be applied to a type";
+}
+
#define PASS_OR_BAIL_AT_LINE(X, LINE) \
{ \
spv_result_t e##LINE = (X); \
@@ -1708,6 +1801,10 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) {
case SpvDecorationLocation:
PASS_OR_BAIL(CheckLocationDecoration(vstate, *inst, decoration));
break;
+ case SpvDecorationRelaxedPrecision:
+ PASS_OR_BAIL(
+ CheckRelaxPrecisionDecoration(vstate, *inst, decoration));
+ break;
default:
break;
}
diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp
index 01cbcd25..1e69cb37 100644
--- a/source/val/validate_extensions.cpp
+++ b/source/val/validate_extensions.cpp
@@ -147,6 +147,24 @@ bool DoesDebugInfoOperandMatchExpectation(
return true;
}
+// Overload for NonSemanticShaderDebugInfo100Instructions.
+bool DoesDebugInfoOperandMatchExpectation(
+ const ValidationState_t& _,
+ const std::function<bool(NonSemanticShaderDebugInfo100Instructions)>&
+ expectation,
+ const Instruction* inst, uint32_t word_index) {
+ if (inst->words().size() <= word_index) return false;
+ auto* debug_inst = _.FindDef(inst->word(word_index));
+ if (debug_inst->opcode() != SpvOpExtInst ||
+ (debug_inst->ext_inst_type() !=
+ SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) ||
+ !expectation(
+ NonSemanticShaderDebugInfo100Instructions(debug_inst->word(4)))) {
+ return false;
+ }
+ return true;
+}
+
// Check that the operand of a debug info instruction |inst| at |word_index|
// is a result id of an debug info instruction whose debug instruction type
// is |expected_debug_inst|.
@@ -223,6 +241,18 @@ spv_result_t ValidateOperandDebugType(
const Instruction* inst, uint32_t word_index,
const std::function<std::string()>& ext_inst_name,
bool allow_template_param) {
+ // Check for NonSemanticShaderDebugInfo100 specific types.
+ if (inst->ext_inst_type() ==
+ SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
+ std::function<bool(NonSemanticShaderDebugInfo100Instructions)> expectation =
+ [](NonSemanticShaderDebugInfo100Instructions dbg_inst) {
+ return dbg_inst == NonSemanticShaderDebugInfo100DebugTypeMatrix;
+ };
+ if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
+ return SPV_SUCCESS;
+ }
+
+ // Check for common types.
std::function<bool(CommonDebugInfoInstructions)> expectation =
[&allow_template_param](CommonDebugInfoInstructions dbg_inst) {
if (allow_template_param &&
@@ -726,10 +756,10 @@ spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
if (_.version() < SPV_SPIRV_VERSION_WORD(1, 4)) {
std::string extension = GetExtensionString(&(inst->c_inst()));
if (extension ==
- ExtensionToString(kSPV_KHR_workgroup_memory_explicit_layout)) {
+ ExtensionToString(kSPV_KHR_workgroup_memory_explicit_layout) ||
+ extension == ExtensionToString(kSPV_EXT_mesh_shader)) {
return _.diag(SPV_ERROR_WRONG_VERSION, inst)
- << "SPV_KHR_workgroup_memory_explicit_layout extension "
- "requires SPIR-V version 1.4 or later.";
+ << extension << " extension requires SPIR-V version 1.4 or later.";
}
}
@@ -2719,6 +2749,86 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
auto num_words = inst->words().size();
+ // Handle any non-common NonSemanticShaderDebugInfo instructions.
+ if (vulkanDebugInfo) {
+ const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
+ NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
+ switch (ext_inst_key) {
+ // The following block of instructions will be handled by the common
+ // validation.
+ case NonSemanticShaderDebugInfo100DebugInfoNone:
+ case NonSemanticShaderDebugInfo100DebugCompilationUnit:
+ case NonSemanticShaderDebugInfo100DebugTypeBasic:
+ case NonSemanticShaderDebugInfo100DebugTypePointer:
+ case NonSemanticShaderDebugInfo100DebugTypeQualifier:
+ case NonSemanticShaderDebugInfo100DebugTypeArray:
+ case NonSemanticShaderDebugInfo100DebugTypeVector:
+ case NonSemanticShaderDebugInfo100DebugTypedef:
+ case NonSemanticShaderDebugInfo100DebugTypeFunction:
+ case NonSemanticShaderDebugInfo100DebugTypeEnum:
+ case NonSemanticShaderDebugInfo100DebugTypeComposite:
+ case NonSemanticShaderDebugInfo100DebugTypeMember:
+ case NonSemanticShaderDebugInfo100DebugTypeInheritance:
+ case NonSemanticShaderDebugInfo100DebugTypePtrToMember:
+ case NonSemanticShaderDebugInfo100DebugTypeTemplate:
+ case NonSemanticShaderDebugInfo100DebugTypeTemplateParameter:
+ case NonSemanticShaderDebugInfo100DebugTypeTemplateTemplateParameter:
+ case NonSemanticShaderDebugInfo100DebugTypeTemplateParameterPack:
+ case NonSemanticShaderDebugInfo100DebugGlobalVariable:
+ case NonSemanticShaderDebugInfo100DebugFunctionDeclaration:
+ case NonSemanticShaderDebugInfo100DebugFunction:
+ case NonSemanticShaderDebugInfo100DebugLexicalBlock:
+ case NonSemanticShaderDebugInfo100DebugLexicalBlockDiscriminator:
+ case NonSemanticShaderDebugInfo100DebugScope:
+ case NonSemanticShaderDebugInfo100DebugNoScope:
+ case NonSemanticShaderDebugInfo100DebugInlinedAt:
+ case NonSemanticShaderDebugInfo100DebugLocalVariable:
+ case NonSemanticShaderDebugInfo100DebugInlinedVariable:
+ case NonSemanticShaderDebugInfo100DebugDeclare:
+ case NonSemanticShaderDebugInfo100DebugValue:
+ case NonSemanticShaderDebugInfo100DebugOperation:
+ case NonSemanticShaderDebugInfo100DebugExpression:
+ case NonSemanticShaderDebugInfo100DebugMacroDef:
+ case NonSemanticShaderDebugInfo100DebugMacroUndef:
+ case NonSemanticShaderDebugInfo100DebugImportedEntity:
+ case NonSemanticShaderDebugInfo100DebugSource:
+ break;
+ case NonSemanticShaderDebugInfo100DebugTypeMatrix: {
+ CHECK_DEBUG_OPERAND("Vector Type", CommonDebugInfoDebugTypeVector, 5);
+
+ CHECK_CONST_UINT_OPERAND("Vector Count", 6);
+
+ uint32_t vector_count = inst->word(6);
+ uint64_t const_val;
+ if (!_.GetConstantValUint64(vector_count, &const_val)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name()
+ << ": Vector Count must be 32-bit integer OpConstant";
+ }
+
+ vector_count = const_val & 0xffffffff;
+ if (!vector_count || vector_count > 4) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": Vector Count must be positive "
+ << "integer less than or equal to 4";
+ }
+ break;
+ }
+ // TODO: Add validation rules for remaining cases as well.
+ case NonSemanticShaderDebugInfo100DebugFunctionDefinition:
+ case NonSemanticShaderDebugInfo100DebugSourceContinued:
+ case NonSemanticShaderDebugInfo100DebugLine:
+ case NonSemanticShaderDebugInfo100DebugNoLine:
+ case NonSemanticShaderDebugInfo100DebugBuildIdentifier:
+ case NonSemanticShaderDebugInfo100DebugStoragePath:
+ case NonSemanticShaderDebugInfo100DebugEntryPoint:
+ break;
+ case NonSemanticShaderDebugInfo100InstructionsMax:
+ assert(0);
+ break;
+ }
+ }
+
// Handle any non-common OpenCL insts, then common
if (ext_inst_type != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
OpenCLDebugInfo100Instructions(ext_inst_index) !=
@@ -2804,8 +2914,9 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
bool invalid = false;
auto* component_count = _.FindDef(inst->word(i));
if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) {
- // TODO: We need a spec discussion for the bindless array.
- if (!component_count->word(3)) {
+ // TODO: We need a spec discussion for the runtime array for
+ // OpenCL.
+ if (!vulkanDebugInfo && !component_count->word(3)) {
invalid = true;
}
} else if (component_count->words().size() > 6 &&
diff --git a/source/val/validate_function.cpp b/source/val/validate_function.cpp
index 596186bb..2a5fed8e 100644
--- a/source/val/validate_function.cpp
+++ b/source/val/validate_function.cpp
@@ -147,8 +147,8 @@ spv_result_t ValidateFunctionParameter(ValidationState_t& _,
"type of the same index.";
}
- // Validate that PhysicalStorageBufferEXT have one of Restrict, Aliased,
- // RestrictPointerEXT, or AliasedPointerEXT.
+ // Validate that PhysicalStorageBuffer have one of Restrict, Aliased,
+ // RestrictPointer, or AliasedPointer.
auto param_nonarray_type_id = param_type->id();
while (_.GetIdOpcode(param_nonarray_type_id) == SpvOpTypeArray) {
param_nonarray_type_id =
@@ -157,7 +157,7 @@ spv_result_t ValidateFunctionParameter(ValidationState_t& _,
if (_.GetIdOpcode(param_nonarray_type_id) == SpvOpTypePointer) {
auto param_nonarray_type = _.FindDef(param_nonarray_type_id);
if (param_nonarray_type->GetOperandAs<uint32_t>(1u) ==
- SpvStorageClassPhysicalStorageBufferEXT) {
+ SpvStorageClassPhysicalStorageBuffer) {
// check for Aliased or Restrict
const auto& decorations = _.id_decorations(inst->id());
@@ -174,14 +174,14 @@ spv_result_t ValidateFunctionParameter(ValidationState_t& _,
if (!foundAliased && !foundRestrict) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpFunctionParameter " << inst->id()
- << ": expected Aliased or Restrict for PhysicalStorageBufferEXT "
+ << ": expected Aliased or Restrict for PhysicalStorageBuffer "
"pointer.";
}
if (foundAliased && foundRestrict) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpFunctionParameter " << inst->id()
<< ": can't specify both Aliased and Restrict for "
- "PhysicalStorageBufferEXT pointer.";
+ "PhysicalStorageBuffer pointer.";
}
} else {
const auto pointee_type_id =
@@ -189,31 +189,31 @@ spv_result_t ValidateFunctionParameter(ValidationState_t& _,
const auto pointee_type = _.FindDef(pointee_type_id);
if (SpvOpTypePointer == pointee_type->opcode() &&
pointee_type->GetOperandAs<uint32_t>(1u) ==
- SpvStorageClassPhysicalStorageBufferEXT) {
- // check for AliasedPointerEXT/RestrictPointerEXT
+ SpvStorageClassPhysicalStorageBuffer) {
+ // check for AliasedPointer/RestrictPointer
const auto& decorations = _.id_decorations(inst->id());
bool foundAliased = std::any_of(
decorations.begin(), decorations.end(), [](const Decoration& d) {
- return SpvDecorationAliasedPointerEXT == d.dec_type();
+ return SpvDecorationAliasedPointer == d.dec_type();
});
bool foundRestrict = std::any_of(
decorations.begin(), decorations.end(), [](const Decoration& d) {
- return SpvDecorationRestrictPointerEXT == d.dec_type();
+ return SpvDecorationRestrictPointer == d.dec_type();
});
if (!foundAliased && !foundRestrict) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpFunctionParameter " << inst->id()
- << ": expected AliasedPointerEXT or RestrictPointerEXT for "
- "PhysicalStorageBufferEXT pointer.";
+ << ": expected AliasedPointer or RestrictPointer for "
+ "PhysicalStorageBuffer pointer.";
}
if (foundAliased && foundRestrict) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpFunctionParameter " << inst->id()
- << ": can't specify both AliasedPointerEXT and "
- "RestrictPointerEXT for PhysicalStorageBufferEXT pointer.";
+ << ": can't specify both AliasedPointer and "
+ "RestrictPointer for PhysicalStorageBuffer pointer.";
}
}
}
@@ -300,7 +300,7 @@ spv_result_t ValidateFunctionCall(ValidationState_t& _,
// These are always allowed.
break;
case SpvStorageClassStorageBuffer:
- if (!_.features().variable_pointers_storage_buffer) {
+ if (!_.features().variable_pointers) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "StorageBuffer pointer operand "
<< _.getIdName(argument_id)
@@ -316,11 +316,10 @@ spv_result_t ValidateFunctionCall(ValidationState_t& _,
// Validate memory object declaration requirements.
if (argument->opcode() != SpvOpVariable &&
argument->opcode() != SpvOpFunctionParameter) {
- const bool ssbo_vptr =
- _.features().variable_pointers_storage_buffer &&
- sc == SpvStorageClassStorageBuffer;
- const bool wg_vptr =
- _.features().variable_pointers && sc == SpvStorageClassWorkgroup;
+ const bool ssbo_vptr = _.features().variable_pointers &&
+ sc == SpvStorageClassStorageBuffer;
+ const bool wg_vptr = _.HasCapability(SpvCapabilityVariablePointers) &&
+ sc == SpvStorageClassWorkgroup;
const bool uc_ptr = sc == SpvStorageClassUniformConstant;
if (!ssbo_vptr && !wg_vptr && !uc_ptr) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp
index b12d1e82..1d9c877e 100644
--- a/source/val/validate_image.cpp
+++ b/source/val/validate_image.cpp
@@ -640,65 +640,64 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
return SPV_SUCCESS;
}
-// Checks some of the validation rules which are common to multiple opcodes.
-spv_result_t ValidateImageCommon(ValidationState_t& _, const Instruction* inst,
- const ImageTypeInfo& info) {
- const SpvOp opcode = inst->opcode();
- if (IsProj(opcode)) {
- if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D &&
- info.dim != SpvDimRect) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Image 'Dim' parameter to be 1D, 2D, 3D or Rect";
- }
+// Validate OpImage*Proj* instructions
+spv_result_t ValidateImageProj(ValidationState_t& _, const Instruction* inst,
+ const ImageTypeInfo& info) {
+ if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D &&
+ info.dim != SpvDimRect) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Expected Image 'Dim' parameter to be 1D, 2D, 3D or Rect";
+ }
- if (info.multisampled != 0) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Image 'MS' parameter to be 0";
- }
+ if (info.multisampled != 0) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Expected Image 'MS' parameter to be 0";
+ }
- if (info.arrayed != 0) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Image 'arrayed' parameter to be 0";
- }
+ if (info.arrayed != 0) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Expected Image 'arrayed' parameter to be 0";
}
- if (opcode == SpvOpImageRead || opcode == SpvOpImageSparseRead ||
- opcode == SpvOpImageWrite) {
- if (info.sampled == 0) {
- } else if (info.sampled == 2) {
- if (info.dim == SpvDim1D && !_.HasCapability(SpvCapabilityImage1D)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Capability Image1D is required to access storage image";
- } else if (info.dim == SpvDimRect &&
- !_.HasCapability(SpvCapabilityImageRect)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Capability ImageRect is required to access storage image";
- } else if (info.dim == SpvDimBuffer &&
- !_.HasCapability(SpvCapabilityImageBuffer)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Capability ImageBuffer is required to access storage image";
- } else if (info.dim == SpvDimCube && info.arrayed == 1 &&
- !_.HasCapability(SpvCapabilityImageCubeArray)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Capability ImageCubeArray is required to access "
- << "storage image";
- }
+ return SPV_SUCCESS;
+}
+
+// Validate OpImage*Read and OpImage*Write instructions
+spv_result_t ValidateImageReadWrite(ValidationState_t& _,
+ const Instruction* inst,
+ const ImageTypeInfo& info) {
+ if (info.sampled == 2) {
+ if (info.dim == SpvDim1D && !_.HasCapability(SpvCapabilityImage1D)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Capability Image1D is required to access storage image";
+ } else if (info.dim == SpvDimRect &&
+ !_.HasCapability(SpvCapabilityImageRect)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Capability ImageRect is required to access storage image";
+ } else if (info.dim == SpvDimBuffer &&
+ !_.HasCapability(SpvCapabilityImageBuffer)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Capability ImageBuffer is required to access storage image";
+ } else if (info.dim == SpvDimCube && info.arrayed == 1 &&
+ !_.HasCapability(SpvCapabilityImageCubeArray)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Capability ImageCubeArray is required to access "
+ << "storage image";
+ }
- if (info.multisampled == 1 &&
- !_.HasCapability(SpvCapabilityImageMSArray)) {
+ if (info.multisampled == 1 && !_.HasCapability(SpvCapabilityImageMSArray)) {
#if 0
- // TODO(atgoo@github.com) The description of this rule in the spec
- // is unclear and Glslang doesn't declare ImageMSArray. Need to clarify
- // and reenable.
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Capability ImageMSArray is required to access storage "
- << "image";
-#endif
- }
- } else {
+ // TODO(atgoo@github.com) The description of this rule in the spec
+ // is unclear and Glslang doesn't declare ImageMSArray. Need to clarify
+ // and reenable.
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Image 'Sampled' parameter to be 0 or 2";
+ << "Capability ImageMSArray is required to access storage "
+ << "image";
+#endif
}
+ } else if (info.sampled != 0) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Expected Image 'Sampled' parameter to be 0 or 2";
}
return SPV_SUCCESS;
@@ -813,7 +812,8 @@ spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) {
}
}
- // Dim is checked elsewhere.
+ // Universal checks on image type operands
+ // Dim and Format and Access Qualifier are checked elsewhere.
if (info.depth > 2) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -825,62 +825,53 @@ spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) {
<< "Invalid Arrayed " << info.arrayed << " (must be 0 or 1)";
}
- if (spvIsOpenCLEnv(target_env)) {
- if ((info.arrayed == 1) && (info.dim != SpvDim1D) &&
- (info.dim != SpvDim2D)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "In the OpenCL environment, Arrayed may only be set to 1 "
- << "when Dim is either 1D or 2D.";
- }
- }
-
if (info.multisampled > 1) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Invalid MS " << info.multisampled << " (must be 0 or 1)";
}
- if (spvIsOpenCLEnv(target_env)) {
- if (info.multisampled != 0) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "MS must be 0 in the OpenCL environment.";
- }
- }
-
if (info.sampled > 2) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Invalid Sampled " << info.sampled << " (must be 0, 1 or 2)";
}
- if (spvIsVulkanEnv(target_env)) {
- if (info.sampled == 0) {
+ if (info.dim == SpvDimSubpassData) {
+ if (info.sampled != 2) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << _.VkErrorID(4657)
- << "Sampled must be 1 or 2 in the Vulkan environment.";
+ << _.VkErrorID(6214) << "Dim SubpassData requires Sampled to be 2";
}
- }
- if (spvIsOpenCLEnv(_.context()->target_env)) {
- if (info.sampled != 0) {
+ if (info.format != SpvImageFormatUnknown) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Sampled must be 0 in the OpenCL environment.";
+ << "Dim SubpassData requires format Unknown";
+ }
+ } else {
+ if (info.multisampled && (info.sampled == 2) &&
+ !_.HasCapability(SpvCapabilityStorageImageMultisample)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Capability StorageImageMultisample is required when using "
+ "multisampled storage image";
}
}
- if (info.dim == SpvDimSubpassData) {
- if (info.sampled != 2) {
+ if (spvIsOpenCLEnv(target_env)) {
+ if ((info.arrayed == 1) && (info.dim != SpvDim1D) &&
+ (info.dim != SpvDim2D)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Dim SubpassData requires Sampled to be 2";
+ << "In the OpenCL environment, Arrayed may only be set to 1 "
+ << "when Dim is either 1D or 2D.";
}
- if (info.format != SpvImageFormatUnknown) {
+ if (info.multisampled != 0) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Dim SubpassData requires format Unknown";
+ << "MS must be 0 in the OpenCL environment.";
}
- }
- // Format and Access Qualifier are also checked elsewhere.
+ if (info.sampled != 0) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Sampled must be 0 in the OpenCL environment.";
+ }
- if (spvIsOpenCLEnv(_.context()->target_env)) {
if (info.access_qualifier == SpvAccessQualifierMax) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "In the OpenCL environment, the optional Access Qualifier"
@@ -888,12 +879,16 @@ spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) {
}
}
- if (info.multisampled && (info.sampled == 2) &&
- (info.dim != SpvDimSubpassData)) {
- if (!_.HasCapability(SpvCapabilityStorageImageMultisample)) {
+ if (spvIsVulkanEnv(target_env)) {
+ if (info.sampled == 0) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Capability StorageImageMultisample is required when using "
- "multisampled storage image";
+ << _.VkErrorID(4657)
+ << "Sampled must be 1 or 2 in the Vulkan environment.";
+ }
+
+ if (info.dim == SpvDimSubpassData && info.arrayed != 0) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << _.VkErrorID(6214) << "Dim SubpassData requires Arrayed to be 0";
}
}
@@ -932,7 +927,7 @@ spv_result_t ValidateTypeSampledImage(ValidationState_t& _,
return SPV_SUCCESS;
}
-bool IsAllowedSampledImageOperand(SpvOp opcode) {
+bool IsAllowedSampledImageOperand(SpvOp opcode, ValidationState_t& _) {
switch (opcode) {
case SpvOpSampledImage:
case SpvOpImageSampleImplicitLod:
@@ -955,6 +950,9 @@ bool IsAllowedSampledImageOperand(SpvOp opcode) {
case SpvOpImageSparseDrefGather:
case SpvOpCopyObject:
return true;
+ case SpvOpStore:
+ if (_.HasCapability(SpvCapabilityBindlessTextureNV)) return true;
+ return false;
default:
return false;
}
@@ -985,8 +983,9 @@ spv_result_t ValidateSampledImage(ValidationState_t& _,
if (spvIsVulkanEnv(_.context()->target_env)) {
if (info.sampled != 1) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Image 'Sampled' parameter to be 1 "
- << "for Vulkan environment.";
+ << _.VkErrorID(6671)
+ << "Expected Image 'Sampled' parameter to be 1 for Vulkan "
+ "environment.";
}
} else {
if (info.sampled != 0 && info.sampled != 1) {
@@ -1039,7 +1038,7 @@ spv_result_t ValidateSampledImage(ValidationState_t& _,
<< _.getIdName(consumer_instr->id()) << "'.";
}
- if (!IsAllowedSampledImageOperand(consumer_opcode)) {
+ if (!IsAllowedSampledImageOperand(consumer_opcode, _)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Result <id> from OpSampledImage instruction must not appear "
"as operand for Op"
@@ -1205,7 +1204,9 @@ spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) {
<< "Corrupt image type definition";
}
- if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result;
+ if (IsProj(opcode)) {
+ if (spv_result_t result = ValidateImageProj(_, inst, info)) return result;
+ }
if (info.multisampled) {
// When using image operands, the Sample image operand is required if and
@@ -1268,6 +1269,27 @@ spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) {
return SPV_SUCCESS;
}
+// Validates anything OpImage*Dref* instruction
+spv_result_t ValidateImageDref(ValidationState_t& _, const Instruction* inst,
+ const ImageTypeInfo& info) {
+ const uint32_t dref_type = _.GetOperandTypeId(inst, 4);
+ if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Expected Dref to be of 32-bit float type";
+ }
+
+ if (spvIsVulkanEnv(_.context()->target_env)) {
+ if (info.dim == SpvDim3D) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << _.VkErrorID(4777)
+ << "In Vulkan, OpImage*Dref* instructions must not use images "
+ "with a 3D Dim";
+ }
+ }
+
+ return SPV_SUCCESS;
+}
+
spv_result_t ValidateImageDrefLod(ValidationState_t& _,
const Instruction* inst) {
const SpvOp opcode = inst->opcode();
@@ -1295,7 +1317,9 @@ spv_result_t ValidateImageDrefLod(ValidationState_t& _,
<< "Corrupt image type definition";
}
- if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result;
+ if (IsProj(opcode)) {
+ if (spv_result_t result = ValidateImageProj(_, inst, info)) return result;
+ }
if (info.multisampled) {
// When using image operands, the Sample image operand is required if and
@@ -1325,11 +1349,7 @@ spv_result_t ValidateImageDrefLod(ValidationState_t& _,
<< " components, but given only " << actual_coord_size;
}
- const uint32_t dref_type = _.GetOperandTypeId(inst, 4);
- if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Dref to be of 32-bit float type";
- }
+ if (spv_result_t result = ValidateImageDref(_, inst, info)) return result;
if (spv_result_t result =
ValidateImageOperands(_, inst, info, /* word_index = */ 7))
@@ -1464,7 +1484,8 @@ spv_result_t ValidateImageGather(ValidationState_t& _,
if (info.dim != SpvDim2D && info.dim != SpvDimCube &&
info.dim != SpvDimRect) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Image 'Dim' cannot be Cube";
+ << _.VkErrorID(4777)
+ << "Expected Image 'Dim' to be 2D, Cube, or Rect";
}
const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
@@ -1500,11 +1521,7 @@ spv_result_t ValidateImageGather(ValidationState_t& _,
} else {
assert(opcode == SpvOpImageDrefGather ||
opcode == SpvOpImageSparseDrefGather);
- const uint32_t dref_type = _.GetOperandTypeId(inst, 4);
- if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Dref to be of 32-bit float type";
- }
+ if (spv_result_t result = ValidateImageDref(_, inst, info)) return result;
}
if (spv_result_t result =
@@ -1572,6 +1589,13 @@ spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) {
<< " to have 4 components";
}
}
+
+ const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
+ if (mask & SpvImageOperandsConstOffsetMask) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "ConstOffset image operand not allowed "
+ << "in the OpenCL environment.";
+ }
}
if (info.dim == SpvDimSubpassData) {
@@ -1597,7 +1621,8 @@ spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) {
}
}
- if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result;
+ if (spv_result_t result = ValidateImageReadWrite(_, inst, info))
+ return result;
const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
if (!_.IsIntScalarOrVectorType(coord_type)) {
@@ -1622,16 +1647,6 @@ spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) {
}
}
- const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
-
- if (mask & SpvImageOperandsConstOffsetMask) {
- if (spvIsOpenCLEnv(_.context()->target_env)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "ConstOffset image operand not allowed "
- << "in the OpenCL environment.";
- }
- }
-
if (spv_result_t result =
ValidateImageOperands(_, inst, info, /* word_index = */ 6))
return result;
@@ -1657,7 +1672,8 @@ spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) {
<< "Image 'Dim' cannot be SubpassData";
}
- if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result;
+ if (spv_result_t result = ValidateImageReadWrite(_, inst, info))
+ return result;
const uint32_t coord_type = _.GetOperandTypeId(inst, 1);
if (!_.IsIntScalarOrVectorType(coord_type)) {
@@ -1673,8 +1689,7 @@ spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) {
<< " components, but given only " << actual_coord_size;
}
- // TODO(atgoo@github.com) The spec doesn't explicitly say what the type
- // of texel should be.
+ // because it needs to match with 'Sampled Type' the Texel can't be a boolean
const uint32_t texel_type = _.GetOperandTypeId(inst, 2);
if (!_.IsIntScalarOrVectorType(texel_type) &&
!_.IsFloatScalarOrVectorType(texel_type)) {
@@ -1682,14 +1697,6 @@ spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) {
<< "Expected Texel to be int or float vector or scalar";
}
-#if 0
- // TODO: See above.
- if (_.GetDimension(texel_type) != 4) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Texel to have 4 components";
- }
-#endif
-
if (_.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) {
const uint32_t texel_component_type = _.GetComponentType(texel_type);
if (texel_component_type != info.sampled_type) {
diff --git a/source/val/validate_instruction.cpp b/source/val/validate_instruction.cpp
index 3edf1637..767c0cee 100644
--- a/source/val/validate_instruction.cpp
+++ b/source/val/validate_instruction.cpp
@@ -483,6 +483,22 @@ spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) {
if (auto error = LimitCheckNumVars(_, inst->id(), storage_class)) {
return error;
}
+ } else if (opcode == SpvOpSamplerImageAddressingModeNV) {
+ if (!_.HasCapability(SpvCapabilityBindlessTextureNV)) {
+ return _.diag(SPV_ERROR_MISSING_EXTENSION, inst)
+ << "OpSamplerImageAddressingModeNV supported only with extension "
+ "SPV_NV_bindless_texture";
+ }
+ uint32_t bitwidth = inst->GetOperandAs<uint32_t>(0);
+ if (_.samplerimage_variable_address_mode() != 0) {
+ return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+ << "OpSamplerImageAddressingModeNV should only be provided once";
+ }
+ if (bitwidth != 32 && bitwidth != 64) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "OpSamplerImageAddressingModeNV bitwidth should be 64 or 32";
+ }
+ _.set_samplerimage_variable_address_mode(bitwidth);
}
if (auto error = ReservedCheck(_, inst)) return error;
diff --git a/source/val/validate_interfaces.cpp b/source/val/validate_interfaces.cpp
index adf2e472..7f2d6488 100644
--- a/source/val/validate_interfaces.cpp
+++ b/source/val/validate_interfaces.cpp
@@ -238,7 +238,7 @@ spv_result_t GetLocationsForVariable(
uint32_t index = 0;
bool has_patch = false;
bool has_per_task_nv = false;
- bool has_per_vertex_nv = false;
+ bool has_per_vertex_khr = false;
for (auto& dec : _.id_decorations(variable->id())) {
if (dec.dec_type() == SpvDecorationLocation) {
if (has_location && dec.params()[0] != location) {
@@ -272,8 +272,20 @@ spv_result_t GetLocationsForVariable(
has_patch = true;
} else if (dec.dec_type() == SpvDecorationPerTaskNV) {
has_per_task_nv = true;
- } else if (dec.dec_type() == SpvDecorationPerVertexNV) {
- has_per_vertex_nv = true;
+ } else if (dec.dec_type() == SpvDecorationPerVertexKHR) {
+ if (!is_fragment) {
+ return _.diag(SPV_ERROR_INVALID_DATA, variable)
+ << _.VkErrorID(6777)
+ << "PerVertexKHR can only be applied to Fragment Execution "
+ "Models";
+ }
+ if (type->opcode() != SpvOpTypeArray &&
+ type->opcode() != SpvOpTypeRuntimeArray) {
+ return _.diag(SPV_ERROR_INVALID_DATA, variable)
+ << _.VkErrorID(6778)
+ << "PerVertexKHR must be declared as arrays";
+ }
+ has_per_vertex_khr = true;
}
}
@@ -298,7 +310,7 @@ spv_result_t GetLocationsForVariable(
}
break;
case SpvExecutionModelFragment:
- if (!is_output && has_per_vertex_nv) {
+ if (!is_output && has_per_vertex_khr) {
is_arrayed = true;
}
break;
diff --git a/source/val/validate_layout.cpp b/source/val/validate_layout.cpp
index d5823219..6f951352 100644
--- a/source/val/validate_layout.cpp
+++ b/source/val/validate_layout.cpp
@@ -363,6 +363,7 @@ spv_result_t ModuleLayoutPass(ValidationState_t& _, const Instruction* inst) {
case kLayoutExtensions:
case kLayoutExtInstImport:
case kLayoutMemoryModel:
+ case kLayoutSamplerImageAddressMode:
case kLayoutEntryPoint:
case kLayoutExecutionMode:
case kLayoutDebug1:
diff --git a/source/val/validate_logicals.cpp b/source/val/validate_logicals.cpp
index bb35f558..ec1e207b 100644
--- a/source/val/validate_logicals.cpp
+++ b/source/val/validate_logicals.cpp
@@ -163,14 +163,23 @@ spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) {
switch (type_opcode) {
case SpvOpTypePointer: {
if (_.addressing_model() == SpvAddressingModelLogical &&
- !_.features().variable_pointers &&
- !_.features().variable_pointers_storage_buffer)
+ !_.features().variable_pointers)
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Using pointers with OpSelect requires capability "
<< "VariablePointers or VariablePointersStorageBuffer";
break;
}
+ case SpvOpTypeSampledImage:
+ case SpvOpTypeImage:
+ case SpvOpTypeSampler: {
+ if (!_.HasCapability(SpvCapabilityBindlessTextureNV))
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Using image/sampler with OpSelect requires capability "
+ << "BindlessTextureNV";
+ break;
+ }
+
case SpvOpTypeVector: {
dimension = type_inst->word(3);
break;
diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp
index 93b18000..f939542f 100644
--- a/source/val/validate_memory.cpp
+++ b/source/val/validate_memory.cpp
@@ -35,8 +35,8 @@ bool HaveLayoutCompatibleMembers(ValidationState_t&, const Instruction*,
const Instruction*);
bool HaveSameLayoutDecorations(ValidationState_t&, const Instruction*,
const Instruction*);
-bool HasConflictingMemberOffsets(const std::vector<Decoration>&,
- const std::vector<Decoration>&);
+bool HasConflictingMemberOffsets(const std::set<Decoration>&,
+ const std::set<Decoration>&);
bool IsAllowedTypeOrArrayOfSame(ValidationState_t& _, const Instruction* type,
std::initializer_list<uint32_t> allowed) {
@@ -105,10 +105,8 @@ bool HaveSameLayoutDecorations(ValidationState_t& _, const Instruction* type1,
"type1 must be an OpTypeStruct instruction.");
assert(type2->opcode() == SpvOpTypeStruct &&
"type2 must be an OpTypeStruct instruction.");
- const std::vector<Decoration>& type1_decorations =
- _.id_decorations(type1->id());
- const std::vector<Decoration>& type2_decorations =
- _.id_decorations(type2->id());
+ const std::set<Decoration>& type1_decorations = _.id_decorations(type1->id());
+ const std::set<Decoration>& type2_decorations = _.id_decorations(type2->id());
// TODO: Will have to add other check for arrays an matricies if we want to
// handle them.
@@ -120,8 +118,8 @@ bool HaveSameLayoutDecorations(ValidationState_t& _, const Instruction* type1,
}
bool HasConflictingMemberOffsets(
- const std::vector<Decoration>& type1_decorations,
- const std::vector<Decoration>& type2_decorations) {
+ const std::set<Decoration>& type1_decorations,
+ const std::set<Decoration>& type2_decorations) {
{
// We are interested in conflicting decoration. If a decoration is in one
// list but not the other, then we will assume the code is correct. We are
@@ -315,11 +313,12 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
SpvStorageClass dst_sc, src_sc;
std::tie(dst_sc, src_sc) = GetStorageClass(_, inst);
if (inst->operands().size() <= index) {
- if (src_sc == SpvStorageClassPhysicalStorageBufferEXT ||
- dst_sc == SpvStorageClassPhysicalStorageBufferEXT) {
+ // Cases where lack of some operand is invalid
+ if (src_sc == SpvStorageClassPhysicalStorageBuffer ||
+ dst_sc == SpvStorageClassPhysicalStorageBuffer) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "Memory accesses with PhysicalStorageBufferEXT must use "
- "Aligned.";
+ << _.VkErrorID(4708)
+ << "Memory accesses with PhysicalStorageBuffer must use Aligned.";
}
return SPV_SUCCESS;
}
@@ -368,7 +367,7 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
dst_sc != SpvStorageClassCrossWorkgroup &&
dst_sc != SpvStorageClassGeneric && dst_sc != SpvStorageClassImage &&
dst_sc != SpvStorageClassStorageBuffer &&
- dst_sc != SpvStorageClassPhysicalStorageBufferEXT) {
+ dst_sc != SpvStorageClassPhysicalStorageBuffer) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "NonPrivatePointerKHR requires a pointer in Uniform, "
<< "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer "
@@ -379,7 +378,7 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
src_sc != SpvStorageClassCrossWorkgroup &&
src_sc != SpvStorageClassGeneric && src_sc != SpvStorageClassImage &&
src_sc != SpvStorageClassStorageBuffer &&
- src_sc != SpvStorageClassPhysicalStorageBufferEXT) {
+ src_sc != SpvStorageClassPhysicalStorageBuffer) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "NonPrivatePointerKHR requires a pointer in Uniform, "
<< "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer "
@@ -388,11 +387,11 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
}
if (!(mask & SpvMemoryAccessAlignedMask)) {
- if (src_sc == SpvStorageClassPhysicalStorageBufferEXT ||
- dst_sc == SpvStorageClassPhysicalStorageBufferEXT) {
+ if (src_sc == SpvStorageClassPhysicalStorageBuffer ||
+ dst_sc == SpvStorageClassPhysicalStorageBuffer) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "Memory accesses with PhysicalStorageBufferEXT must use "
- "Aligned.";
+ << _.VkErrorID(4708)
+ << "Memory accesses with PhysicalStorageBuffer must use Aligned.";
}
}
@@ -439,11 +438,12 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
storage_class != SpvStorageClassCrossWorkgroup &&
storage_class != SpvStorageClassPrivate &&
storage_class != SpvStorageClassFunction &&
- storage_class != SpvStorageClassRayPayloadNV &&
- storage_class != SpvStorageClassIncomingRayPayloadNV &&
- storage_class != SpvStorageClassHitAttributeNV &&
- storage_class != SpvStorageClassCallableDataNV &&
- storage_class != SpvStorageClassIncomingCallableDataNV) {
+ storage_class != SpvStorageClassRayPayloadKHR &&
+ storage_class != SpvStorageClassIncomingRayPayloadKHR &&
+ storage_class != SpvStorageClassHitAttributeKHR &&
+ storage_class != SpvStorageClassCallableDataKHR &&
+ storage_class != SpvStorageClassIncomingCallableDataKHR &&
+ storage_class != SpvStorageClassTaskPayloadWorkgroupEXT) {
bool storage_input_or_output = storage_class == SpvStorageClassInput ||
storage_class == SpvStorageClassOutput;
bool builtin = false;
@@ -455,12 +455,24 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
}
}
}
- if (!(storage_input_or_output && builtin) &&
+ if (!builtin &&
ContainsInvalidBool(_, value_type, storage_input_or_output)) {
- return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "If OpTypeBool is stored in conjunction with OpVariable, it "
- << "can only be used with non-externally visible shader Storage "
- << "Classes: Workgroup, CrossWorkgroup, Private, and Function";
+ if (storage_input_or_output) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << _.VkErrorID(7290)
+ << "If OpTypeBool is stored in conjunction with OpVariable "
+ "using Input or Output Storage Classes it requires a BuiltIn "
+ "decoration";
+
+ } else {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "If OpTypeBool is stored in conjunction with OpVariable, it "
+ "can only be used with non-externally visible shader Storage "
+ "Classes: Workgroup, CrossWorkgroup, Private, Function, "
+ "Input, Output, RayPayloadKHR, IncomingRayPayloadKHR, "
+ "HitAttributeKHR, CallableDataKHR, or "
+ "IncomingCallableDataKHR";
+ }
}
}
@@ -519,20 +531,21 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
}
}
- // Vulkan 14.5.1: Check type of PushConstant variables.
- // Vulkan 14.5.2: Check type of UniformConstant and Uniform variables.
if (spvIsVulkanEnv(_.context()->target_env)) {
+ // Vulkan Push Constant Interface section: Check type of PushConstant
+ // variables.
if (storage_class == SpvStorageClassPushConstant) {
- if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) {
+ if (pointee->opcode() != SpvOpTypeStruct) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "PushConstant OpVariable <id> '" << _.getIdName(inst->id())
- << "' has illegal type.\n"
- << "From Vulkan spec, section 14.5.1:\n"
- << "Such variables must be typed as OpTypeStruct, "
- << "or an array of this type";
+ << _.VkErrorID(6808) << "PushConstant OpVariable <id> '"
+ << _.getIdName(inst->id()) << "' has illegal type.\n"
+ << "From Vulkan spec, Push Constant Interface section:\n"
+ << "Such variables must be typed as OpTypeStruct";
}
}
+ // Vulkan Descriptor Set Interface: Check type of UniformConstant and
+ // Uniform variables.
if (storage_class == SpvStorageClassUniformConstant) {
if (!IsAllowedTypeOrArrayOfSame(
_, pointee,
@@ -552,9 +565,9 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
if (storage_class == SpvStorageClassUniform) {
if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "Uniform OpVariable <id> '" << _.getIdName(inst->id())
- << "' has illegal type.\n"
- << "From Vulkan spec, section 14.5.2:\n"
+ << _.VkErrorID(6807) << "Uniform OpVariable <id> '"
+ << _.getIdName(inst->id()) << "' has illegal type.\n"
+ << "From Vulkan spec:\n"
<< "Variables identified with the Uniform storage class are "
<< "used to access transparent buffer backed resources. Such "
<< "variables must be typed as OpTypeStruct, or an array of "
@@ -565,9 +578,9 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
if (storage_class == SpvStorageClassStorageBuffer) {
if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "StorageBuffer OpVariable <id> '" << _.getIdName(inst->id())
- << "' has illegal type.\n"
- << "From Vulkan spec, section 14.5.2:\n"
+ << _.VkErrorID(6807) << "StorageBuffer OpVariable <id> '"
+ << _.getIdName(inst->id()) << "' has illegal type.\n"
+ << "From Vulkan spec:\n"
<< "Variables identified with the StorageBuffer storage class "
"are used to access transparent buffer backed resources. "
"Such variables must be typed as OpTypeStruct, or an array "
@@ -596,23 +609,23 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
}
}
}
- }
- // Vulkan Appendix A: Check that if contains initializer, then
- // storage class is Output, Private, or Function.
- if (inst->operands().size() > 3 && storage_class != SpvStorageClassOutput &&
- storage_class != SpvStorageClassPrivate &&
- storage_class != SpvStorageClassFunction) {
- if (spvIsVulkanEnv(_.context()->target_env)) {
+ // Initializers in Vulkan are only allowed in some storage clases
+ if (inst->operands().size() > 3) {
if (storage_class == SpvStorageClassWorkgroup) {
auto init_id = inst->GetOperandAs<uint32_t>(3);
auto init = _.FindDef(init_id);
if (init->opcode() != SpvOpConstantNull) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "Variable initializers in Workgroup storage class are "
- "limited to OpConstantNull";
+ << _.VkErrorID(4734) << "OpVariable, <id> '"
+ << _.getIdName(inst->id())
+ << "', initializers are limited to OpConstantNull in "
+ "Workgroup "
+ "storage class";
}
- } else {
+ } else if (storage_class != SpvStorageClassOutput &&
+ storage_class != SpvStorageClassPrivate &&
+ storage_class != SpvStorageClassFunction) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< _.VkErrorID(4651) << "OpVariable, <id> '"
<< _.getIdName(inst->id())
@@ -627,9 +640,9 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
}
}
- if (storage_class == SpvStorageClassPhysicalStorageBufferEXT) {
+ if (storage_class == SpvStorageClassPhysicalStorageBuffer) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "PhysicalStorageBufferEXT must not be used with OpVariable.";
+ << "PhysicalStorageBuffer must not be used with OpVariable.";
}
auto pointee_base = pointee;
@@ -638,23 +651,23 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
}
if (pointee_base->opcode() == SpvOpTypePointer) {
if (pointee_base->GetOperandAs<uint32_t>(1u) ==
- SpvStorageClassPhysicalStorageBufferEXT) {
- // check for AliasedPointerEXT/RestrictPointerEXT
+ SpvStorageClassPhysicalStorageBuffer) {
+ // check for AliasedPointer/RestrictPointer
bool foundAliased =
- _.HasDecoration(inst->id(), SpvDecorationAliasedPointerEXT);
+ _.HasDecoration(inst->id(), SpvDecorationAliasedPointer);
bool foundRestrict =
- _.HasDecoration(inst->id(), SpvDecorationRestrictPointerEXT);
+ _.HasDecoration(inst->id(), SpvDecorationRestrictPointer);
if (!foundAliased && !foundRestrict) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpVariable " << inst->id()
- << ": expected AliasedPointerEXT or RestrictPointerEXT for "
- << "PhysicalStorageBufferEXT pointer.";
+ << ": expected AliasedPointer or RestrictPointer for "
+ << "PhysicalStorageBuffer pointer.";
}
if (foundAliased && foundRestrict) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpVariable " << inst->id()
- << ": can't specify both AliasedPointerEXT and "
- << "RestrictPointerEXT for PhysicalStorageBufferEXT pointer.";
+ << ": can't specify both AliasedPointer and "
+ << "RestrictPointer for PhysicalStorageBuffer pointer.";
}
}
}
@@ -667,7 +680,8 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
if (value_type && value_type->opcode() == SpvOpTypeRuntimeArray) {
if (!_.HasCapability(SpvCapabilityRuntimeDescriptorArrayEXT)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "OpVariable, <id> '" << _.getIdName(inst->id())
+ << _.VkErrorID(4680) << "OpVariable, <id> '"
+ << _.getIdName(inst->id())
<< "', is attempting to create memory for an illegal type, "
<< "OpTypeRuntimeArray.\nFor Vulkan OpTypeRuntimeArray can only "
<< "appear as the final member of an OpTypeStruct, thus cannot "
@@ -679,6 +693,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
storage_class != SpvStorageClassUniform &&
storage_class != SpvStorageClassUniformConstant) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << _.VkErrorID(4680)
<< "For Vulkan with RuntimeDescriptorArrayEXT, a variable "
<< "containing OpTypeRuntimeArray must have storage class of "
<< "StorageBuffer, Uniform, or UniformConstant.";
@@ -692,25 +707,30 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
// as BufferBlock.
if (value_type && value_type->opcode() == SpvOpTypeStruct) {
if (DoesStructContainRTA(_, value_type)) {
- if (storage_class == SpvStorageClassStorageBuffer) {
+ if (storage_class == SpvStorageClassStorageBuffer ||
+ storage_class == SpvStorageClassPhysicalStorageBuffer) {
if (!_.HasDecoration(value_id, SpvDecorationBlock)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << _.VkErrorID(4680)
<< "For Vulkan, an OpTypeStruct variable containing an "
<< "OpTypeRuntimeArray must be decorated with Block if it "
- << "has storage class StorageBuffer.";
+ << "has storage class StorageBuffer or "
+ "PhysicalStorageBuffer.";
}
} else if (storage_class == SpvStorageClassUniform) {
if (!_.HasDecoration(value_id, SpvDecorationBufferBlock)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << _.VkErrorID(4680)
<< "For Vulkan, an OpTypeStruct variable containing an "
<< "OpTypeRuntimeArray must be decorated with BufferBlock "
<< "if it has storage class Uniform.";
}
} else {
return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << _.VkErrorID(4680)
<< "For Vulkan, OpTypeStruct variables containing "
<< "OpTypeRuntimeArray must have storage class of "
- << "StorageBuffer or Uniform.";
+ << "StorageBuffer, PhysicalStorageBuffer, or Uniform.";
}
}
}
@@ -745,7 +765,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
SPV_OPERAND_TYPE_STORAGE_CLASS, storage_class);
switch (storage_class) {
case SpvStorageClassStorageBuffer:
- case SpvStorageClassPhysicalStorageBufferEXT:
+ case SpvStorageClassPhysicalStorageBuffer:
if (!_.HasCapability(SpvCapabilityStorageBuffer16BitAccess)) {
storage_class_ok = false;
}
@@ -807,7 +827,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
SPV_OPERAND_TYPE_STORAGE_CLASS, storage_class);
switch (storage_class) {
case SpvStorageClassStorageBuffer:
- case SpvStorageClassPhysicalStorageBufferEXT:
+ case SpvStorageClassPhysicalStorageBuffer:
if (!_.HasCapability(SpvCapabilityStorageBuffer8BitAccess)) {
storage_class_ok = false;
}
@@ -861,17 +881,14 @@ spv_result_t ValidateLoad(ValidationState_t& _, const Instruction* inst) {
<< "' is not defined.";
}
- const bool uses_variable_pointers =
- _.features().variable_pointers ||
- _.features().variable_pointers_storage_buffer;
const auto pointer_index = 2;
const auto pointer_id = inst->GetOperandAs<uint32_t>(pointer_index);
const auto pointer = _.FindDef(pointer_id);
if (!pointer ||
((_.addressing_model() == SpvAddressingModelLogical) &&
- ((!uses_variable_pointers &&
+ ((!_.features().variable_pointers &&
!spvOpcodeReturnsLogicalPointer(pointer->opcode())) ||
- (uses_variable_pointers &&
+ (_.features().variable_pointers &&
!spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpLoad Pointer <id> '" << _.getIdName(pointer_id)
@@ -885,8 +902,11 @@ spv_result_t ValidateLoad(ValidationState_t& _, const Instruction* inst) {
<< "' is not a pointer type.";
}
- const auto pointee_type = _.FindDef(pointer_type->GetOperandAs<uint32_t>(2));
- if (!pointee_type || result_type->id() != pointee_type->id()) {
+ uint32_t pointee_data_type;
+ uint32_t storage_class;
+ if (!_.GetPointerTypeInfo(pointer_type->id(), &pointee_data_type,
+ &storage_class) ||
+ result_type->id() != pointee_data_type) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpLoad Result Type <id> '" << _.getIdName(inst->type_id())
<< "' does not match Pointer <id> '" << _.getIdName(pointer->id())
@@ -917,17 +937,14 @@ spv_result_t ValidateLoad(ValidationState_t& _, const Instruction* inst) {
}
spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) {
- const bool uses_variable_pointer =
- _.features().variable_pointers ||
- _.features().variable_pointers_storage_buffer;
const auto pointer_index = 0;
const auto pointer_id = inst->GetOperandAs<uint32_t>(pointer_index);
const auto pointer = _.FindDef(pointer_id);
if (!pointer ||
(_.addressing_model() == SpvAddressingModelLogical &&
- ((!uses_variable_pointer &&
+ ((!_.features().variable_pointers &&
!spvOpcodeReturnsLogicalPointer(pointer->opcode())) ||
- (uses_variable_pointer &&
+ (_.features().variable_pointers &&
!spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpStore Pointer <id> '" << _.getIdName(pointer_id)
@@ -963,6 +980,26 @@ spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpStore Pointer <id> '" << _.getIdName(pointer_id)
<< "' storage class is read-only";
+ } else if (storage_class == SpvStorageClassShaderRecordBufferKHR) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "ShaderRecordBufferKHR Storage Class variables are read only";
+ } else if (storage_class == SpvStorageClassHitAttributeKHR) {
+ std::string errorVUID = _.VkErrorID(4703);
+ _.function(inst->function()->id())
+ ->RegisterExecutionModelLimitation(
+ [errorVUID](SpvExecutionModel model, std::string* message) {
+ if (model == SpvExecutionModelAnyHitKHR ||
+ model == SpvExecutionModelClosestHitKHR) {
+ if (message) {
+ *message =
+ errorVUID +
+ "HitAttributeKHR Storage Class variables are read only "
+ "with AnyHitKHR and ClosestHitKHR";
+ }
+ return false;
+ }
+ return true;
+ });
}
if (spvIsVulkanEnv(_.context()->target_env) &&
@@ -979,6 +1016,7 @@ spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) {
}
if (_.HasDecoration(base_type->id(), SpvDecorationBlock)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << _.VkErrorID(6925)
<< "In the Vulkan environment, cannot store to Uniform Blocks";
}
}
@@ -1138,8 +1176,6 @@ spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) {
<< "'s type does not match Source <id> '"
<< _.getIdName(source_type->id()) << "'s type.";
}
-
- if (auto error = CheckMemoryAccess(_, inst, 2)) return error;
} else {
const auto size_id = inst->GetOperandAs<uint32_t>(2);
const auto size = _.FindDef(size_id);
@@ -1182,8 +1218,6 @@ spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) {
// Cannot infer any other opcodes.
break;
}
-
- if (auto error = CheckMemoryAccess(_, inst, 3)) return error;
}
if (auto error = ValidateCopyMemoryMemoryAccess(_, inst)) return error;
@@ -1357,8 +1391,7 @@ spv_result_t ValidateAccessChain(ValidationState_t& _,
spv_result_t ValidatePtrAccessChain(ValidationState_t& _,
const Instruction* inst) {
if (_.addressing_model() == SpvAddressingModelLogical) {
- if (!_.features().variable_pointers &&
- !_.features().variable_pointers_storage_buffer) {
+ if (!_.features().variable_pointers) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Generating variable pointers requires capability "
<< "VariablePointers or VariablePointersStorageBuffer";
@@ -1476,18 +1509,15 @@ spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _,
}
}
- const bool uses_variable_pointers =
- _.features().variable_pointers ||
- _.features().variable_pointers_storage_buffer;
const auto pointer_index =
(inst->opcode() == SpvOpCooperativeMatrixLoadNV) ? 2u : 0u;
const auto pointer_id = inst->GetOperandAs<uint32_t>(pointer_index);
const auto pointer = _.FindDef(pointer_id);
if (!pointer ||
((_.addressing_model() == SpvAddressingModelLogical) &&
- ((!uses_variable_pointers &&
+ ((!_.features().variable_pointers &&
!spvOpcodeReturnsLogicalPointer(pointer->opcode())) ||
- (uses_variable_pointers &&
+ (_.features().variable_pointers &&
!spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< opname << " Pointer <id> '" << _.getIdName(pointer_id)
@@ -1508,7 +1538,7 @@ spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _,
if (storage_class != SpvStorageClassWorkgroup &&
storage_class != SpvStorageClassStorageBuffer &&
- storage_class != SpvStorageClassPhysicalStorageBufferEXT) {
+ storage_class != SpvStorageClassPhysicalStorageBuffer) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< opname << " storage class for pointer type <id> '"
<< _.getIdName(pointer_type_id)
@@ -1559,10 +1589,10 @@ spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _,
spv_result_t ValidatePtrComparison(ValidationState_t& _,
const Instruction* inst) {
if (_.addressing_model() == SpvAddressingModelLogical &&
- !_.features().variable_pointers_storage_buffer) {
+ !_.features().variable_pointers) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "Instruction cannot be used without a variable pointers "
- "capability";
+ << "Instruction cannot for logical addressing model be used without "
+ "a variable pointers capability";
}
const auto result_type = _.FindDef(inst->type_id());
@@ -1597,7 +1627,8 @@ spv_result_t ValidatePtrComparison(ValidationState_t& _,
<< "Invalid pointer storage class";
}
- if (sc == SpvStorageClassWorkgroup && !_.features().variable_pointers) {
+ if (sc == SpvStorageClassWorkgroup &&
+ !_.HasCapability(SpvCapabilityVariablePointers)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Workgroup storage class pointer requires VariablePointers "
"capability to be specified";
diff --git a/source/val/validate_misc.cpp b/source/val/validate_misc.cpp
index 3bc15ca0..5acc21ea 100644
--- a/source/val/validate_misc.cpp
+++ b/source/val/validate_misc.cpp
@@ -59,10 +59,7 @@ spv_result_t ValidateShaderClock(ValidationState_t& _,
// a vector of two - components of 32 -
// bit unsigned integer type
const uint32_t result_type = inst->type_id();
- if (!(_.IsUnsignedIntScalarType(result_type) &&
- _.GetBitWidth(result_type) == 64) &&
- !(_.IsUnsignedIntVectorType(result_type) &&
- _.GetDimension(result_type) == 2 && _.GetBitWidth(result_type) == 32)) {
+ if (!_.IsUnsigned64BitHandle(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Value to be a "
"vector of two components"
" of unsigned integer"
diff --git a/source/val/validate_mode_setting.cpp b/source/val/validate_mode_setting.cpp
index 96352687..09a9d48e 100644
--- a/source/val/validate_mode_setting.cpp
+++ b/source/val/validate_mode_setting.cpp
@@ -12,13 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
-#include "source/val/validate.h"
-
#include <algorithm>
#include "source/opcode.h"
#include "source/spirv_target_env.h"
#include "source/val/instruction.h"
+#include "source/val/validate.h"
#include "source/val/validation_state.h"
namespace spvtools {
@@ -112,6 +111,44 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
<< "Fragment execution model entry points can specify at most "
"one fragment shader interlock execution mode.";
}
+ if (execution_modes &&
+ 1 < std::count_if(
+ execution_modes->begin(), execution_modes->end(),
+ [](const SpvExecutionMode& mode) {
+ switch (mode) {
+ case SpvExecutionModeStencilRefUnchangedFrontAMD:
+ case SpvExecutionModeStencilRefLessFrontAMD:
+ case SpvExecutionModeStencilRefGreaterFrontAMD:
+ return true;
+ default:
+ return false;
+ }
+ })) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Fragment execution model entry points can specify at most "
+ "one of StencilRefUnchangedFrontAMD, "
+ "StencilRefLessFrontAMD or StencilRefGreaterFrontAMD "
+ "execution modes.";
+ }
+ if (execution_modes &&
+ 1 < std::count_if(
+ execution_modes->begin(), execution_modes->end(),
+ [](const SpvExecutionMode& mode) {
+ switch (mode) {
+ case SpvExecutionModeStencilRefUnchangedBackAMD:
+ case SpvExecutionModeStencilRefLessBackAMD:
+ case SpvExecutionModeStencilRefGreaterBackAMD:
+ return true;
+ default:
+ return false;
+ }
+ })) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Fragment execution model entry points can specify at most "
+ "one of StencilRefUnchangedBackAMD, "
+ "StencilRefLessBackAMD or StencilRefGreaterBackAMD "
+ "execution modes.";
+ }
break;
case SpvExecutionModelTessellationControl:
case SpvExecutionModelTessellationEvaluation:
@@ -321,14 +358,18 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _,
return true;
case SpvExecutionModelMeshNV:
return _.HasCapability(SpvCapabilityMeshShadingNV);
+ case SpvExecutionModelMeshEXT:
+ return _.HasCapability(
+ SpvCapabilityMeshShadingEXT);
default:
return false;
}
})) {
- if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
+ if (_.HasCapability(SpvCapabilityMeshShadingNV) ||
+ _.HasCapability(SpvCapabilityMeshShadingEXT)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Execution mode can only be used with the Geometry or "
- "MeshNV execution model.";
+ << "Execution mode can only be used with the Geometry "
+ "MeshNV or MeshEXT execution model.";
} else {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Execution mode can only be used with the Geometry "
@@ -383,14 +424,18 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _,
return true;
case SpvExecutionModelMeshNV:
return _.HasCapability(SpvCapabilityMeshShadingNV);
+ case SpvExecutionModelMeshEXT:
+ return _.HasCapability(
+ SpvCapabilityMeshShadingEXT);
default:
return false;
}
})) {
- if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
+ if (_.HasCapability(SpvCapabilityMeshShadingNV) ||
+ _.HasCapability(SpvCapabilityMeshShadingEXT)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Execution mode can only be used with a Geometry, "
- "tessellation or MeshNV execution model.";
+ "tessellation, MeshNV or MeshEXT execution model.";
} else {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Execution mode can only be used with a Geometry or "
@@ -412,6 +457,13 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _,
case SpvExecutionModeSampleInterlockUnorderedEXT:
case SpvExecutionModeShadingRateInterlockOrderedEXT:
case SpvExecutionModeShadingRateInterlockUnorderedEXT:
+ case SpvExecutionModeEarlyAndLateFragmentTestsAMD:
+ case SpvExecutionModeStencilRefUnchangedFrontAMD:
+ case SpvExecutionModeStencilRefGreaterFrontAMD:
+ case SpvExecutionModeStencilRefLessFrontAMD:
+ case SpvExecutionModeStencilRefUnchangedBackAMD:
+ case SpvExecutionModeStencilRefGreaterBackAMD:
+ case SpvExecutionModeStencilRefLessBackAMD:
if (!std::all_of(models->begin(), models->end(),
[](const SpvExecutionModel& model) {
return model == SpvExecutionModelFragment;
@@ -449,14 +501,19 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _,
case SpvExecutionModelTaskNV:
case SpvExecutionModelMeshNV:
return _.HasCapability(SpvCapabilityMeshShadingNV);
+ case SpvExecutionModelTaskEXT:
+ case SpvExecutionModelMeshEXT:
+ return _.HasCapability(
+ SpvCapabilityMeshShadingEXT);
default:
return false;
}
})) {
- if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
+ if (_.HasCapability(SpvCapabilityMeshShadingNV) ||
+ _.HasCapability(SpvCapabilityMeshShadingEXT)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Execution mode can only be used with a Kernel, GLCompute, "
- "MeshNV, or TaskNV execution model.";
+ "MeshNV, MeshEXT, TaskNV or TaskEXT execution model.";
} else {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Execution mode can only be used with a Kernel or "
diff --git a/source/val/validate_non_uniform.cpp b/source/val/validate_non_uniform.cpp
index 2b6eb8b5..6d4f8a28 100644
--- a/source/val/validate_non_uniform.cpp
+++ b/source/val/validate_non_uniform.cpp
@@ -63,6 +63,59 @@ spv_result_t ValidateGroupNonUniformBallotBitCount(ValidationState_t& _,
return SPV_SUCCESS;
}
+spv_result_t ValidateGroupNonUniformRotateKHR(ValidationState_t& _,
+ const Instruction* inst) {
+ // Scope is already checked by ValidateExecutionScope() above.
+ const uint32_t result_type = inst->type_id();
+ if (!_.IsIntScalarOrVectorType(result_type) &&
+ !_.IsFloatScalarOrVectorType(result_type) &&
+ !_.IsBoolScalarOrVectorType(result_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Expected Result Type to be a scalar or vector of "
+ "floating-point, integer or boolean type.";
+ }
+
+ const uint32_t value_type = _.GetTypeId(inst->GetOperandAs<uint32_t>(3));
+ if (value_type != result_type) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Result Type must be the same as the type of Value.";
+ }
+
+ const uint32_t delta_type = _.GetTypeId(inst->GetOperandAs<uint32_t>(4));
+ if (!_.IsUnsignedIntScalarType(delta_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Delta must be a scalar of integer type, whose Signedness "
+ "operand is 0.";
+ }
+
+ if (inst->words().size() > 6) {
+ const uint32_t cluster_size_op_id = inst->GetOperandAs<uint32_t>(5);
+ const uint32_t cluster_size_type = _.GetTypeId(cluster_size_op_id);
+ if (!_.IsUnsignedIntScalarType(cluster_size_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "ClusterSize must be a scalar of integer type, whose "
+ "Signedness operand is 0.";
+ }
+
+ uint64_t cluster_size;
+ if (!_.GetConstantValUint64(cluster_size_op_id, &cluster_size)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "ClusterSize must come from a constant instruction.";
+ }
+
+ if ((cluster_size == 0) || ((cluster_size & (cluster_size - 1)) != 0)) {
+ return _.diag(SPV_WARNING, inst)
+ << "Behavior is undefined unless ClusterSize is at least 1 and a "
+ "power of 2.";
+ }
+
+ // TODO(kpet) Warn about undefined behavior when ClusterSize is greater than
+ // the declared SubGroupSize
+ }
+
+ return SPV_SUCCESS;
+}
+
} // namespace
// Validates correctness of non-uniform group instructions.
@@ -79,6 +132,8 @@ spv_result_t NonUniformPass(ValidationState_t& _, const Instruction* inst) {
switch (opcode) {
case SpvOpGroupNonUniformBallotBitCount:
return ValidateGroupNonUniformBallotBitCount(_, inst);
+ case SpvOpGroupNonUniformRotateKHR:
+ return ValidateGroupNonUniformRotateKHR(_, inst);
default:
break;
}
diff --git a/source/val/validate_ray_query.cpp b/source/val/validate_ray_query.cpp
new file mode 100644
index 00000000..b553449d
--- /dev/null
+++ b/source/val/validate_ray_query.cpp
@@ -0,0 +1,273 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+//
+// 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.
+
+// Validates ray query instructions from SPV_KHR_ray_query
+
+#include "source/opcode.h"
+#include "source/val/instruction.h"
+#include "source/val/validate.h"
+#include "source/val/validation_state.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+spv_result_t ValidateRayQueryPointer(ValidationState_t& _,
+ const Instruction* inst,
+ uint32_t ray_query_index) {
+ const uint32_t ray_query_id = inst->GetOperandAs<uint32_t>(ray_query_index);
+ auto variable = _.FindDef(ray_query_id);
+ const auto var_opcode = variable->opcode();
+ if (!variable ||
+ (var_opcode != SpvOpVariable && var_opcode != SpvOpFunctionParameter &&
+ var_opcode != SpvOpAccessChain)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray Query must be a memory object declaration";
+ }
+ auto pointer = _.FindDef(variable->GetOperandAs<uint32_t>(0));
+ if (!pointer || pointer->opcode() != SpvOpTypePointer) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray Query must be a pointer";
+ }
+ auto type = _.FindDef(pointer->GetOperandAs<uint32_t>(2));
+ if (!type || type->opcode() != SpvOpTypeRayQueryKHR) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray Query must be a pointer to OpTypeRayQueryKHR";
+ }
+ return SPV_SUCCESS;
+}
+
+spv_result_t ValidateIntersectionId(ValidationState_t& _,
+ const Instruction* inst,
+ uint32_t intersection_index) {
+ const uint32_t intersection_id =
+ inst->GetOperandAs<uint32_t>(intersection_index);
+ const uint32_t intersection_type = _.GetTypeId(intersection_id);
+ const SpvOp intersection_opcode = _.GetIdOpcode(intersection_id);
+ if (!_.IsIntScalarType(intersection_type) ||
+ _.GetBitWidth(intersection_type) != 32 ||
+ !spvOpcodeIsConstant(intersection_opcode)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Intersection ID to be a constant 32-bit int scalar";
+ }
+
+ return SPV_SUCCESS;
+}
+
+} // namespace
+
+spv_result_t RayQueryPass(ValidationState_t& _, const Instruction* inst) {
+ const SpvOp opcode = inst->opcode();
+ const uint32_t result_type = inst->type_id();
+
+ switch (opcode) {
+ case SpvOpRayQueryInitializeKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error;
+
+ if (_.GetIdOpcode(_.GetOperandTypeId(inst, 1)) !=
+ SpvOpTypeAccelerationStructureKHR) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Expected Acceleration Structure to be of type "
+ "OpTypeAccelerationStructureKHR";
+ }
+
+ const uint32_t ray_flags = _.GetOperandTypeId(inst, 2);
+ if (!_.IsIntScalarType(ray_flags) || _.GetBitWidth(ray_flags) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray Flags must be a 32-bit int scalar";
+ }
+
+ const uint32_t cull_mask = _.GetOperandTypeId(inst, 3);
+ if (!_.IsIntScalarType(cull_mask) || _.GetBitWidth(cull_mask) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Cull Mask must be a 32-bit int scalar";
+ }
+
+ const uint32_t ray_origin = _.GetOperandTypeId(inst, 4);
+ if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 ||
+ _.GetBitWidth(ray_origin) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray Origin must be a 32-bit float 3-component vector";
+ }
+
+ const uint32_t ray_tmin = _.GetOperandTypeId(inst, 5);
+ if (!_.IsFloatScalarType(ray_tmin) || _.GetBitWidth(ray_tmin) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray TMin must be a 32-bit float scalar";
+ }
+
+ const uint32_t ray_direction = _.GetOperandTypeId(inst, 6);
+ if (!_.IsFloatVectorType(ray_direction) ||
+ _.GetDimension(ray_direction) != 3 ||
+ _.GetBitWidth(ray_direction) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray Direction must be a 32-bit float 3-component vector";
+ }
+
+ const uint32_t ray_tmax = _.GetOperandTypeId(inst, 7);
+ if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray TMax must be a 32-bit float scalar";
+ }
+ break;
+ }
+
+ case SpvOpRayQueryTerminateKHR:
+ case SpvOpRayQueryConfirmIntersectionKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error;
+ break;
+ }
+
+ case SpvOpRayQueryGenerateIntersectionKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error;
+
+ const uint32_t hit_t_id = _.GetOperandTypeId(inst, 1);
+ if (!_.IsFloatScalarType(hit_t_id) || _.GetBitWidth(hit_t_id) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Hit T must be a 32-bit float scalar";
+ }
+
+ break;
+ }
+
+ case SpvOpRayQueryGetIntersectionFrontFaceKHR:
+ case SpvOpRayQueryProceedKHR:
+ case SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+
+ if (!_.IsBoolScalarType(result_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Result Type to be bool scalar type";
+ }
+
+ if (opcode == SpvOpRayQueryGetIntersectionFrontFaceKHR) {
+ if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+ }
+
+ break;
+ }
+
+ case SpvOpRayQueryGetIntersectionTKHR:
+ case SpvOpRayQueryGetRayTMinKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+
+ if (!_.IsFloatScalarType(result_type) ||
+ _.GetBitWidth(result_type) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Result Type to be 32-bit float scalar type";
+ }
+
+ if (opcode == SpvOpRayQueryGetIntersectionTKHR) {
+ if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+ }
+
+ break;
+ }
+
+ case SpvOpRayQueryGetIntersectionTypeKHR:
+ case SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR:
+ case SpvOpRayQueryGetIntersectionInstanceIdKHR:
+ case SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR:
+ case SpvOpRayQueryGetIntersectionGeometryIndexKHR:
+ case SpvOpRayQueryGetIntersectionPrimitiveIndexKHR:
+ case SpvOpRayQueryGetRayFlagsKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+
+ if (!_.IsIntScalarType(result_type) || _.GetBitWidth(result_type) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Result Type to be 32-bit int scalar type";
+ }
+
+ if (opcode != SpvOpRayQueryGetRayFlagsKHR) {
+ if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+ }
+
+ break;
+ }
+
+ case SpvOpRayQueryGetIntersectionObjectRayDirectionKHR:
+ case SpvOpRayQueryGetIntersectionObjectRayOriginKHR:
+ case SpvOpRayQueryGetWorldRayDirectionKHR:
+ case SpvOpRayQueryGetWorldRayOriginKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+
+ if (!_.IsFloatVectorType(result_type) ||
+ _.GetDimension(result_type) != 3 ||
+ _.GetBitWidth(result_type) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Result Type to be 32-bit float 3-component "
+ "vector type";
+ }
+
+ if (opcode == SpvOpRayQueryGetIntersectionObjectRayDirectionKHR ||
+ opcode == SpvOpRayQueryGetIntersectionObjectRayOriginKHR) {
+ if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+ }
+
+ break;
+ }
+
+ case SpvOpRayQueryGetIntersectionBarycentricsKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+ if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+
+ if (!_.IsFloatVectorType(result_type) ||
+ _.GetDimension(result_type) != 2 ||
+ _.GetBitWidth(result_type) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Result Type to be 32-bit float 2-component "
+ "vector type";
+ }
+
+ break;
+ }
+
+ case SpvOpRayQueryGetIntersectionObjectToWorldKHR:
+ case SpvOpRayQueryGetIntersectionWorldToObjectKHR: {
+ if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+ if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+
+ uint32_t num_rows = 0;
+ uint32_t num_cols = 0;
+ uint32_t col_type = 0;
+ uint32_t component_type = 0;
+ if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type,
+ &component_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected matrix type as Result Type";
+ }
+
+ if (num_cols != 4) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Result Type matrix to have a Column Count of 4";
+ }
+
+ if (!_.IsFloatScalarType(component_type) ||
+ _.GetBitWidth(result_type) != 32 || num_rows != 3) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Result Type matrix to have a Column Type of "
+ "3-component 32-bit float vectors";
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return SPV_SUCCESS;
+}
+
+} // namespace val
+} // namespace spvtools
diff --git a/source/val/validate_ray_tracing.cpp b/source/val/validate_ray_tracing.cpp
new file mode 100644
index 00000000..78bac19b
--- /dev/null
+++ b/source/val/validate_ray_tracing.cpp
@@ -0,0 +1,206 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+//
+// 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.
+
+// Validates ray tracing instructions from SPV_KHR_ray_tracing
+
+#include "source/opcode.h"
+#include "source/val/instruction.h"
+#include "source/val/validate.h"
+#include "source/val/validation_state.h"
+
+namespace spvtools {
+namespace val {
+
+spv_result_t RayTracingPass(ValidationState_t& _, const Instruction* inst) {
+ const SpvOp opcode = inst->opcode();
+ const uint32_t result_type = inst->type_id();
+
+ switch (opcode) {
+ case SpvOpTraceRayKHR: {
+ _.function(inst->function()->id())
+ ->RegisterExecutionModelLimitation(
+ [](SpvExecutionModel model, std::string* message) {
+ if (model != SpvExecutionModelRayGenerationKHR &&
+ model != SpvExecutionModelClosestHitKHR &&
+ model != SpvExecutionModelMissKHR) {
+ if (message) {
+ *message =
+ "OpTraceRayKHR requires RayGenerationKHR, "
+ "ClosestHitKHR and MissKHR execution models";
+ }
+ return false;
+ }
+ return true;
+ });
+
+ if (_.GetIdOpcode(_.GetOperandTypeId(inst, 0)) !=
+ SpvOpTypeAccelerationStructureKHR) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Expected Acceleration Structure to be of type "
+ "OpTypeAccelerationStructureKHR";
+ }
+
+ const uint32_t ray_flags = _.GetOperandTypeId(inst, 1);
+ if (!_.IsIntScalarType(ray_flags) || _.GetBitWidth(ray_flags) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray Flags must be a 32-bit int scalar";
+ }
+
+ const uint32_t cull_mask = _.GetOperandTypeId(inst, 2);
+ if (!_.IsIntScalarType(cull_mask) || _.GetBitWidth(cull_mask) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Cull Mask must be a 32-bit int scalar";
+ }
+
+ const uint32_t sbt_offset = _.GetOperandTypeId(inst, 3);
+ if (!_.IsIntScalarType(sbt_offset) || _.GetBitWidth(sbt_offset) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "SBT Offset must be a 32-bit int scalar";
+ }
+
+ const uint32_t sbt_stride = _.GetOperandTypeId(inst, 4);
+ if (!_.IsIntScalarType(sbt_stride) || _.GetBitWidth(sbt_stride) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "SBT Stride must be a 32-bit int scalar";
+ }
+
+ const uint32_t miss_index = _.GetOperandTypeId(inst, 5);
+ if (!_.IsIntScalarType(miss_index) || _.GetBitWidth(miss_index) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Miss Index must be a 32-bit int scalar";
+ }
+
+ const uint32_t ray_origin = _.GetOperandTypeId(inst, 6);
+ if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 ||
+ _.GetBitWidth(ray_origin) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray Origin must be a 32-bit float 3-component vector";
+ }
+
+ const uint32_t ray_tmin = _.GetOperandTypeId(inst, 7);
+ if (!_.IsFloatScalarType(ray_tmin) || _.GetBitWidth(ray_tmin) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray TMin must be a 32-bit float scalar";
+ }
+
+ const uint32_t ray_direction = _.GetOperandTypeId(inst, 8);
+ if (!_.IsFloatVectorType(ray_direction) ||
+ _.GetDimension(ray_direction) != 3 ||
+ _.GetBitWidth(ray_direction) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray Direction must be a 32-bit float 3-component vector";
+ }
+
+ const uint32_t ray_tmax = _.GetOperandTypeId(inst, 9);
+ if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Ray TMax must be a 32-bit float scalar";
+ }
+
+ const Instruction* payload = _.FindDef(inst->GetOperandAs<uint32_t>(10));
+ if (payload->opcode() != SpvOpVariable) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Payload must be the result of a OpVariable";
+ } else if (payload->word(3) != SpvStorageClassRayPayloadKHR &&
+ payload->word(3) != SpvStorageClassIncomingRayPayloadKHR) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Payload must have storage class RayPayloadKHR or "
+ "IncomingRayPayloadKHR";
+ }
+ break;
+ }
+
+ case SpvOpReportIntersectionKHR: {
+ _.function(inst->function()->id())
+ ->RegisterExecutionModelLimitation(
+ [](SpvExecutionModel model, std::string* message) {
+ if (model != SpvExecutionModelIntersectionKHR) {
+ if (message) {
+ *message =
+ "OpReportIntersectionKHR requires IntersectionKHR "
+ "execution model";
+ }
+ return false;
+ }
+ return true;
+ });
+
+ if (!_.IsBoolScalarType(result_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "expected Result Type to be bool scalar type";
+ }
+
+ const uint32_t hit = _.GetOperandTypeId(inst, 2);
+ if (!_.IsFloatScalarType(hit) || _.GetBitWidth(hit) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Hit must be a 32-bit int scalar";
+ }
+
+ const uint32_t hit_kind = _.GetOperandTypeId(inst, 3);
+ if (!_.IsUnsignedIntScalarType(hit_kind) ||
+ _.GetBitWidth(hit_kind) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Hit Kind must be a 32-bit unsigned int scalar";
+ }
+ break;
+ }
+
+ case SpvOpExecuteCallableKHR: {
+ _.function(inst->function()->id())
+ ->RegisterExecutionModelLimitation([](SpvExecutionModel model,
+ std::string* message) {
+ if (model != SpvExecutionModelRayGenerationKHR &&
+ model != SpvExecutionModelClosestHitKHR &&
+ model != SpvExecutionModelMissKHR &&
+ model != SpvExecutionModelCallableKHR) {
+ if (message) {
+ *message =
+ "OpExecuteCallableKHR requires RayGenerationKHR, "
+ "ClosestHitKHR, MissKHR and CallableKHR execution models";
+ }
+ return false;
+ }
+ return true;
+ });
+
+ const uint32_t sbt_index = _.GetOperandTypeId(inst, 0);
+ if (!_.IsUnsignedIntScalarType(sbt_index) ||
+ _.GetBitWidth(sbt_index) != 32) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "SBT Index must be a 32-bit unsigned int scalar";
+ }
+
+ const auto callable_data = _.FindDef(inst->GetOperandAs<uint32_t>(1));
+ if (callable_data->opcode() != SpvOpVariable) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Callable Data must be the result of a OpVariable";
+ } else if (callable_data->word(3) != SpvStorageClassCallableDataKHR &&
+ callable_data->word(3) !=
+ SpvStorageClassIncomingCallableDataKHR) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "Callable Data must have storage class CallableDataKHR or "
+ "IncomingCallableDataKHR";
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return SPV_SUCCESS;
+}
+} // namespace val
+} // namespace spvtools
diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp
index 1c5f70a3..e9781802 100644
--- a/source/val/validate_scopes.cpp
+++ b/source/val/validate_scopes.cpp
@@ -144,14 +144,16 @@ spv_result_t ValidateExecutionScope(ValidationState_t& _,
[errorVUID](SpvExecutionModel model, std::string* message) {
if (model != SpvExecutionModelTaskNV &&
model != SpvExecutionModelMeshNV &&
+ model != SpvExecutionModelTaskEXT &&
+ model != SpvExecutionModelMeshEXT &&
model != SpvExecutionModelTessellationControl &&
model != SpvExecutionModelGLCompute) {
if (message) {
*message =
errorVUID +
"in Vulkan environment, Workgroup execution scope is "
- "only for TaskNV, MeshNV, TessellationControl, and "
- "GLCompute execution models";
+ "only for TaskNV, MeshNV, TaskEXT, MeshEXT, "
+ "TessellationControl, and GLCompute execution models";
}
return false;
}
@@ -220,30 +222,23 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
// Vulkan Specific rules
if (spvIsVulkanEnv(_.context()->target_env)) {
- if (value == SpvScopeCrossDevice) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << _.VkErrorID(4638) << spvOpcodeString(opcode)
- << ": in Vulkan environment, Memory Scope cannot be CrossDevice";
- }
- // Vulkan 1.0 specific rules
- if (_.context()->target_env == SPV_ENV_VULKAN_1_0 &&
- value != SpvScopeDevice && value != SpvScopeWorkgroup &&
- value != SpvScopeInvocation) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << _.VkErrorID(4638) << spvOpcodeString(opcode)
- << ": in Vulkan 1.0 environment Memory Scope is limited to "
- << "Device, Workgroup and Invocation";
- }
- // Vulkan 1.1 specific rules
- if ((_.context()->target_env == SPV_ENV_VULKAN_1_1 ||
- _.context()->target_env == SPV_ENV_VULKAN_1_2) &&
- value != SpvScopeDevice && value != SpvScopeWorkgroup &&
+ if (value != SpvScopeDevice && value != SpvScopeWorkgroup &&
value != SpvScopeSubgroup && value != SpvScopeInvocation &&
- value != SpvScopeShaderCallKHR) {
+ value != SpvScopeShaderCallKHR && value != SpvScopeQueueFamily) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< _.VkErrorID(4638) << spvOpcodeString(opcode)
- << ": in Vulkan 1.1 and 1.2 environment Memory Scope is limited "
- << "to Device, Workgroup, Invocation, and ShaderCall";
+ << ": in Vulkan environment Memory Scope is limited to Device, "
+ "QueueFamily, Workgroup, ShaderCallKHR, Subgroup, or "
+ "Invocation";
+ } else if (_.context()->target_env == SPV_ENV_VULKAN_1_0 &&
+ value == SpvScopeSubgroup &&
+ !_.HasCapability(SpvCapabilitySubgroupBallotKHR) &&
+ !_.HasCapability(SpvCapabilitySubgroupVoteKHR)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << _.VkErrorID(6997) << spvOpcodeString(opcode)
+ << ": in Vulkan 1.0 environment Memory Scope is can not be "
+ "Subgroup without SubgroupBallotKHR or SubgroupVoteKHR "
+ "declared";
}
if (value == SpvScopeShaderCallKHR) {
@@ -270,22 +265,44 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
}
if (value == SpvScopeWorkgroup) {
- std::string errorVUID = _.VkErrorID(4639);
+ std::string errorVUID = _.VkErrorID(7321);
_.function(inst->function()->id())
->RegisterExecutionModelLimitation(
[errorVUID](SpvExecutionModel model, std::string* message) {
if (model != SpvExecutionModelGLCompute &&
+ model != SpvExecutionModelTessellationControl &&
model != SpvExecutionModelTaskNV &&
- model != SpvExecutionModelMeshNV) {
+ model != SpvExecutionModelMeshNV &&
+ model != SpvExecutionModelTaskEXT &&
+ model != SpvExecutionModelMeshEXT) {
if (message) {
*message = errorVUID +
"Workgroup Memory Scope is limited to MeshNV, "
- "TaskNV, and GLCompute execution model";
+ "TaskNV, MeshEXT, TaskEXT, TessellationControl, "
+ "and GLCompute execution model";
}
return false;
}
return true;
});
+
+ if (_.memory_model() == SpvMemoryModelGLSL450) {
+ errorVUID = _.VkErrorID(7320);
+ _.function(inst->function()->id())
+ ->RegisterExecutionModelLimitation(
+ [errorVUID](SpvExecutionModel model, std::string* message) {
+ if (model == SpvExecutionModelTessellationControl) {
+ if (message) {
+ *message =
+ errorVUID +
+ "Workgroup Memory Scope can't be used with "
+ "TessellationControl using GLSL450 Memory Model";
+ }
+ return false;
+ }
+ return true;
+ });
+ }
}
}
diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp
index 4376b52c..b0b60792 100644
--- a/source/val/validate_type.cpp
+++ b/source/val/validate_type.cpp
@@ -228,8 +228,8 @@ spv_result_t ValidateTypeArray(ValidationState_t& _, const Instruction* inst) {
if (spvIsVulkanEnv(_.context()->target_env) &&
element_type->opcode() == SpvOpTypeRuntimeArray) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "OpTypeArray Element Type <id> '" << _.getIdName(element_type_id)
- << "' is not valid in "
+ << _.VkErrorID(4680) << "OpTypeArray Element Type <id> '"
+ << _.getIdName(element_type_id) << "' is not valid in "
<< spvLogStringForEnv(_.context()->target_env) << " environments.";
}
@@ -298,7 +298,7 @@ spv_result_t ValidateTypeRuntimeArray(ValidationState_t& _,
if (spvIsVulkanEnv(_.context()->target_env) &&
element_type->opcode() == SpvOpTypeRuntimeArray) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "OpTypeRuntimeArray Element Type <id> '"
+ << _.VkErrorID(4680) << "OpTypeRuntimeArray Element Type <id> '"
<< _.getIdName(element_id) << "' is not valid in "
<< spvLogStringForEnv(_.context()->target_env) << " environments.";
}
@@ -306,35 +306,6 @@ spv_result_t ValidateTypeRuntimeArray(ValidationState_t& _,
return SPV_SUCCESS;
}
-bool ContainsOpaqueType(ValidationState_t& _, const Instruction* str) {
- const size_t elem_type_index = 1;
- uint32_t elem_type_id;
- Instruction* elem_type;
-
- if (spvOpcodeIsBaseOpaqueType(str->opcode())) {
- return true;
- }
-
- switch (str->opcode()) {
- case SpvOpTypeArray:
- case SpvOpTypeRuntimeArray:
- elem_type_id = str->GetOperandAs<uint32_t>(elem_type_index);
- elem_type = _.FindDef(elem_type_id);
- return ContainsOpaqueType(_, elem_type);
- case SpvOpTypeStruct:
- for (size_t member_type_index = 1;
- member_type_index < str->operands().size(); ++member_type_index) {
- auto member_type_id = str->GetOperandAs<uint32_t>(member_type_index);
- auto member_type = _.FindDef(member_type_id);
- if (ContainsOpaqueType(_, member_type)) return true;
- }
- break;
- default:
- break;
- }
- return false;
-}
-
spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) {
const uint32_t struct_id = inst->GetOperandAs<uint32_t>(0);
for (size_t member_type_index = 1;
@@ -373,7 +344,8 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) {
member_type_index == inst->operands().size() - 1;
if (!is_last_member) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "In " << spvLogStringForEnv(_.context()->target_env)
+ << _.VkErrorID(4680) << "In "
+ << spvLogStringForEnv(_.context()->target_env)
<< ", OpTypeRuntimeArray must only be used for the last member "
"of an OpTypeStruct";
}
@@ -424,8 +396,21 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) {
_.RegisterStructTypeWithBuiltInMember(struct_id);
}
+ const auto isOpaqueType = [&_](const Instruction* opaque_inst) {
+ auto opcode = opaque_inst->opcode();
+ if (_.HasCapability(SpvCapabilityBindlessTextureNV) &&
+ (opcode == SpvOpTypeImage || opcode == SpvOpTypeSampler ||
+ opcode == SpvOpTypeSampledImage)) {
+ return false;
+ } else if (spvOpcodeIsBaseOpaqueType(opcode)) {
+ return true;
+ }
+ return false;
+ };
+
if (spvIsVulkanEnv(_.context()->target_env) &&
- !_.options()->before_hlsl_legalization && ContainsOpaqueType(_, inst)) {
+ !_.options()->before_hlsl_legalization &&
+ _.ContainsType(inst->id(), isOpaqueType)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< _.VkErrorID(4667) << "In "
<< spvLogStringForEnv(_.context()->target_env)
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index 6f97321f..fa8c624e 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -90,6 +90,8 @@ ModuleLayoutSection InstructionLayoutSection(
if (current_section == kLayoutFunctionDeclarations)
return kLayoutFunctionDeclarations;
return kLayoutFunctionDefinitions;
+ case SpvOpSamplerImageAddressingModeNV:
+ return kLayoutSamplerImageAddressMode;
default:
break;
}
@@ -161,6 +163,7 @@ ValidationState_t::ValidationState_t(const spv_const_context ctx,
addressing_model_(SpvAddressingModelMax),
memory_model_(SpvMemoryModelMax),
pointer_size_and_alignment_(0),
+ sampler_image_addressing_mode_(0),
in_function_(false),
num_of_warnings_(0),
max_num_of_warnings_(max_warnings) {
@@ -392,11 +395,8 @@ void ValidationState_t::RegisterCapability(SpvCapability cap) {
features_.free_fp_rounding_mode = true;
break;
case SpvCapabilityVariablePointers:
- features_.variable_pointers = true;
- features_.variable_pointers_storage_buffer = true;
- break;
case SpvCapabilityVariablePointersStorageBuffer:
- features_.variable_pointers_storage_buffer = true;
+ features_.variable_pointers = true;
break;
default:
// TODO(dneto): For now don't validate SPV_NV_ray_tracing, which uses
@@ -460,7 +460,7 @@ void ValidationState_t::set_addressing_model(SpvAddressingModel am) {
default:
// fall through
case SpvAddressingModelPhysical64:
- case SpvAddressingModelPhysicalStorageBuffer64EXT:
+ case SpvAddressingModelPhysicalStorageBuffer64:
pointer_size_and_alignment_ = 8;
break;
}
@@ -476,6 +476,15 @@ void ValidationState_t::set_memory_model(SpvMemoryModel mm) {
SpvMemoryModel ValidationState_t::memory_model() const { return memory_model_; }
+void ValidationState_t::set_samplerimage_variable_address_mode(
+ uint32_t bit_width) {
+ sampler_image_addressing_mode_ = bit_width;
+}
+
+uint32_t ValidationState_t::samplerimage_variable_address_mode() const {
+ return sampler_image_addressing_mode_;
+}
+
spv_result_t ValidationState_t::RegisterFunction(
uint32_t id, uint32_t ret_type_id, SpvFunctionControlMask function_control,
uint32_t function_type_id) {
@@ -541,7 +550,7 @@ void ValidationState_t::RegisterInstruction(Instruction* inst) {
if (inst->id()) all_definitions_.insert(std::make_pair(inst->id(), inst));
// Some validation checks are easier by getting all the consumers
- for (uint16_t i = 0; i < inst->operands().size(); ++i) {
+ for (size_t i = 0; i < inst->operands().size(); ++i) {
const spv_parsed_operand_t& operand = inst->operand(i);
if ((SPV_OPERAND_TYPE_ID == operand.type) ||
(SPV_OPERAND_TYPE_TYPE_ID == operand.type)) {
@@ -599,7 +608,8 @@ void ValidationState_t::RegisterStorageClassConsumer(
std::string errorVUID = VkErrorID(4644);
function(consumer->function()->id())
->RegisterExecutionModelLimitation([errorVUID](
- SpvExecutionModel model, std::string* message) {
+ SpvExecutionModel model,
+ std::string* message) {
if (model == SpvExecutionModelGLCompute ||
model == SpvExecutionModelRayGenerationKHR ||
model == SpvExecutionModelIntersectionKHR ||
@@ -625,10 +635,13 @@ void ValidationState_t::RegisterStorageClassConsumer(
std::string errorVUID = VkErrorID(4645);
function(consumer->function()->id())
->RegisterExecutionModelLimitation([errorVUID](
- SpvExecutionModel model, std::string* message) {
+ SpvExecutionModel model,
+ std::string* message) {
if (model != SpvExecutionModelGLCompute &&
model != SpvExecutionModelTaskNV &&
- model != SpvExecutionModelMeshNV) {
+ model != SpvExecutionModelMeshNV &&
+ model != SpvExecutionModelTaskEXT &&
+ model != SpvExecutionModelMeshEXT) {
if (message) {
*message =
errorVUID +
@@ -641,6 +654,118 @@ void ValidationState_t::RegisterStorageClassConsumer(
});
}
}
+
+ if (storage_class == SpvStorageClassCallableDataKHR) {
+ std::string errorVUID = VkErrorID(4704);
+ function(consumer->function()->id())
+ ->RegisterExecutionModelLimitation([errorVUID](SpvExecutionModel model,
+ std::string* message) {
+ if (model != SpvExecutionModelRayGenerationKHR &&
+ model != SpvExecutionModelClosestHitKHR &&
+ model != SpvExecutionModelCallableKHR &&
+ model != SpvExecutionModelMissKHR) {
+ if (message) {
+ *message = errorVUID +
+ "CallableDataKHR Storage Class is limited to "
+ "RayGenerationKHR, ClosestHitKHR, CallableKHR, and "
+ "MissKHR execution model";
+ }
+ return false;
+ }
+ return true;
+ });
+ } else if (storage_class == SpvStorageClassIncomingCallableDataKHR) {
+ std::string errorVUID = VkErrorID(4705);
+ function(consumer->function()->id())
+ ->RegisterExecutionModelLimitation([errorVUID](SpvExecutionModel model,
+ std::string* message) {
+ if (model != SpvExecutionModelCallableKHR) {
+ if (message) {
+ *message = errorVUID +
+ "IncomingCallableDataKHR Storage Class is limited to "
+ "CallableKHR execution model";
+ }
+ return false;
+ }
+ return true;
+ });
+ } else if (storage_class == SpvStorageClassRayPayloadKHR) {
+ std::string errorVUID = VkErrorID(4698);
+ function(consumer->function()->id())
+ ->RegisterExecutionModelLimitation([errorVUID](SpvExecutionModel model,
+ std::string* message) {
+ if (model != SpvExecutionModelRayGenerationKHR &&
+ model != SpvExecutionModelClosestHitKHR &&
+ model != SpvExecutionModelMissKHR) {
+ if (message) {
+ *message =
+ errorVUID +
+ "RayPayloadKHR Storage Class is limited to RayGenerationKHR, "
+ "ClosestHitKHR, and MissKHR execution model";
+ }
+ return false;
+ }
+ return true;
+ });
+ } else if (storage_class == SpvStorageClassHitAttributeKHR) {
+ std::string errorVUID = VkErrorID(4701);
+ function(consumer->function()->id())
+ ->RegisterExecutionModelLimitation(
+ [errorVUID](SpvExecutionModel model, std::string* message) {
+ if (model != SpvExecutionModelIntersectionKHR &&
+ model != SpvExecutionModelAnyHitKHR &&
+ model != SpvExecutionModelClosestHitKHR) {
+ if (message) {
+ *message = errorVUID +
+ "HitAttributeKHR Storage Class is limited to "
+ "IntersectionKHR, AnyHitKHR, sand ClosestHitKHR "
+ "execution model";
+ }
+ return false;
+ }
+ return true;
+ });
+ } else if (storage_class == SpvStorageClassIncomingRayPayloadKHR) {
+ std::string errorVUID = VkErrorID(4699);
+ function(consumer->function()->id())
+ ->RegisterExecutionModelLimitation(
+ [errorVUID](SpvExecutionModel model, std::string* message) {
+ if (model != SpvExecutionModelAnyHitKHR &&
+ model != SpvExecutionModelClosestHitKHR &&
+ model != SpvExecutionModelMissKHR) {
+ if (message) {
+ *message =
+ errorVUID +
+ "IncomingRayPayloadKHR Storage Class is limited to "
+ "AnyHitKHR, ClosestHitKHR, and MissKHR execution model";
+ }
+ return false;
+ }
+ return true;
+ });
+ } else if (storage_class == SpvStorageClassShaderRecordBufferKHR) {
+ std::string errorVUID = VkErrorID(7119);
+ function(consumer->function()->id())
+ ->RegisterExecutionModelLimitation(
+ [errorVUID](SpvExecutionModel model, std::string* message) {
+ if (model != SpvExecutionModelRayGenerationKHR &&
+ model != SpvExecutionModelIntersectionKHR &&
+ model != SpvExecutionModelAnyHitKHR &&
+ model != SpvExecutionModelClosestHitKHR &&
+ model != SpvExecutionModelCallableKHR &&
+ model != SpvExecutionModelMissKHR) {
+ if (message) {
+ *message =
+ errorVUID +
+ "ShaderRecordBufferKHR Storage Class is limited to "
+ "RayGenerationKHR, IntersectionKHR, AnyHitKHR, "
+ "ClosestHitKHR, CallableKHR, and MissKHR execution model";
+ }
+ return false;
+ }
+ return true;
+ });
+ }
}
uint32_t ValidationState_t::getIdBound() const { return id_bound_; }
@@ -968,6 +1093,11 @@ bool ValidationState_t::GetPointerTypeInfo(uint32_t id, uint32_t* data_type,
return true;
}
+bool ValidationState_t::IsAccelerationStructureType(uint32_t id) const {
+ const Instruction* inst = FindDef(id);
+ return inst && inst->opcode() == SpvOpTypeAccelerationStructureKHR;
+}
+
bool ValidationState_t::IsCooperativeMatrixType(uint32_t id) const {
const Instruction* inst = FindDef(id);
return inst && inst->opcode() == SpvOpTypeCooperativeMatrixNV;
@@ -988,6 +1118,13 @@ bool ValidationState_t::IsUnsignedIntCooperativeMatrixType(uint32_t id) const {
return IsUnsignedIntScalarType(FindDef(id)->word(2));
}
+// Either a 32 bit 2-component uint vector or a 64 bit uint scalar
+bool ValidationState_t::IsUnsigned64BitHandle(uint32_t id) const {
+ return ((IsUnsignedIntScalarType(id) && GetBitWidth(id) == 64) ||
+ (IsUnsignedIntVectorType(id) && GetDimension(id) == 2 &&
+ GetBitWidth(id) == 32));
+}
+
spv_result_t ValidationState_t::CooperativeMatrixShapesMatch(
const Instruction* inst, uint32_t m1, uint32_t m2) {
const auto m1_type = FindDef(m1);
@@ -1389,6 +1526,7 @@ bool ValidationState_t::IsValidStorageClass(
case SpvStorageClassCallableDataKHR:
case SpvStorageClassIncomingCallableDataKHR:
case SpvStorageClassShaderRecordBufferKHR:
+ case SpvStorageClassTaskPayloadWorkgroupEXT:
return true;
default:
return false;
@@ -1414,6 +1552,18 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
// Clang format adds spaces between hyphens
// clang-format off
switch (id) {
+ case 4154:
+ return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04154);
+ case 4155:
+ return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04155);
+ case 4156:
+ return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04156);
+ case 4160:
+ return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04160);
+ case 4161:
+ return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04161);
+ case 4162:
+ return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04162);
case 4181:
return VUID_WRAP(VUID-BaseInstance-BaseInstance-04181);
case 4182:
@@ -1446,6 +1596,12 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
return VUID_WRAP(VUID-CullDistance-CullDistance-04199);
case 4200:
return VUID_WRAP(VUID-CullDistance-CullDistance-04200);
+ case 6735:
+ return VUID_WRAP(VUID-CullMaskKHR-CullMaskKHR-06735); // Execution Model
+ case 6736:
+ return VUID_WRAP(VUID-CullMaskKHR-CullMaskKHR-06736); // input storage
+ case 6737:
+ return VUID_WRAP(VUID-CullMaskKHR-CullMaskKHR-06737); // 32 int scalar
case 4205:
return VUID_WRAP(VUID-DeviceIndex-DeviceIndex-04205);
case 4206:
@@ -1814,8 +1970,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
return VUID_WRAP(VUID-StandaloneSpirv-None-04637);
case 4638:
return VUID_WRAP(VUID-StandaloneSpirv-None-04638);
- case 4639:
- return VUID_WRAP(VUID-StandaloneSpirv-None-04639);
+ case 7321:
+ return VUID_WRAP(VUID-StandaloneSpirv-None-07321);
case 4640:
return VUID_WRAP(VUID-StandaloneSpirv-None-04640);
case 4641:
@@ -1862,6 +2018,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
return VUID_WRAP(VUID-StandaloneSpirv-FPRoundingMode-04675);
case 4677:
return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677);
+ case 4680:
+ return VUID_WRAP(VUID-StandaloneSpirv-OpTypeRuntimeArray-04680);
case 4682:
return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682);
case 6426:
@@ -1870,6 +2028,22 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685);
case 4686:
return VUID_WRAP(VUID-StandaloneSpirv-None-04686);
+ case 4698:
+ return VUID_WRAP(VUID-StandaloneSpirv-RayPayloadKHR-04698);
+ case 4699:
+ return VUID_WRAP(VUID-StandaloneSpirv-IncomingRayPayloadKHR-04699);
+ case 4701:
+ return VUID_WRAP(VUID-StandaloneSpirv-HitAttributeKHR-04701);
+ case 4703:
+ return VUID_WRAP(VUID-StandaloneSpirv-HitAttributeKHR-04703);
+ case 4704:
+ return VUID_WRAP(VUID-StandaloneSpirv-CallableDataKHR-04704);
+ case 4705:
+ return VUID_WRAP(VUID-StandaloneSpirv-IncomingCallableDataKHR-04705);
+ case 7119:
+ return VUID_WRAP(VUID-StandaloneSpirv-ShaderRecordBufferKHR-07119);
+ case 4708:
+ return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708);
case 4710:
return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04710);
case 4711:
@@ -1882,8 +2056,16 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04732);
case 4733:
return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04733);
+ case 4734:
+ return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04734);
+ case 4744:
+ return VUID_WRAP(VUID-StandaloneSpirv-Flat-04744);
+ case 4777:
+ return VUID_WRAP(VUID-StandaloneSpirv-OpImage-04777);
case 4780:
return VUID_WRAP(VUID-StandaloneSpirv-Result-04780);
+ case 4781:
+ return VUID_WRAP(VUID-StandaloneSpirv-Base-04781);
case 4915:
return VUID_WRAP(VUID-StandaloneSpirv-Location-04915);
case 4916:
@@ -1894,6 +2076,44 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
return VUID_WRAP(VUID-StandaloneSpirv-Location-04918);
case 4919:
return VUID_WRAP(VUID-StandaloneSpirv-Location-04919);
+ case 6201:
+ return VUID_WRAP(VUID-StandaloneSpirv-Flat-06201);
+ case 6202:
+ return VUID_WRAP(VUID-StandaloneSpirv-Flat-06202);
+ case 6214:
+ return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06214);
+ case 6491:
+ return VUID_WRAP(VUID-StandaloneSpirv-DescriptorSet-06491);
+ case 6671:
+ return VUID_WRAP(VUID-StandaloneSpirv-OpTypeSampledImage-06671);
+ case 6672:
+ return VUID_WRAP(VUID-StandaloneSpirv-Location-06672);
+ case 6674:
+ return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-06674);
+ case 6675:
+ return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06675);
+ case 6676:
+ return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06676);
+ case 6677:
+ return VUID_WRAP(VUID-StandaloneSpirv-UniformConstant-06677);
+ case 6678:
+ return VUID_WRAP(VUID-StandaloneSpirv-InputAttachmentIndex-06678);
+ case 6777:
+ return VUID_WRAP(VUID-StandaloneSpirv-PerVertexKHR-06777);
+ case 6778:
+ return VUID_WRAP(VUID-StandaloneSpirv-Input-06778);
+ case 6807:
+ return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06807);
+ case 6808:
+ return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06808);
+ case 6925:
+ return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06925);
+ case 6997:
+ return VUID_WRAP(VUID-StandaloneSpirv-SubgroupVoteKHR-06997);
+ case 7320:
+ return VUID_WRAP(VUID-StandaloneSpirv-ExecutionModel-07320);
+ case 7290:
+ return VUID_WRAP(VUID-StandaloneSpirv-Input-07290);
default:
return ""; // unknown id
}
diff --git a/source/val/validation_state.h b/source/val/validation_state.h
index 89834a0d..1b599ff3 100644
--- a/source/val/validation_state.h
+++ b/source/val/validation_state.h
@@ -44,19 +44,20 @@ namespace val {
/// of the SPIRV spec for additional details of the order. The enumerant values
/// are in the same order as the vector returned by GetModuleOrder
enum ModuleLayoutSection {
- kLayoutCapabilities, /// < Section 2.4 #1
- kLayoutExtensions, /// < Section 2.4 #2
- kLayoutExtInstImport, /// < Section 2.4 #3
- kLayoutMemoryModel, /// < Section 2.4 #4
- kLayoutEntryPoint, /// < Section 2.4 #5
- kLayoutExecutionMode, /// < Section 2.4 #6
- kLayoutDebug1, /// < Section 2.4 #7 > 1
- kLayoutDebug2, /// < Section 2.4 #7 > 2
- kLayoutDebug3, /// < Section 2.4 #7 > 3
- kLayoutAnnotations, /// < Section 2.4 #8
- kLayoutTypes, /// < Section 2.4 #9
- kLayoutFunctionDeclarations, /// < Section 2.4 #10
- kLayoutFunctionDefinitions /// < Section 2.4 #11
+ kLayoutCapabilities, /// < Section 2.4 #1
+ kLayoutExtensions, /// < Section 2.4 #2
+ kLayoutExtInstImport, /// < Section 2.4 #3
+ kLayoutMemoryModel, /// < Section 2.4 #4
+ kLayoutSamplerImageAddressMode, /// < Section 2.4 #5
+ kLayoutEntryPoint, /// < Section 2.4 #6
+ kLayoutExecutionMode, /// < Section 2.4 #7
+ kLayoutDebug1, /// < Section 2.4 #8 > 1
+ kLayoutDebug2, /// < Section 2.4 #8 > 2
+ kLayoutDebug3, /// < Section 2.4 #8 > 3
+ kLayoutAnnotations, /// < Section 2.4 #9
+ kLayoutTypes, /// < Section 2.4 #10
+ kLayoutFunctionDeclarations, /// < Section 2.4 #11
+ kLayoutFunctionDefinitions /// < Section 2.4 #12
};
/// This class manages the state of the SPIR-V validation as it is being parsed.
@@ -70,11 +71,9 @@ class ValidationState_t {
// and its values to be used without
// requiring any capability
- // Allow functionalities enabled by VariablePointers capability.
+ // Allow functionalities enabled by VariablePointers or
+ // VariablePointersStorageBuffer capability.
bool variable_pointers = false;
- // Allow functionalities enabled by VariablePointersStorageBuffer
- // capability.
- bool variable_pointers_storage_buffer = false;
// Permit group oerations Reduce, InclusiveScan, ExclusiveScan
bool group_ops_reduce_and_scans = false;
@@ -362,6 +361,20 @@ class ValidationState_t {
/// Returns the memory model of this module, or Simple if uninitialized.
SpvMemoryModel memory_model() const;
+ /// Sets the bit width for sampler/image type variables. If not set, they are
+ /// considered opaque
+ void set_samplerimage_variable_address_mode(uint32_t bit_width);
+
+ /// Get the addressing mode currently set. If 0, it means addressing mode is
+ /// invalid Sampler/Image type variables must be considered opaque This mode
+ /// is only valid after the instruction has been read
+ uint32_t samplerimage_variable_address_mode() const;
+
+ /// Returns true if the OpSamplerImageAddressingModeNV was found.
+ bool has_samplerimage_variable_address_mode_specified() const {
+ return sampler_image_addressing_mode_ != 0;
+ }
+
const AssemblyGrammar& grammar() const { return grammar_; }
/// Inserts the instruction into the list of ordered instructions in the file.
@@ -377,17 +390,14 @@ class ValidationState_t {
/// Registers the decoration for the given <id>
void RegisterDecorationForId(uint32_t id, const Decoration& dec) {
auto& dec_list = id_decorations_[id];
- auto lb = std::find(dec_list.begin(), dec_list.end(), dec);
- if (lb == dec_list.end()) {
- dec_list.push_back(dec);
- }
+ dec_list.insert(dec);
}
/// Registers the list of decorations for the given <id>
template <class InputIt>
void RegisterDecorationsForId(uint32_t id, InputIt begin, InputIt end) {
- std::vector<Decoration>& cur_decs = id_decorations_[id];
- cur_decs.insert(cur_decs.end(), begin, end);
+ std::set<Decoration>& cur_decs = id_decorations_[id];
+ cur_decs.insert(begin, end);
}
/// Registers the list of decorations for the given member of the given
@@ -396,21 +406,44 @@ class ValidationState_t {
void RegisterDecorationsForStructMember(uint32_t struct_id,
uint32_t member_index, InputIt begin,
InputIt end) {
- RegisterDecorationsForId(struct_id, begin, end);
- for (auto& decoration : id_decorations_[struct_id]) {
- decoration.set_struct_member_index(member_index);
+ std::set<Decoration>& cur_decs = id_decorations_[struct_id];
+ for (InputIt iter = begin; iter != end; ++iter) {
+ Decoration dec = *iter;
+ dec.set_struct_member_index(member_index);
+ cur_decs.insert(dec);
}
}
/// Returns all the decorations for the given <id>. If no decorations exist
- /// for the <id>, it registers an empty vector for it in the map and
- /// returns the empty vector.
- std::vector<Decoration>& id_decorations(uint32_t id) {
+ /// for the <id>, it registers an empty set for it in the map and
+ /// returns the empty set.
+ std::set<Decoration>& id_decorations(uint32_t id) {
return id_decorations_[id];
}
+ /// Returns the range of decorations for the given field of the given <id>.
+ struct FieldDecorationsIter {
+ std::set<Decoration>::const_iterator begin;
+ std::set<Decoration>::const_iterator end;
+ };
+ FieldDecorationsIter id_member_decorations(uint32_t id,
+ uint32_t member_index) {
+ const auto& decorations = id_decorations_[id];
+
+ // The decorations are sorted by member_index, so this look up will give the
+ // exact range of decorations for this member index.
+ Decoration min_decoration((SpvDecoration)0, {}, member_index);
+ Decoration max_decoration(SpvDecorationMax, {}, member_index);
+
+ FieldDecorationsIter result;
+ result.begin = decorations.lower_bound(min_decoration);
+ result.end = decorations.upper_bound(max_decoration);
+
+ return result;
+ }
+
// Returns const pointer to the internal decoration container.
- const std::map<uint32_t, std::vector<Decoration>>& id_decorations() const {
+ const std::map<uint32_t, std::set<Decoration>>& id_decorations() const {
return id_decorations_;
}
@@ -574,10 +607,12 @@ class ValidationState_t {
bool IsBoolVectorType(uint32_t id) const;
bool IsBoolScalarOrVectorType(uint32_t id) const;
bool IsPointerType(uint32_t id) const;
+ bool IsAccelerationStructureType(uint32_t id) const;
bool IsCooperativeMatrixType(uint32_t id) const;
bool IsFloatCooperativeMatrixType(uint32_t id) const;
bool IsIntCooperativeMatrixType(uint32_t id) const;
bool IsUnsignedIntCooperativeMatrixType(uint32_t id) const;
+ bool IsUnsigned64BitHandle(uint32_t id) const;
// Returns true if |id| is a type id that contains |type| (or integer or
// floating point type) of |width| bits.
@@ -688,6 +723,16 @@ class ValidationState_t {
// Returns the disassembly string for the given instruction.
std::string Disassemble(const uint32_t* words, uint16_t num_words) const;
+ // Returns the string name for |decoration|.
+ std::string SpvDecorationString(uint32_t decoration) {
+ spv_operand_desc desc = nullptr;
+ if (grammar_.lookupOperand(SPV_OPERAND_TYPE_DECORATION, decoration,
+ &desc) != SPV_SUCCESS) {
+ return std::string("Unknown");
+ }
+ return std::string(desc->name);
+ }
+
// Returns whether type m1 and type m2 are cooperative matrices with
// the same "shape" (matching scope, rows, cols). If any are specialization
// constants, we assume they can match because we can't prove they don't.
@@ -828,7 +873,7 @@ class ValidationState_t {
struct_has_nested_blockorbufferblock_struct_;
/// Stores the list of decorations for a given <id>
- std::map<uint32_t, std::vector<Decoration>> id_decorations_;
+ std::map<uint32_t, std::set<Decoration>> id_decorations_;
/// Stores type declarations which need to be unique (i.e. non-aggregates),
/// in the form [opcode, operand words], result_id is not stored.
@@ -844,7 +889,10 @@ class ValidationState_t {
// have the same pointer size (for physical pointer types).
uint32_t pointer_size_and_alignment_;
- /// NOTE: See corresponding getter functions
+ /// bit width of sampler/image type variables. Valid values are 32 and 64
+ uint32_t sampler_image_addressing_mode_;
+
+ /// NOTE: See correspoding getter functions
bool in_function_;
/// The state of optional features. These are determined by capabilities
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index e88df04d..4ca8ef8f 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -185,6 +185,7 @@ add_spvtools_unittest(
endif()
+add_subdirectory(diff)
add_subdirectory(link)
add_subdirectory(lint)
add_subdirectory(opt)
diff --git a/test/binary_to_text_test.cpp b/test/binary_to_text_test.cpp
index df703e5d..44705f2a 100644
--- a/test/binary_to_text_test.cpp
+++ b/test/binary_to_text_test.cpp
@@ -101,6 +101,17 @@ TEST_F(BinaryToText, Default) {
spvTextDestroy(text);
}
+TEST_F(BinaryToText, Print) {
+ spv_text text = nullptr;
+ spv_diagnostic diagnostic = nullptr;
+ ASSERT_EQ(
+ SPV_SUCCESS,
+ spvBinaryToText(context, binary->code, binary->wordCount,
+ SPV_BINARY_TO_TEXT_OPTION_PRINT, &text, &diagnostic));
+ ASSERT_EQ(text, nullptr);
+ spvTextDestroy(text);
+}
+
TEST_F(BinaryToText, MissingModule) {
spv_text text;
spv_diagnostic diagnostic = nullptr;
diff --git a/test/c_interface_test.cpp b/test/c_interface_test.cpp
index 1562057f..4424d7f5 100644
--- a/test/c_interface_test.cpp
+++ b/test/c_interface_test.cpp
@@ -122,7 +122,10 @@ TEST(CInterface, SpecifyConsumerNullDiagnosticForAssembling) {
EXPECT_EQ(1u, position.line);
EXPECT_EQ(0u, position.column);
EXPECT_EQ(12u, position.index);
- EXPECT_STREQ("Expected operand, found end of stream.", message);
+ EXPECT_STREQ(
+ "Expected operand for OpName instruction, but found the end of the "
+ "stream.",
+ message);
});
spv_binary binary = nullptr;
@@ -228,7 +231,10 @@ TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForAssembling) {
spvTextToBinary(context, input_text, sizeof(input_text), &binary,
&diagnostic));
EXPECT_EQ(0, invocation); // Consumer should not be invoked at all.
- EXPECT_STREQ("Expected operand, found end of stream.", diagnostic->error);
+ EXPECT_STREQ(
+ "Expected operand for OpName instruction, but found the end of the "
+ "stream.",
+ diagnostic->error);
spvDiagnosticDestroy(diagnostic);
spvBinaryDestroy(binary);
diff --git a/test/diff/CMakeLists.txt b/test/diff/CMakeLists.txt
new file mode 100644
index 00000000..811805bd
--- /dev/null
+++ b/test/diff/CMakeLists.txt
@@ -0,0 +1,26 @@
+# Copyright (c) 2022 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(diff_files/diff_test_files_autogen.cmake)
+
+add_spvtools_unittest(TARGET lcs
+ SRCS lcs_test.cpp
+ LIBS SPIRV-Tools-diff
+)
+
+add_spvtools_unittest(TARGET diff
+ SRCS diff_test.cpp diff_test_utils.h diff_test_utils.cpp
+ ${DIFF_TEST_FILES} ${spirv-tools_SOURCE_DIR}/tools/util/cli_consumer.cpp
+ LIBS SPIRV-Tools-diff
+)
diff --git a/test/diff/diff_files/.gitignore b/test/diff/diff_files/.gitignore
new file mode 100644
index 00000000..727526ef
--- /dev/null
+++ b/test/diff/diff_files/.gitignore
@@ -0,0 +1,3 @@
+# To aid debugging no-dbg variants, the temporary files used to strip debug information are placed
+# in a hidden directory and aren't removed after generation.
+.no_dbg/
diff --git a/test/diff/diff_files/OpExtInst_in_dst_only_autogen.cpp b/test/diff/diff_files/OpExtInst_in_dst_only_autogen.cpp
new file mode 100644
index 00000000..ce899edd
--- /dev/null
+++ b/test/diff/diff_files/OpExtInst_in_dst_only_autogen.cpp
@@ -0,0 +1,242 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests a diff where the src shader doesn't have OpExtImport while the
+// dst shader does (and uses OpExtInst). This test ensures that when matching,
+// the OpExtImport instruction from the correct module is referenced.
+constexpr char kSrc[] = R"( OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %9 "color"
+ OpName %11 "v"
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpCompositeConstruct %7 %12 %12 %12 %12
+ OpStore %9 %13
+ OpReturn
+ OpFunctionEnd
+)";
+constexpr char kDst[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %9 "color"
+ OpName %11 "v"
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ OpDecorate %14 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpExtInst %6 %1 Log2 %12
+ %14 = OpCompositeConstruct %7 %13 %13 %13 %13
+ OpStore %9 %14
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+TEST(DiffTest, OpextinstInDstOnly) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 14
++; Bound: 16
+ ; Schema: 0
+ OpCapability Shader
++%14 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %9 "color"
+ OpName %11 "v"
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
++OpDecorate %15 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
++%15 = OpExtInst %6 %14 Log2 %12
+-%13 = OpCompositeConstruct %7 %12 %12 %12 %12
++%13 = OpCompositeConstruct %7 %15 %15 %15 %15
+ OpStore %9 %13
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, OpextinstInDstOnlyNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpCompositeConstruct %7 %12 %12 %12 %12
+ OpStore %9 %13
+ OpReturn
+ OpFunctionEnd
+
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ OpDecorate %14 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpExtInst %6 %1 Log2 %12
+ %14 = OpCompositeConstruct %7 %13 %13 %13 %13
+ OpStore %9 %14
+ OpReturn
+ OpFunctionEnd
+
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 14
++; Bound: 16
+ ; Schema: 0
+ OpCapability Shader
++%14 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
++OpDecorate %15 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
++%15 = OpExtInst %6 %14 Log2 %12
+-%13 = OpCompositeConstruct %7 %12 %12 %12 %12
++%13 = OpCompositeConstruct %7 %15 %15 %15 %15
+ OpStore %9 %13
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/OpExtInst_in_dst_only_dst.spvasm b/test/diff/diff_files/OpExtInst_in_dst_only_dst.spvasm
new file mode 100644
index 00000000..e599d1e9
--- /dev/null
+++ b/test/diff/diff_files/OpExtInst_in_dst_only_dst.spvasm
@@ -0,0 +1,33 @@
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %9 "color"
+ OpName %11 "v"
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ OpDecorate %14 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpExtInst %6 %1 Log2 %12
+ %14 = OpCompositeConstruct %7 %13 %13 %13 %13
+ OpStore %9 %14
+ OpReturn
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/OpExtInst_in_dst_only_src.spvasm b/test/diff/diff_files/OpExtInst_in_dst_only_src.spvasm
new file mode 100644
index 00000000..9f6fc18c
--- /dev/null
+++ b/test/diff/diff_files/OpExtInst_in_dst_only_src.spvasm
@@ -0,0 +1,33 @@
+;; Tests a diff where the src shader doesn't have OpExtImport while the
+;; dst shader does (and uses OpExtInst). This test ensures that when matching,
+;; the OpExtImport instruction from the correct module is referenced.
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %9 "color"
+ OpName %11 "v"
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpCompositeConstruct %7 %12 %12 %12 %12
+ OpStore %9 %13
+ OpReturn
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp b/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp
new file mode 100644
index 00000000..9944c2cf
--- /dev/null
+++ b/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp
@@ -0,0 +1,242 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests a diff where the dst shader doesn't have OpExtImport while the
+// src shader does (and uses OpExtInst). This test ensures that when matching,
+// the OpExtImport instruction from the correct module is referenced.
+constexpr char kSrc[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %9 "color"
+ OpName %11 "v"
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ OpDecorate %14 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpExtInst %6 %1 Log2 %12
+ %14 = OpCompositeConstruct %7 %13 %13 %13 %13
+ OpStore %9 %14
+ OpReturn
+ OpFunctionEnd
+)";
+constexpr char kDst[] = R"( OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %9 "color"
+ OpName %11 "v"
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpCompositeConstruct %7 %12 %12 %12 %12
+ OpStore %9 %13
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+TEST(DiffTest, OpextinstInSrcOnly) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 15
++; Bound: 16
+ ; Schema: 0
+ OpCapability Shader
+-%1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %9 "color"
+ OpName %11 "v"
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+-OpDecorate %13 RelaxedPrecision
+ OpDecorate %14 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+-%13 = OpExtInst %6 %1 Log2 %12
+-%14 = OpCompositeConstruct %7 %13 %13 %13 %13
++%14 = OpCompositeConstruct %7 %12 %12 %12 %12
+ OpStore %9 %14
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, OpextinstInSrcOnlyNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ OpDecorate %14 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpExtInst %6 %1 Log2 %12
+ %14 = OpCompositeConstruct %7 %13 %13 %13 %13
+ OpStore %9 %14
+ OpReturn
+ OpFunctionEnd
+
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpCompositeConstruct %7 %12 %12 %12 %12
+ OpStore %9 %13
+ OpReturn
+ OpFunctionEnd
+
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 15
++; Bound: 16
+ ; Schema: 0
+ OpCapability Shader
+-%1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+-OpDecorate %13 RelaxedPrecision
+ OpDecorate %14 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+-%13 = OpExtInst %6 %1 Log2 %12
+-%14 = OpCompositeConstruct %7 %13 %13 %13 %13
++%14 = OpCompositeConstruct %7 %12 %12 %12 %12
+ OpStore %9 %14
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/OpExtInst_in_src_only_dst.spvasm b/test/diff/diff_files/OpExtInst_in_src_only_dst.spvasm
new file mode 100644
index 00000000..47233051
--- /dev/null
+++ b/test/diff/diff_files/OpExtInst_in_src_only_dst.spvasm
@@ -0,0 +1,30 @@
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %9 "color"
+ OpName %11 "v"
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpCompositeConstruct %7 %12 %12 %12 %12
+ OpStore %9 %13
+ OpReturn
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/OpExtInst_in_src_only_src.spvasm b/test/diff/diff_files/OpExtInst_in_src_only_src.spvasm
new file mode 100644
index 00000000..efb663a9
--- /dev/null
+++ b/test/diff/diff_files/OpExtInst_in_src_only_src.spvasm
@@ -0,0 +1,36 @@
+;; Tests a diff where the dst shader doesn't have OpExtImport while the
+;; src shader does (and uses OpExtInst). This test ensures that when matching,
+;; the OpExtImport instruction from the correct module is referenced.
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %9 "color"
+ OpName %11 "v"
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ OpDecorate %14 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpExtInst %6 %1 Log2 %12
+ %14 = OpCompositeConstruct %7 %13 %13 %13 %13
+ OpStore %9 %14
+ OpReturn
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/OpTypeForwardPointer_basic_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_basic_autogen.cpp
new file mode 100644
index 00000000..af252b11
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_basic_autogen.cpp
@@ -0,0 +1,136 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Basic test that OpTypeForwardPointer is matched
+constexpr char kSrc[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %structptr "structptr"
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1)";
+constexpr char kDst[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %structptr "structptr"
+ OpName %structptr2 "structptr2"
+ OpTypeForwardPointer %structptr UniformConstant
+ OpTypeForwardPointer %structptr2 Function
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
+ %structptr2 = OpTypePointer Function %structt1
+)";
+
+TEST(DiffTest, OptypeforwardpointerBasic) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 7
++; Bound: 8
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %1 "structptr"
++OpName %7 "structptr2"
+ OpTypeForwardPointer %1 UniformConstant
++OpTypeForwardPointer %7 Function
+ %2 = OpTypeInt 32 0
+ %3 = OpTypeStruct %1 %2
+ %4 = OpTypeStruct %2 %1
+ %5 = OpTypeStruct %2 %2 %1
+ %6 = OpTypeStruct %2 %2 %2 %1
+ %1 = OpTypePointer UniformConstant %3
++%7 = OpTypePointer Function %3
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, OptypeforwardpointerBasicNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %structptr UniformConstant
+ OpTypeForwardPointer %structptr2 Function
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
+ %structptr2 = OpTypePointer Function %structt1
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 7
++; Bound: 8
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %1 UniformConstant
++OpTypeForwardPointer %7 Function
+ %2 = OpTypeInt 32 0
+ %3 = OpTypeStruct %1 %2
+ %4 = OpTypeStruct %2 %1
+ %5 = OpTypeStruct %2 %2 %1
+ %6 = OpTypeStruct %2 %2 %2 %1
+ %1 = OpTypePointer UniformConstant %3
++%7 = OpTypePointer Function %3
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/OpTypeForwardPointer_basic_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_basic_dst.spvasm
new file mode 100644
index 00000000..0c6e0cbe
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_basic_dst.spvasm
@@ -0,0 +1,15 @@
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %structptr "structptr"
+ OpName %structptr2 "structptr2"
+ OpTypeForwardPointer %structptr UniformConstant
+ OpTypeForwardPointer %structptr2 Function
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
+ %structptr2 = OpTypePointer Function %structt1
diff --git a/test/diff/diff_files/OpTypeForwardPointer_basic_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_basic_src.spvasm
new file mode 100644
index 00000000..408ec983
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_basic_src.spvasm
@@ -0,0 +1,13 @@
+;; Basic test that OpTypeForwardPointer is matched
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %structptr "structptr"
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
diff --git a/test/diff/diff_files/OpTypeForwardPointer_intertwined_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_intertwined_autogen.cpp
new file mode 100644
index 00000000..f2c9008e
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_intertwined_autogen.cpp
@@ -0,0 +1,138 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests that two forwarded types whose declarations are intertwined match
+// correctly
+constexpr char kSrc[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpName %Bptr "Bptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ OpTypeForwardPointer %Bptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint %Bptr
+ %B = OpTypeStruct %uint %Aptr %Bptr
+ %Aptr = OpTypePointer UniformConstant %A
+ %Bptr = OpTypePointer UniformConstant %B)";
+constexpr char kDst[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpName %Bptr "Bptr"
+ OpTypeForwardPointer %Bptr UniformConstant
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %B = OpTypeStruct %uint %Aptr %Bptr %uint
+ %A = OpTypeStruct %Aptr %uint %Bptr
+ %Aptr = OpTypePointer UniformConstant %A
+ %Bptr = OpTypePointer UniformConstant %B
+)";
+
+TEST(DiffTest, OptypeforwardpointerIntertwined) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 6
++; Bound: 7
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %1 "Aptr"
+ OpName %2 "Bptr"
+ OpTypeForwardPointer %1 UniformConstant
+ OpTypeForwardPointer %2 UniformConstant
+ %3 = OpTypeInt 32 0
++%6 = OpTypeStruct %3 %1 %2 %3
+ %4 = OpTypeStruct %1 %3 %2
+-%5 = OpTypeStruct %3 %1 %2
+ %1 = OpTypePointer UniformConstant %4
+-%2 = OpTypePointer UniformConstant %5
++%2 = OpTypePointer UniformConstant %6
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, OptypeforwardpointerIntertwinedNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %Aptr UniformConstant
+ OpTypeForwardPointer %Bptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint %Bptr
+ %B = OpTypeStruct %uint %Aptr %Bptr
+ %Aptr = OpTypePointer UniformConstant %A
+ %Bptr = OpTypePointer UniformConstant %B
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %Bptr UniformConstant
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %B = OpTypeStruct %uint %Aptr %Bptr %uint
+ %A = OpTypeStruct %Aptr %uint %Bptr
+ %Aptr = OpTypePointer UniformConstant %A
+ %Bptr = OpTypePointer UniformConstant %B
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 6
++; Bound: 10
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+-OpTypeForwardPointer %1 UniformConstant
+-OpTypeForwardPointer %2 UniformConstant
++OpTypeForwardPointer %6 UniformConstant
++OpTypeForwardPointer %7 UniformConstant
+ %3 = OpTypeInt 32 0
+-%4 = OpTypeStruct %1 %3 %2
+-%5 = OpTypeStruct %3 %1 %2
+-%1 = OpTypePointer UniformConstant %4
+-%2 = OpTypePointer UniformConstant %5
++%8 = OpTypeStruct %3 %7 %6 %3
++%9 = OpTypeStruct %7 %3 %6
++%7 = OpTypePointer UniformConstant %9
++%6 = OpTypePointer UniformConstant %8
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/OpTypeForwardPointer_intertwined_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_intertwined_dst.spvasm
new file mode 100644
index 00000000..bd735010
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_intertwined_dst.spvasm
@@ -0,0 +1,13 @@
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpName %Bptr "Bptr"
+ OpTypeForwardPointer %Bptr UniformConstant
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %B = OpTypeStruct %uint %Aptr %Bptr %uint
+ %A = OpTypeStruct %Aptr %uint %Bptr
+ %Aptr = OpTypePointer UniformConstant %A
+ %Bptr = OpTypePointer UniformConstant %B
diff --git a/test/diff/diff_files/OpTypeForwardPointer_intertwined_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_intertwined_src.spvasm
new file mode 100644
index 00000000..8fdaf28f
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_intertwined_src.spvasm
@@ -0,0 +1,15 @@
+;; Tests that two forwarded types whose declarations are intertwined match
+;; correctly
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpName %Bptr "Bptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ OpTypeForwardPointer %Bptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint %Bptr
+ %B = OpTypeStruct %uint %Aptr %Bptr
+ %Aptr = OpTypePointer UniformConstant %A
+ %Bptr = OpTypePointer UniformConstant %B
diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp
new file mode 100644
index 00000000..0a59be32
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp
@@ -0,0 +1,116 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests that two forwarded type pointers with mismatching storage classes
+// aren't matched
+constexpr char kSrc[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer UniformConstant %A)";
+constexpr char kDst[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr Function
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer Function %A
+)";
+
+TEST(DiffTest, OptypeforwardpointerMismatchingClass) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 4
++; Bound: 6
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+-OpName %1 "Aptr"
++OpName %4 "Aptr"
+-OpTypeForwardPointer %1 UniformConstant
++OpTypeForwardPointer %4 Function
+ %2 = OpTypeInt 32 0
+-%3 = OpTypeStruct %1 %2
+-%1 = OpTypePointer UniformConstant %3
++%5 = OpTypeStruct %4 %2
++%4 = OpTypePointer Function %5
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, OptypeforwardpointerMismatchingClassNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer UniformConstant %A
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %Aptr Function
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer Function %A
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 4
++; Bound: 6
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+-OpTypeForwardPointer %1 UniformConstant
++OpTypeForwardPointer %4 Function
+ %2 = OpTypeInt 32 0
+-%3 = OpTypeStruct %1 %2
+-%1 = OpTypePointer UniformConstant %3
++%5 = OpTypeStruct %4 %2
++%4 = OpTypePointer Function %5
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_dst.spvasm
new file mode 100644
index 00000000..e874a0ca
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_dst.spvasm
@@ -0,0 +1,9 @@
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr Function
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer Function %A
diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_src.spvasm
new file mode 100644
index 00000000..8a339331
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_src.spvasm
@@ -0,0 +1,11 @@
+;; Tests that two forwarded type pointers with mismatching storage classes
+;; aren't matched
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer UniformConstant %A
diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp
new file mode 100644
index 00000000..0067cdf6
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp
@@ -0,0 +1,111 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests that two forwarded type pointers with mismatching types aren't matched
+constexpr char kSrc[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer UniformConstant %A)";
+constexpr char kDst[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %Aptr = OpTypePointer UniformConstant %uint
+)";
+
+TEST(DiffTest, OptypeforwardpointerMismatchingType) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 4
++; Bound: 5
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+-OpName %1 "Aptr"
++OpName %4 "Aptr"
+-OpTypeForwardPointer %1 UniformConstant
++OpTypeForwardPointer %4 UniformConstant
+ %2 = OpTypeInt 32 0
+-%3 = OpTypeStruct %1 %2
+-%1 = OpTypePointer UniformConstant %3
++%4 = OpTypePointer UniformConstant %2
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, OptypeforwardpointerMismatchingTypeNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer UniformConstant %A
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %Aptr = OpTypePointer UniformConstant %uint
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 4
++; Bound: 5
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+-OpTypeForwardPointer %1 UniformConstant
++OpTypeForwardPointer %4 UniformConstant
+ %2 = OpTypeInt 32 0
+-%3 = OpTypeStruct %1 %2
+-%1 = OpTypePointer UniformConstant %3
++%4 = OpTypePointer UniformConstant %2
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_dst.spvasm
new file mode 100644
index 00000000..ee3d35c0
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_dst.spvasm
@@ -0,0 +1,8 @@
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %Aptr = OpTypePointer UniformConstant %uint
diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_src.spvasm
new file mode 100644
index 00000000..a4596a07
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_src.spvasm
@@ -0,0 +1,10 @@
+;; Tests that two forwarded type pointers with mismatching types aren't matched
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %A = OpTypeStruct %Aptr %uint
+ %Aptr = OpTypePointer UniformConstant %A
diff --git a/test/diff/diff_files/OpTypeForwardPointer_nested_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_nested_autogen.cpp
new file mode 100644
index 00000000..d66c28ae
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_nested_autogen.cpp
@@ -0,0 +1,127 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests that two forwarded declarations match even if the type pointer is used
+// in a nested struct declaration, and in multiple places
+constexpr char kSrc[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %C = OpTypeStruct %Aptr %uint %Aptr
+ %B = OpTypeStruct %C %Aptr %uint
+ %A = OpTypeStruct %B %C %B
+ %Aptr = OpTypePointer UniformConstant %A)";
+constexpr char kDst[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %C = OpTypeStruct %Aptr %uint %Aptr
+ %B = OpTypeStruct %C %Aptr
+ %A = OpTypeStruct %B %C %B
+ %Aptr = OpTypePointer UniformConstant %A
+)";
+
+TEST(DiffTest, OptypeforwardpointerNested) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 6
++; Bound: 8
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %1 "Aptr"
+ OpTypeForwardPointer %1 UniformConstant
+ %2 = OpTypeInt 32 0
+ %3 = OpTypeStruct %1 %2 %1
+-%4 = OpTypeStruct %3 %1 %2
+-%5 = OpTypeStruct %4 %3 %4
++%6 = OpTypeStruct %3 %1
++%7 = OpTypeStruct %6 %3 %6
+-%1 = OpTypePointer UniformConstant %5
++%1 = OpTypePointer UniformConstant %7
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, OptypeforwardpointerNestedNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %C = OpTypeStruct %Aptr %uint %Aptr
+ %B = OpTypeStruct %C %Aptr %uint
+ %A = OpTypeStruct %B %C %B
+ %Aptr = OpTypePointer UniformConstant %A
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %C = OpTypeStruct %Aptr %uint %Aptr
+ %B = OpTypeStruct %C %Aptr
+ %A = OpTypeStruct %B %C %B
+ %Aptr = OpTypePointer UniformConstant %A
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 6
++; Bound: 8
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %1 UniformConstant
+ %2 = OpTypeInt 32 0
+ %3 = OpTypeStruct %1 %2 %1
+-%4 = OpTypeStruct %3 %1 %2
+-%5 = OpTypeStruct %4 %3 %4
++%6 = OpTypeStruct %3 %1
++%7 = OpTypeStruct %6 %3 %6
+-%1 = OpTypePointer UniformConstant %5
++%1 = OpTypePointer UniformConstant %7
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/OpTypeForwardPointer_nested_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_nested_dst.spvasm
new file mode 100644
index 00000000..e2483556
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_nested_dst.spvasm
@@ -0,0 +1,11 @@
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %C = OpTypeStruct %Aptr %uint %Aptr
+ %B = OpTypeStruct %C %Aptr
+ %A = OpTypeStruct %B %C %B
+ %Aptr = OpTypePointer UniformConstant %A
diff --git a/test/diff/diff_files/OpTypeForwardPointer_nested_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_nested_src.spvasm
new file mode 100644
index 00000000..035410e0
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_nested_src.spvasm
@@ -0,0 +1,13 @@
+;; Tests that two forwarded declarations match even if the type pointer is used
+;; in a nested struct declaration, and in multiple places
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %Aptr "Aptr"
+ OpTypeForwardPointer %Aptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %C = OpTypeStruct %Aptr %uint %Aptr
+ %B = OpTypeStruct %C %Aptr %uint
+ %A = OpTypeStruct %B %C %B
+ %Aptr = OpTypePointer UniformConstant %A
diff --git a/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp
new file mode 100644
index 00000000..df86fef2
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp
@@ -0,0 +1,124 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Test that OpTypeForwardPointer is matched when one SPIR-V doesn't have debug
+// info
+constexpr char kSrc[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %structptr "structptr"
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1)";
+constexpr char kDst[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
+)";
+
+TEST(DiffTest, OptypeforwardpointerOnesidedDebug) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 7
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+-OpName %1 "structptr"
+ OpTypeForwardPointer %1 UniformConstant
+ %2 = OpTypeInt 32 0
+ %3 = OpTypeStruct %1 %2
+ %4 = OpTypeStruct %2 %1
+ %5 = OpTypeStruct %2 %2 %1
+ %6 = OpTypeStruct %2 %2 %2 %1
+ %1 = OpTypePointer UniformConstant %3
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, OptypeforwardpointerOnesidedDebugNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 7
+ ; Schema: 0
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %1 UniformConstant
+ %2 = OpTypeInt 32 0
+ %3 = OpTypeStruct %1 %2
+ %4 = OpTypeStruct %2 %1
+ %5 = OpTypeStruct %2 %2 %1
+ %6 = OpTypeStruct %2 %2 %2 %1
+ %1 = OpTypePointer UniformConstant %3
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_dst.spvasm
new file mode 100644
index 00000000..7e25710e
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_dst.spvasm
@@ -0,0 +1,11 @@
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
diff --git a/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_src.spvasm
new file mode 100644
index 00000000..e949b272
--- /dev/null
+++ b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_src.spvasm
@@ -0,0 +1,14 @@
+;; Test that OpTypeForwardPointer is matched when one SPIR-V doesn't have debug
+;; info
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability Linkage
+ OpMemoryModel Logical OpenCL
+ OpName %structptr "structptr"
+ OpTypeForwardPointer %structptr UniformConstant
+ %uint = OpTypeInt 32 0
+ %structt1 = OpTypeStruct %structptr %uint
+ %structt2 = OpTypeStruct %uint %structptr
+ %structt3 = OpTypeStruct %uint %uint %structptr
+ %structt4 = OpTypeStruct %uint %uint %uint %structptr
+ %structptr = OpTypePointer UniformConstant %structt1
diff --git a/test/diff/diff_files/README.md b/test/diff/diff_files/README.md
new file mode 100644
index 00000000..5dcee25a
--- /dev/null
+++ b/test/diff/diff_files/README.md
@@ -0,0 +1,17 @@
+# Diff tests
+
+This directory contains files used to ensure correctness of the `spirv-diff` implementation. The
+`generate_tests.py` script takes `name_src.spvasm` and `name_dst.spvasm` (for each `name`) and
+produces unit test files in the form of `name_autogen.cpp`.
+
+The unit test files test the diff between the src and dst inputs, as well as between debug-stripped
+versions of those. Additionally, based on the `{variant}_TESTS` lists defined in
+`generate_tests.py`, extra unit tests are added to exercise different options of spirv-diff.
+
+New tests are added simply by placing a new `name_src.spvasm` and `name_dst.spvasm` pair in this
+directory and running `generate_tests.py`. Note that this script needs the path to the spirv-diff
+executable that is built.
+
+The `generate_tests.py` script additionally expects `name_src.spvasm` to include a heading where the
+purpose of the test is explained. This heading is parsed as a block of lines starting with `;;` at
+the top of the file.
diff --git a/test/diff/diff_files/basic_autogen.cpp b/test/diff/diff_files/basic_autogen.cpp
new file mode 100644
index 00000000..f3afc701
--- /dev/null
+++ b/test/diff/diff_files/basic_autogen.cpp
@@ -0,0 +1,407 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Basic test for spirv-diff
+constexpr char kSrc[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %22 "main" %4 %14 %19
+ OpSource GLSL 450
+ OpName %4 "_ua_position"
+ OpName %14 "ANGLEXfbPosition"
+ OpName %17 "gl_PerVertex"
+ OpMemberName %17 0 "gl_Position"
+OpMemberName %17 1 "gl_PointSize"
+OpMemberName %17 2 "gl_ClipDistance"
+ OpMemberName %17 3 "gl_CullDistance"
+ OpName %19 ""
+ OpName %22 "main"
+ OpDecorate %4 Location 0
+ OpDecorate %14 Location 0
+ OpMemberDecorate %17 1 RelaxedPrecision
+ OpMemberDecorate %17 0 BuiltIn Position
+ OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%6 = OpTypeInt 32 1
+%15 = OpConstant %5 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %6 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%14 = OpVariable %13 Output
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd)";
+constexpr char kDst[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 10
+; Bound: 28
+; Schema: 0
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %4 "main" %13 %17 %27
+OpSource GLSL 450
+OpName %4 "main"
+OpName %11 "gl_PerVertex"
+OpMemberName %11 0 "gl_Position"
+OpMemberName %11 1 "gl_PointSize"
+OpMemberName %11 2 "gl_ClipDistance"
+OpMemberName %11 3 "gl_CullDistance"
+OpName %13 ""
+OpName %17 "_ua_position"
+OpName %27 "ANGLEXfbPosition"
+OpMemberDecorate %11 0 BuiltIn Position
+OpMemberDecorate %11 1 BuiltIn PointSize
+OpMemberDecorate %11 2 BuiltIn ClipDistance
+OpMemberDecorate %11 3 BuiltIn CullDistance
+OpDecorate %11 Block
+OpDecorate %17 Location 0
+OpDecorate %27 Location 0
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%7 = OpTypeVector %6 4
+%8 = OpTypeInt 32 0
+%9 = OpConstant %8 1
+%10 = OpTypeArray %6 %9
+%11 = OpTypeStruct %7 %6 %10 %10
+%12 = OpTypePointer Output %11
+%13 = OpVariable %12 Output
+%14 = OpTypeInt 32 1
+%15 = OpConstant %14 0
+%16 = OpTypePointer Input %7
+%17 = OpVariable %16 Input
+%19 = OpTypePointer Output %7
+%27 = OpVariable %19 Output
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%18 = OpLoad %7 %17
+%20 = OpAccessChain %19 %13 %15
+OpStore %20 %18
+OpReturn
+OpFunctionEnd
+)";
+
+TEST(DiffTest, Basic) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 27
++; Bound: 36
+ ; Schema: 0
+ OpCapability Shader
++%27 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+-OpEntryPoint Vertex %22 "main" %4 %14 %19
++OpEntryPoint Vertex %22 "main" %19 %4 %14
+ OpSource GLSL 450
+ OpName %4 "_ua_position"
+ OpName %14 "ANGLEXfbPosition"
+ OpName %17 "gl_PerVertex"
+ OpMemberName %17 0 "gl_Position"
+ OpMemberName %17 1 "gl_PointSize"
+ OpMemberName %17 2 "gl_ClipDistance"
+ OpMemberName %17 3 "gl_CullDistance"
+ OpName %19 ""
+ OpName %22 "main"
+ OpDecorate %4 Location 0
+ OpDecorate %14 Location 0
+-OpMemberDecorate %17 1 RelaxedPrecision
+ OpMemberDecorate %17 0 BuiltIn Position
+ OpMemberDecorate %17 1 BuiltIn PointSize
+ OpMemberDecorate %17 2 BuiltIn ClipDistance
+ OpMemberDecorate %17 3 BuiltIn CullDistance
+ OpDecorate %17 Block
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %5 = OpTypeInt 32 0
+ %6 = OpTypeInt 32 1
+-%15 = OpConstant %5 8
+-%16 = OpTypeArray %1 %15
+-%17 = OpTypeStruct %2 %1 %16 %16
++%17 = OpTypeStruct %2 %1 %29 %29
++%28 = OpConstant %5 1
++%29 = OpTypeArray %1 %28
+ %20 = OpTypeVoid
+ %25 = OpConstant %6 0
+ %3 = OpTypePointer Input %2
+ %13 = OpTypePointer Output %2
+ %18 = OpTypePointer Output %17
+ %21 = OpTypeFunction %20
+ %4 = OpVariable %3 Input
+ %14 = OpVariable %13 Output
+ %19 = OpVariable %18 Output
+ %22 = OpFunction %20 None %21
+ %23 = OpLabel
+ %24 = OpLoad %2 %4
+ %26 = OpAccessChain %13 %19 %25
+ OpStore %26 %24
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, BasicNoDebug) {
+ constexpr char kSrcNoDebug[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %22 "main" %4 %14 %19
+ OpSource GLSL 450
+ OpDecorate %4 Location 0
+ OpDecorate %14 Location 0
+ OpMemberDecorate %17 1 RelaxedPrecision
+ OpMemberDecorate %17 0 BuiltIn Position
+ OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%6 = OpTypeInt 32 1
+%15 = OpConstant %5 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %6 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%14 = OpVariable %13 Output
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd
+)";
+ constexpr char kDstNoDebug[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 10
+; Bound: 28
+; Schema: 0
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %4 "main" %13 %17 %27
+OpSource GLSL 450
+OpMemberDecorate %11 0 BuiltIn Position
+OpMemberDecorate %11 1 BuiltIn PointSize
+OpMemberDecorate %11 2 BuiltIn ClipDistance
+OpMemberDecorate %11 3 BuiltIn CullDistance
+OpDecorate %11 Block
+OpDecorate %17 Location 0
+OpDecorate %27 Location 0
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%7 = OpTypeVector %6 4
+%8 = OpTypeInt 32 0
+%9 = OpConstant %8 1
+%10 = OpTypeArray %6 %9
+%11 = OpTypeStruct %7 %6 %10 %10
+%12 = OpTypePointer Output %11
+%13 = OpVariable %12 Output
+%14 = OpTypeInt 32 1
+%15 = OpConstant %14 0
+%16 = OpTypePointer Input %7
+%17 = OpVariable %16 Input
+%19 = OpTypePointer Output %7
+%27 = OpVariable %19 Output
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%18 = OpLoad %7 %17
+%20 = OpAccessChain %19 %13 %15
+OpStore %20 %18
+OpReturn
+OpFunctionEnd
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 27
++; Bound: 36
+ ; Schema: 0
+ OpCapability Shader
++%27 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+-OpEntryPoint Vertex %22 "main" %4 %14 %19
++OpEntryPoint Vertex %22 "main" %19 %4 %14
+ OpSource GLSL 450
+ OpDecorate %4 Location 0
+ OpDecorate %14 Location 0
+-OpMemberDecorate %17 1 RelaxedPrecision
+ OpMemberDecorate %17 0 BuiltIn Position
+ OpMemberDecorate %17 1 BuiltIn PointSize
+ OpMemberDecorate %17 2 BuiltIn ClipDistance
+ OpMemberDecorate %17 3 BuiltIn CullDistance
+ OpDecorate %17 Block
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %5 = OpTypeInt 32 0
+ %6 = OpTypeInt 32 1
+-%15 = OpConstant %5 8
+-%16 = OpTypeArray %1 %15
+-%17 = OpTypeStruct %2 %1 %16 %16
++%17 = OpTypeStruct %2 %1 %29 %29
++%28 = OpConstant %5 1
++%29 = OpTypeArray %1 %28
+ %20 = OpTypeVoid
+ %25 = OpConstant %6 0
+ %3 = OpTypePointer Input %2
+ %13 = OpTypePointer Output %2
+ %18 = OpTypePointer Output %17
+ %21 = OpTypeFunction %20
+ %4 = OpVariable %3 Input
+ %14 = OpVariable %13 Output
+ %19 = OpVariable %18 Output
+ %22 = OpFunction %20 None %21
+ %23 = OpLabel
+ %24 = OpLoad %2 %4
+ %26 = OpAccessChain %13 %19 %25
+ OpStore %26 %24
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+TEST(DiffTest, BasicDumpIds) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 27
++; Bound: 36
+ ; Schema: 0
+ OpCapability Shader
++%27 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+-OpEntryPoint Vertex %22 "main" %4 %14 %19
++OpEntryPoint Vertex %22 "main" %19 %4 %14
+ OpSource GLSL 450
+ OpName %4 "_ua_position"
+ OpName %14 "ANGLEXfbPosition"
+ OpName %17 "gl_PerVertex"
+ OpMemberName %17 0 "gl_Position"
+ OpMemberName %17 1 "gl_PointSize"
+ OpMemberName %17 2 "gl_ClipDistance"
+ OpMemberName %17 3 "gl_CullDistance"
+ OpName %19 ""
+ OpName %22 "main"
+ OpDecorate %4 Location 0
+ OpDecorate %14 Location 0
+-OpMemberDecorate %17 1 RelaxedPrecision
+ OpMemberDecorate %17 0 BuiltIn Position
+ OpMemberDecorate %17 1 BuiltIn PointSize
+ OpMemberDecorate %17 2 BuiltIn ClipDistance
+ OpMemberDecorate %17 3 BuiltIn CullDistance
+ OpDecorate %17 Block
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %5 = OpTypeInt 32 0
+ %6 = OpTypeInt 32 1
+-%15 = OpConstant %5 8
+-%16 = OpTypeArray %1 %15
+-%17 = OpTypeStruct %2 %1 %16 %16
++%17 = OpTypeStruct %2 %1 %29 %29
++%28 = OpConstant %5 1
++%29 = OpTypeArray %1 %28
+ %20 = OpTypeVoid
+ %25 = OpConstant %6 0
+ %3 = OpTypePointer Input %2
+ %13 = OpTypePointer Output %2
+ %18 = OpTypePointer Output %17
+ %21 = OpTypeFunction %20
+ %4 = OpVariable %3 Input
+ %14 = OpVariable %13 Output
+ %19 = OpVariable %18 Output
+ %22 = OpFunction %20 None %21
+ %23 = OpLabel
+ %24 = OpLoad %2 %4
+ %26 = OpAccessChain %13 %19 %25
+ OpStore %26 %24
+ OpReturn
+ OpFunctionEnd
+ Src -> Dst
+ 1 -> 6 [TypeFloat]
+ 2 -> 7 [TypeVector]
+ 3 -> 16 [TypePointer]
+ 4 -> 17 [Variable]
+ 5 -> 8 [TypeInt]
+ 6 -> 14 [TypeInt]
+ 13 -> 19 [TypePointer]
+ 14 -> 27 [Variable]
+ 15 -> 34 [Constant]
+ 16 -> 35 [TypeArray]
+ 17 -> 11 [TypeStruct]
+ 18 -> 12 [TypePointer]
+ 19 -> 13 [Variable]
+ 20 -> 2 [TypeVoid]
+ 21 -> 3 [TypeFunction]
+ 22 -> 4 [Function]
+ 23 -> 5 [Label]
+ 24 -> 18 [Load]
+ 25 -> 15 [Constant]
+ 26 -> 20 [AccessChain]
+)";
+ Options options;
+ options.dump_id_map = true;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/basic_dst.spvasm b/test/diff/diff_files/basic_dst.spvasm
new file mode 100644
index 00000000..79cb8887
--- /dev/null
+++ b/test/diff/diff_files/basic_dst.spvasm
@@ -0,0 +1,49 @@
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 10
+; Bound: 28
+; Schema: 0
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %4 "main" %13 %17 %27
+OpSource GLSL 450
+OpName %4 "main"
+OpName %11 "gl_PerVertex"
+OpMemberName %11 0 "gl_Position"
+OpMemberName %11 1 "gl_PointSize"
+OpMemberName %11 2 "gl_ClipDistance"
+OpMemberName %11 3 "gl_CullDistance"
+OpName %13 ""
+OpName %17 "_ua_position"
+OpName %27 "ANGLEXfbPosition"
+OpMemberDecorate %11 0 BuiltIn Position
+OpMemberDecorate %11 1 BuiltIn PointSize
+OpMemberDecorate %11 2 BuiltIn ClipDistance
+OpMemberDecorate %11 3 BuiltIn CullDistance
+OpDecorate %11 Block
+OpDecorate %17 Location 0
+OpDecorate %27 Location 0
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%7 = OpTypeVector %6 4
+%8 = OpTypeInt 32 0
+%9 = OpConstant %8 1
+%10 = OpTypeArray %6 %9
+%11 = OpTypeStruct %7 %6 %10 %10
+%12 = OpTypePointer Output %11
+%13 = OpVariable %12 Output
+%14 = OpTypeInt 32 1
+%15 = OpConstant %14 0
+%16 = OpTypePointer Input %7
+%17 = OpVariable %16 Input
+%19 = OpTypePointer Output %7
+%27 = OpVariable %19 Output
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%18 = OpLoad %7 %17
+%20 = OpAccessChain %19 %13 %15
+OpStore %20 %18
+OpReturn
+OpFunctionEnd
diff --git a/test/diff/diff_files/basic_src.spvasm b/test/diff/diff_files/basic_src.spvasm
new file mode 100644
index 00000000..c55ec7af
--- /dev/null
+++ b/test/diff/diff_files/basic_src.spvasm
@@ -0,0 +1,50 @@
+;; Basic test for spirv-diff
+; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %22 "main" %4 %14 %19
+ OpSource GLSL 450
+ OpName %4 "_ua_position"
+ OpName %14 "ANGLEXfbPosition"
+ OpName %17 "gl_PerVertex"
+ OpMemberName %17 0 "gl_Position"
+OpMemberName %17 1 "gl_PointSize"
+OpMemberName %17 2 "gl_ClipDistance"
+ OpMemberName %17 3 "gl_CullDistance"
+ OpName %19 ""
+ OpName %22 "main"
+ OpDecorate %4 Location 0
+ OpDecorate %14 Location 0
+ OpMemberDecorate %17 1 RelaxedPrecision
+ OpMemberDecorate %17 0 BuiltIn Position
+ OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%6 = OpTypeInt 32 1
+%15 = OpConstant %5 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %6 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%14 = OpVariable %13 Output
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd
diff --git a/test/diff/diff_files/constant_array_size_autogen.cpp b/test/diff/diff_files/constant_array_size_autogen.cpp
new file mode 100644
index 00000000..16975ff4
--- /dev/null
+++ b/test/diff/diff_files/constant_array_size_autogen.cpp
@@ -0,0 +1,306 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests that identical integer constants are matched when used as array size,
+// regardless of int or uint.
+constexpr char kSrc[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %22 "main" %4 %19
+OpSource GLSL 450
+OpName %4 "_ua_position"
+OpName %17 "gl_PerVertex"
+OpMemberName %17 0 "gl_Position"
+OpMemberName %17 1 "gl_PointSize"
+OpMemberName %17 2 "gl_ClipDistance"
+OpMemberName %17 3 "gl_CullDistance"
+OpName %19 ""
+OpName %22 "main"
+OpDecorate %4 Location 0
+OpMemberDecorate %17 1 RelaxedPrecision
+OpMemberDecorate %17 0 BuiltIn Position
+OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%8 = OpTypeVector %5 4
+%15 = OpConstant %5 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %5 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd)";
+constexpr char kDst[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %22 "main" %4 %19
+OpSource GLSL 450
+OpName %4 "_ua_position"
+OpName %17 "gl_PerVertex"
+OpMemberName %17 0 "gl_Position"
+OpMemberName %17 1 "gl_PointSize"
+OpMemberName %17 2 "gl_ClipDistance"
+OpMemberName %17 3 "gl_CullDistance"
+OpName %19 ""
+OpName %22 "main"
+OpDecorate %4 Location 0
+OpMemberDecorate %17 1 RelaxedPrecision
+OpMemberDecorate %17 0 BuiltIn Position
+OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%6 = OpTypeInt 32 1
+%8 = OpTypeVector %5 4
+%15 = OpConstant %6 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %5 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd
+)";
+
+TEST(DiffTest, ConstantArraySize) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 27
++; Bound: 34
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %22 "main" %4 %19
+ OpSource GLSL 450
+ OpName %4 "_ua_position"
+ OpName %17 "gl_PerVertex"
+ OpMemberName %17 0 "gl_Position"
+ OpMemberName %17 1 "gl_PointSize"
+ OpMemberName %17 2 "gl_ClipDistance"
+ OpMemberName %17 3 "gl_CullDistance"
+ OpName %19 ""
+ OpName %22 "main"
+ OpDecorate %4 Location 0
+ OpMemberDecorate %17 1 RelaxedPrecision
+ OpMemberDecorate %17 0 BuiltIn Position
+ OpMemberDecorate %17 1 BuiltIn PointSize
+ OpMemberDecorate %17 2 BuiltIn ClipDistance
+ OpMemberDecorate %17 3 BuiltIn CullDistance
+ OpDecorate %17 Block
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %5 = OpTypeInt 32 0
++%27 = OpTypeInt 32 1
+ %8 = OpTypeVector %5 4
+-%15 = OpConstant %5 8
++%15 = OpConstant %27 8
+ %16 = OpTypeArray %1 %15
+ %17 = OpTypeStruct %2 %1 %16 %16
+ %20 = OpTypeVoid
+ %25 = OpConstant %5 0
+ %3 = OpTypePointer Input %2
+ %13 = OpTypePointer Output %2
+ %18 = OpTypePointer Output %17
+ %21 = OpTypeFunction %20
+ %4 = OpVariable %3 Input
+ %19 = OpVariable %18 Output
+ %22 = OpFunction %20 None %21
+ %23 = OpLabel
+ %24 = OpLoad %2 %4
+ %26 = OpAccessChain %13 %19 %25
+ OpStore %26 %24
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, ConstantArraySizeNoDebug) {
+ constexpr char kSrcNoDebug[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %22 "main" %4 %19
+OpSource GLSL 450
+OpDecorate %4 Location 0
+OpMemberDecorate %17 1 RelaxedPrecision
+OpMemberDecorate %17 0 BuiltIn Position
+OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%8 = OpTypeVector %5 4
+%15 = OpConstant %5 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %5 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd
+)";
+ constexpr char kDstNoDebug[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %22 "main" %4 %19
+OpSource GLSL 450
+OpDecorate %4 Location 0
+OpMemberDecorate %17 1 RelaxedPrecision
+OpMemberDecorate %17 0 BuiltIn Position
+OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%6 = OpTypeInt 32 1
+%8 = OpTypeVector %5 4
+%15 = OpConstant %6 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %5 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 27
++; Bound: 34
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %22 "main" %4 %19
+ OpSource GLSL 450
+ OpDecorate %4 Location 0
+ OpMemberDecorate %17 1 RelaxedPrecision
+ OpMemberDecorate %17 0 BuiltIn Position
+ OpMemberDecorate %17 1 BuiltIn PointSize
+ OpMemberDecorate %17 2 BuiltIn ClipDistance
+ OpMemberDecorate %17 3 BuiltIn CullDistance
+ OpDecorate %17 Block
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %5 = OpTypeInt 32 0
++%27 = OpTypeInt 32 1
+ %8 = OpTypeVector %5 4
+-%15 = OpConstant %5 8
++%15 = OpConstant %27 8
+ %16 = OpTypeArray %1 %15
+ %17 = OpTypeStruct %2 %1 %16 %16
+ %20 = OpTypeVoid
+ %25 = OpConstant %5 0
+ %3 = OpTypePointer Input %2
+ %13 = OpTypePointer Output %2
+ %18 = OpTypePointer Output %17
+ %21 = OpTypeFunction %20
+ %4 = OpVariable %3 Input
+ %19 = OpVariable %18 Output
+ %22 = OpFunction %20 None %21
+ %23 = OpLabel
+ %24 = OpLoad %2 %4
+ %26 = OpAccessChain %13 %19 %25
+ OpStore %26 %24
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/constant_array_size_dst.spvasm b/test/diff/diff_files/constant_array_size_dst.spvasm
new file mode 100644
index 00000000..a432b612
--- /dev/null
+++ b/test/diff/diff_files/constant_array_size_dst.spvasm
@@ -0,0 +1,47 @@
+; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %22 "main" %4 %19
+OpSource GLSL 450
+OpName %4 "_ua_position"
+OpName %17 "gl_PerVertex"
+OpMemberName %17 0 "gl_Position"
+OpMemberName %17 1 "gl_PointSize"
+OpMemberName %17 2 "gl_ClipDistance"
+OpMemberName %17 3 "gl_CullDistance"
+OpName %19 ""
+OpName %22 "main"
+OpDecorate %4 Location 0
+OpMemberDecorate %17 1 RelaxedPrecision
+OpMemberDecorate %17 0 BuiltIn Position
+OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%6 = OpTypeInt 32 1
+%8 = OpTypeVector %5 4
+%15 = OpConstant %6 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %5 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd
diff --git a/test/diff/diff_files/constant_array_size_src.spvasm b/test/diff/diff_files/constant_array_size_src.spvasm
new file mode 100644
index 00000000..4e1ba646
--- /dev/null
+++ b/test/diff/diff_files/constant_array_size_src.spvasm
@@ -0,0 +1,48 @@
+;; Tests that identical integer constants are matched when used as array size,
+;; regardless of int or uint.
+; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %22 "main" %4 %19
+OpSource GLSL 450
+OpName %4 "_ua_position"
+OpName %17 "gl_PerVertex"
+OpMemberName %17 0 "gl_Position"
+OpMemberName %17 1 "gl_PointSize"
+OpMemberName %17 2 "gl_ClipDistance"
+OpMemberName %17 3 "gl_CullDistance"
+OpName %19 ""
+OpName %22 "main"
+OpDecorate %4 Location 0
+OpMemberDecorate %17 1 RelaxedPrecision
+OpMemberDecorate %17 0 BuiltIn Position
+OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%8 = OpTypeVector %5 4
+%15 = OpConstant %5 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %5 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd
diff --git a/test/diff/diff_files/diff_test_files_autogen.cmake b/test/diff/diff_files/diff_test_files_autogen.cmake
new file mode 100644
index 00000000..6440d0b9
--- /dev/null
+++ b/test/diff/diff_files/diff_test_files_autogen.cmake
@@ -0,0 +1,47 @@
+# GENERATED FILE - DO NOT EDIT.
+# Generated by generate_tests.py
+#
+# Copyright (c) 2022 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.
+
+list(APPEND DIFF_TEST_FILES
+"diff_files/OpExtInst_in_dst_only_autogen.cpp"
+"diff_files/OpExtInst_in_src_only_autogen.cpp"
+"diff_files/OpTypeForwardPointer_basic_autogen.cpp"
+"diff_files/OpTypeForwardPointer_intertwined_autogen.cpp"
+"diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp"
+"diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp"
+"diff_files/OpTypeForwardPointer_nested_autogen.cpp"
+"diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp"
+"diff_files/basic_autogen.cpp"
+"diff_files/constant_array_size_autogen.cpp"
+"diff_files/different_decorations_fragment_autogen.cpp"
+"diff_files/different_decorations_vertex_autogen.cpp"
+"diff_files/different_function_parameter_count_autogen.cpp"
+"diff_files/extra_if_block_autogen.cpp"
+"diff_files/index_signedness_autogen.cpp"
+"diff_files/int_vs_uint_constants_autogen.cpp"
+"diff_files/large_functions_large_diffs_autogen.cpp"
+"diff_files/large_functions_small_diffs_autogen.cpp"
+"diff_files/multiple_different_entry_points_autogen.cpp"
+"diff_files/multiple_same_entry_points_autogen.cpp"
+"diff_files/reordered_if_blocks_autogen.cpp"
+"diff_files/reordered_switch_blocks_autogen.cpp"
+"diff_files/small_functions_small_diffs_autogen.cpp"
+"diff_files/spec_constant_array_size_autogen.cpp"
+"diff_files/spec_constant_composite_autogen.cpp"
+"diff_files/spec_constant_op_autogen.cpp"
+"diff_files/spec_constant_specid_autogen.cpp"
+"diff_files/unrelated_shaders_autogen.cpp"
+)
diff --git a/test/diff/diff_files/different_decorations_fragment_autogen.cpp b/test/diff/diff_files/different_decorations_fragment_autogen.cpp
new file mode 100644
index 00000000..0d34654f
--- /dev/null
+++ b/test/diff/diff_files/different_decorations_fragment_autogen.cpp
@@ -0,0 +1,1626 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Test where variable set/binding/location decorations are different between
+// src and dst fragment shaders.
+constexpr char kSrc[] = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %63 "main" %4 %22
+OpExecutionMode %63 OriginUpperLeft
+OpSource GLSL 450
+OpName %4 "_ue"
+OpName %8 "_uf"
+OpName %11 "_ug"
+OpName %12 "_uA"
+OpMemberName %12 0 "_ux"
+OpName %14 "_uc"
+OpName %15 "_uB"
+OpMemberName %15 0 "_ux"
+OpName %20 "_ud"
+OpName %22 "_ucol"
+OpName %26 "ANGLEDepthRangeParams"
+OpMemberName %26 0 "near"
+OpMemberName %26 1 "far"
+OpMemberName %26 2 "diff"
+OpMemberName %26 3 "reserved"
+OpName %27 "ANGLEUniformBlock"
+OpMemberName %27 0 "viewport"
+OpMemberName %27 1 "clipDistancesEnabled"
+OpMemberName %27 2 "xfbActiveUnpaused"
+OpMemberName %27 3 "xfbVerticesPerInstance"
+OpMemberName %27 4 "numSamples"
+OpMemberName %27 5 "xfbBufferOffsets"
+OpMemberName %27 6 "acbBufferOffsets"
+OpMemberName %27 7 "depthRange"
+OpName %29 "ANGLEUniforms"
+OpName %33 "_uc"
+OpName %32 "_uh"
+OpName %49 "_ux"
+OpName %50 "_uy"
+OpName %48 "_ui"
+OpName %63 "main"
+OpName %65 "param"
+OpName %68 "param"
+OpName %73 "param"
+OpDecorate %4 Location 0
+OpDecorate %8 RelaxedPrecision
+OpDecorate %8 DescriptorSet 0
+OpDecorate %8 Binding 0
+OpDecorate %11 DescriptorSet 0
+OpDecorate %11 Binding 1
+OpMemberDecorate %12 0 Offset 0
+OpMemberDecorate %12 0 RelaxedPrecision
+OpDecorate %12 Block
+OpDecorate %14 DescriptorSet 0
+OpDecorate %14 Binding 2
+OpMemberDecorate %15 0 Offset 0
+OpMemberDecorate %15 0 RelaxedPrecision
+OpDecorate %15 BufferBlock
+OpDecorate %20 DescriptorSet 0
+OpDecorate %20 Binding 3
+OpDecorate %22 RelaxedPrecision
+OpDecorate %22 Location 0
+OpMemberDecorate %26 0 Offset 0
+OpMemberDecorate %26 1 Offset 4
+OpMemberDecorate %26 2 Offset 8
+OpMemberDecorate %26 3 Offset 12
+OpMemberDecorate %27 0 Offset 0
+OpMemberDecorate %27 1 Offset 16
+OpMemberDecorate %27 2 Offset 20
+OpMemberDecorate %27 3 Offset 24
+OpMemberDecorate %27 4 Offset 28
+OpMemberDecorate %27 5 Offset 32
+OpMemberDecorate %27 6 Offset 48
+OpMemberDecorate %27 7 Offset 64
+OpMemberDecorate %27 2 RelaxedPrecision
+OpMemberDecorate %27 4 RelaxedPrecision
+OpDecorate %27 Block
+OpDecorate %29 DescriptorSet 0
+OpDecorate %29 Binding 4
+OpDecorate %32 RelaxedPrecision
+OpDecorate %33 RelaxedPrecision
+OpDecorate %36 RelaxedPrecision
+OpDecorate %37 RelaxedPrecision
+OpDecorate %38 RelaxedPrecision
+OpDecorate %39 RelaxedPrecision
+OpDecorate %41 RelaxedPrecision
+OpDecorate %42 RelaxedPrecision
+OpDecorate %43 RelaxedPrecision
+OpDecorate %48 RelaxedPrecision
+OpDecorate %49 RelaxedPrecision
+OpDecorate %50 RelaxedPrecision
+OpDecorate %52 RelaxedPrecision
+OpDecorate %53 RelaxedPrecision
+OpDecorate %54 RelaxedPrecision
+OpDecorate %55 RelaxedPrecision
+OpDecorate %56 RelaxedPrecision
+OpDecorate %57 RelaxedPrecision
+OpDecorate %58 RelaxedPrecision
+OpDecorate %59 RelaxedPrecision
+OpDecorate %60 RelaxedPrecision
+OpDecorate %67 RelaxedPrecision
+OpDecorate %68 RelaxedPrecision
+OpDecorate %72 RelaxedPrecision
+OpDecorate %73 RelaxedPrecision
+OpDecorate %75 RelaxedPrecision
+OpDecorate %76 RelaxedPrecision
+OpDecorate %77 RelaxedPrecision
+OpDecorate %80 RelaxedPrecision
+OpDecorate %81 RelaxedPrecision
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeImage %1 2D 0 0 0 1 Unknown
+%6 = OpTypeSampledImage %5
+%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8
+%12 = OpTypeStruct %2
+%15 = OpTypeStruct %2
+%16 = OpTypeInt 32 0
+%17 = OpConstant %16 2
+%18 = OpTypeArray %15 %17
+%23 = OpTypeInt 32 1
+%24 = OpTypeVector %23 4
+%25 = OpTypeVector %16 4
+%26 = OpTypeStruct %1 %1 %1 %1
+%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26
+%35 = OpTypeVector %1 2
+%40 = OpTypeVector %23 2
+%61 = OpTypeVoid
+%69 = OpConstant %16 0
+%78 = OpConstant %16 1
+%3 = OpTypePointer Input %2
+%7 = OpTypePointer UniformConstant %6
+%10 = OpTypePointer UniformConstant %9
+%13 = OpTypePointer Uniform %12
+%19 = OpTypePointer Uniform %18
+%21 = OpTypePointer Output %2
+%28 = OpTypePointer Uniform %27
+%30 = OpTypePointer Function %2
+%70 = OpTypePointer Uniform %2
+%31 = OpTypeFunction %2 %30
+%47 = OpTypeFunction %2 %30 %30
+%62 = OpTypeFunction %61
+%4 = OpVariable %3 Input
+%8 = OpVariable %7 UniformConstant
+%11 = OpVariable %10 UniformConstant
+%14 = OpVariable %13 Uniform
+%20 = OpVariable %19 Uniform
+%22 = OpVariable %21 Output
+%29 = OpVariable %28 Uniform
+%32 = OpFunction %2 None %31
+%33 = OpFunctionParameter %30
+%34 = OpLabel
+%36 = OpLoad %6 %8
+%37 = OpLoad %2 %33
+%38 = OpVectorShuffle %35 %37 %37 0 1
+%39 = OpImageSampleImplicitLod %2 %36 %38
+%41 = OpLoad %2 %33
+%42 = OpVectorShuffle %35 %41 %41 2 3
+%43 = OpConvertFToS %40 %42
+%44 = OpLoad %9 %11
+%45 = OpImageRead %2 %44 %43
+%46 = OpFAdd %2 %39 %45
+OpReturnValue %46
+OpFunctionEnd
+%48 = OpFunction %2 None %47
+%49 = OpFunctionParameter %30
+%50 = OpFunctionParameter %30
+%51 = OpLabel
+%52 = OpLoad %2 %49
+%53 = OpVectorShuffle %35 %52 %52 0 1
+%54 = OpLoad %2 %50
+%55 = OpVectorShuffle %35 %54 %54 2 3
+%56 = OpCompositeExtract %1 %53 0
+%57 = OpCompositeExtract %1 %53 1
+%58 = OpCompositeExtract %1 %55 0
+%59 = OpCompositeExtract %1 %55 1
+%60 = OpCompositeConstruct %2 %56 %57 %58 %59
+OpReturnValue %60
+OpFunctionEnd
+%63 = OpFunction %61 None %62
+%64 = OpLabel
+%65 = OpVariable %30 Function
+%68 = OpVariable %30 Function
+%73 = OpVariable %30 Function
+%66 = OpLoad %2 %4
+OpStore %65 %66
+%67 = OpFunctionCall %2 %32 %65
+%71 = OpAccessChain %70 %14 %69
+%72 = OpLoad %2 %71
+OpStore %68 %72
+%74 = OpAccessChain %70 %20 %69 %69
+%75 = OpLoad %2 %74
+OpStore %73 %75
+%76 = OpFunctionCall %2 %48 %68 %73
+%77 = OpFAdd %2 %67 %76
+%79 = OpAccessChain %70 %20 %78 %69
+%80 = OpLoad %2 %79
+%81 = OpFAdd %2 %77 %80
+OpStore %22 %81
+OpReturn
+OpFunctionEnd
+)";
+constexpr char kDst[] = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %63 "main" %4 %22
+OpExecutionMode %63 OriginUpperLeft
+OpSource GLSL 450
+OpName %4 "_ue"
+OpName %8 "_uf"
+OpName %11 "_ug"
+OpName %12 "_uA"
+OpMemberName %12 0 "_ux"
+OpName %14 "_uc"
+OpName %15 "_uB"
+OpMemberName %15 0 "_ux"
+OpName %20 "_ud"
+OpName %22 "_ucol"
+OpName %26 "ANGLEDepthRangeParams"
+OpMemberName %26 0 "near"
+OpMemberName %26 1 "far"
+OpMemberName %26 2 "diff"
+OpMemberName %26 3 "reserved"
+OpName %27 "ANGLEUniformBlock"
+OpMemberName %27 0 "viewport"
+OpMemberName %27 1 "clipDistancesEnabled"
+OpMemberName %27 2 "xfbActiveUnpaused"
+OpMemberName %27 3 "xfbVerticesPerInstance"
+OpMemberName %27 4 "numSamples"
+OpMemberName %27 5 "xfbBufferOffsets"
+OpMemberName %27 6 "acbBufferOffsets"
+OpMemberName %27 7 "depthRange"
+OpName %29 "ANGLEUniforms"
+OpName %33 "_uc"
+OpName %32 "_uh"
+OpName %49 "_ux"
+OpName %50 "_uy"
+OpName %48 "_ui"
+OpName %63 "main"
+OpName %65 "param"
+OpName %68 "param"
+OpName %73 "param"
+OpDecorate %4 Location 1
+OpDecorate %8 RelaxedPrecision
+OpDecorate %8 DescriptorSet 2
+OpDecorate %8 Binding 0
+OpDecorate %11 DescriptorSet 3
+OpDecorate %11 Binding 0
+OpMemberDecorate %12 0 Offset 0
+OpMemberDecorate %12 0 RelaxedPrecision
+OpDecorate %12 Block
+OpDecorate %14 DescriptorSet 3
+OpDecorate %14 Binding 1
+OpMemberDecorate %15 0 Offset 0
+OpMemberDecorate %15 0 RelaxedPrecision
+OpDecorate %15 BufferBlock
+OpDecorate %20 DescriptorSet 3
+OpDecorate %20 Binding 2
+OpDecorate %22 RelaxedPrecision
+OpDecorate %22 Location 1
+OpMemberDecorate %26 0 Offset 0
+OpMemberDecorate %26 1 Offset 4
+OpMemberDecorate %26 2 Offset 8
+OpMemberDecorate %26 3 Offset 12
+OpMemberDecorate %27 0 Offset 0
+OpMemberDecorate %27 1 Offset 16
+OpMemberDecorate %27 2 Offset 20
+OpMemberDecorate %27 3 Offset 24
+OpMemberDecorate %27 4 Offset 28
+OpMemberDecorate %27 5 Offset 32
+OpMemberDecorate %27 6 Offset 48
+OpMemberDecorate %27 7 Offset 64
+OpMemberDecorate %27 2 RelaxedPrecision
+OpMemberDecorate %27 4 RelaxedPrecision
+OpDecorate %27 Block
+OpDecorate %29 DescriptorSet 0
+OpDecorate %29 Binding 0
+OpDecorate %32 RelaxedPrecision
+OpDecorate %33 RelaxedPrecision
+OpDecorate %36 RelaxedPrecision
+OpDecorate %37 RelaxedPrecision
+OpDecorate %38 RelaxedPrecision
+OpDecorate %39 RelaxedPrecision
+OpDecorate %41 RelaxedPrecision
+OpDecorate %42 RelaxedPrecision
+OpDecorate %43 RelaxedPrecision
+OpDecorate %48 RelaxedPrecision
+OpDecorate %49 RelaxedPrecision
+OpDecorate %50 RelaxedPrecision
+OpDecorate %52 RelaxedPrecision
+OpDecorate %53 RelaxedPrecision
+OpDecorate %54 RelaxedPrecision
+OpDecorate %55 RelaxedPrecision
+OpDecorate %56 RelaxedPrecision
+OpDecorate %57 RelaxedPrecision
+OpDecorate %58 RelaxedPrecision
+OpDecorate %59 RelaxedPrecision
+OpDecorate %60 RelaxedPrecision
+OpDecorate %67 RelaxedPrecision
+OpDecorate %68 RelaxedPrecision
+OpDecorate %72 RelaxedPrecision
+OpDecorate %73 RelaxedPrecision
+OpDecorate %75 RelaxedPrecision
+OpDecorate %76 RelaxedPrecision
+OpDecorate %77 RelaxedPrecision
+OpDecorate %80 RelaxedPrecision
+OpDecorate %81 RelaxedPrecision
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeImage %1 2D 0 0 0 1 Unknown
+%6 = OpTypeSampledImage %5
+%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8
+%12 = OpTypeStruct %2
+%15 = OpTypeStruct %2
+%16 = OpTypeInt 32 0
+%17 = OpConstant %16 2
+%18 = OpTypeArray %15 %17
+%23 = OpTypeInt 32 1
+%24 = OpTypeVector %23 4
+%25 = OpTypeVector %16 4
+%26 = OpTypeStruct %1 %1 %1 %1
+%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26
+%35 = OpTypeVector %1 2
+%40 = OpTypeVector %23 2
+%61 = OpTypeVoid
+%69 = OpConstant %16 0
+%78 = OpConstant %16 1
+%82 = OpTypePointer Private %2
+%3 = OpTypePointer Input %2
+%7 = OpTypePointer UniformConstant %6
+%10 = OpTypePointer UniformConstant %9
+%13 = OpTypePointer Uniform %12
+%19 = OpTypePointer Uniform %18
+%83 = OpTypePointer Private %2
+%21 = OpTypePointer Output %2
+%28 = OpTypePointer Uniform %27
+%30 = OpTypePointer Function %2
+%70 = OpTypePointer Uniform %2
+%31 = OpTypeFunction %2 %30
+%47 = OpTypeFunction %2 %30 %30
+%62 = OpTypeFunction %61
+%4 = OpVariable %3 Input
+%8 = OpVariable %7 UniformConstant
+%11 = OpVariable %10 UniformConstant
+%14 = OpVariable %13 Uniform
+%20 = OpVariable %19 Uniform
+%22 = OpVariable %21 Output
+%29 = OpVariable %28 Uniform
+%84 = OpConstant %23 0
+%85 = OpConstant %1 0.5
+%32 = OpFunction %2 None %31
+%33 = OpFunctionParameter %30
+%34 = OpLabel
+%36 = OpLoad %6 %8
+%37 = OpLoad %2 %33
+%38 = OpVectorShuffle %35 %37 %37 0 1
+%39 = OpImageSampleImplicitLod %2 %36 %38
+%41 = OpLoad %2 %33
+%42 = OpVectorShuffle %35 %41 %41 2 3
+%43 = OpConvertFToS %40 %42
+%44 = OpLoad %9 %11
+%45 = OpImageRead %2 %44 %43
+%46 = OpFAdd %2 %39 %45
+OpReturnValue %46
+OpFunctionEnd
+%48 = OpFunction %2 None %47
+%49 = OpFunctionParameter %30
+%50 = OpFunctionParameter %30
+%51 = OpLabel
+%52 = OpLoad %2 %49
+%53 = OpVectorShuffle %35 %52 %52 0 1
+%54 = OpLoad %2 %50
+%55 = OpVectorShuffle %35 %54 %54 2 3
+%56 = OpCompositeExtract %1 %53 0
+%57 = OpCompositeExtract %1 %53 1
+%58 = OpCompositeExtract %1 %55 0
+%59 = OpCompositeExtract %1 %55 1
+%60 = OpCompositeConstruct %2 %56 %57 %58 %59
+OpReturnValue %60
+OpFunctionEnd
+%63 = OpFunction %61 None %62
+%64 = OpLabel
+%65 = OpVariable %30 Function
+%68 = OpVariable %30 Function
+%73 = OpVariable %30 Function
+%66 = OpLoad %2 %4
+OpStore %65 %66
+%67 = OpFunctionCall %2 %32 %65
+%71 = OpAccessChain %70 %14 %69
+%72 = OpLoad %2 %71
+OpStore %68 %72
+%74 = OpAccessChain %70 %20 %69 %69
+%75 = OpLoad %2 %74
+OpStore %73 %75
+%76 = OpFunctionCall %2 %48 %68 %73
+%77 = OpFAdd %2 %67 %76
+%79 = OpAccessChain %70 %20 %78 %69
+%80 = OpLoad %2 %79
+%81 = OpFAdd %2 %77 %80
+OpStore %22 %81
+OpReturn
+OpFunctionEnd
+)";
+
+TEST(DiffTest, DifferentDecorationsFragment) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 82
++; Bound: 86
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %63 "main" %4 %22
+ OpExecutionMode %63 OriginUpperLeft
+ OpSource GLSL 450
+ OpName %4 "_ue"
+ OpName %8 "_uf"
+ OpName %11 "_ug"
+ OpName %12 "_uA"
+ OpMemberName %12 0 "_ux"
+ OpName %14 "_uc"
+ OpName %15 "_uB"
+ OpMemberName %15 0 "_ux"
+ OpName %20 "_ud"
+ OpName %22 "_ucol"
+ OpName %26 "ANGLEDepthRangeParams"
+ OpMemberName %26 0 "near"
+ OpMemberName %26 1 "far"
+ OpMemberName %26 2 "diff"
+ OpMemberName %26 3 "reserved"
+ OpName %27 "ANGLEUniformBlock"
+ OpMemberName %27 0 "viewport"
+ OpMemberName %27 1 "clipDistancesEnabled"
+ OpMemberName %27 2 "xfbActiveUnpaused"
+ OpMemberName %27 3 "xfbVerticesPerInstance"
+ OpMemberName %27 4 "numSamples"
+ OpMemberName %27 5 "xfbBufferOffsets"
+ OpMemberName %27 6 "acbBufferOffsets"
+ OpMemberName %27 7 "depthRange"
+ OpName %29 "ANGLEUniforms"
+ OpName %33 "_uc"
+ OpName %32 "_uh"
+ OpName %49 "_ux"
+ OpName %50 "_uy"
+ OpName %48 "_ui"
+ OpName %63 "main"
+ OpName %65 "param"
+ OpName %68 "param"
+ OpName %73 "param"
+-OpDecorate %4 Location 0
++OpDecorate %4 Location 1
+ OpDecorate %8 RelaxedPrecision
+-OpDecorate %8 DescriptorSet 0
++OpDecorate %8 DescriptorSet 2
+ OpDecorate %8 Binding 0
+-OpDecorate %11 DescriptorSet 0
++OpDecorate %11 DescriptorSet 3
+-OpDecorate %11 Binding 1
++OpDecorate %11 Binding 0
+ OpMemberDecorate %12 0 Offset 0
+ OpMemberDecorate %12 0 RelaxedPrecision
+ OpDecorate %12 Block
+-OpDecorate %14 DescriptorSet 0
++OpDecorate %14 DescriptorSet 3
+-OpDecorate %14 Binding 2
++OpDecorate %14 Binding 1
+ OpMemberDecorate %15 0 Offset 0
+ OpMemberDecorate %15 0 RelaxedPrecision
+ OpDecorate %15 BufferBlock
+-OpDecorate %20 DescriptorSet 0
++OpDecorate %20 DescriptorSet 3
+-OpDecorate %20 Binding 3
++OpDecorate %20 Binding 2
+ OpDecorate %22 RelaxedPrecision
+-OpDecorate %22 Location 0
++OpDecorate %22 Location 1
+ OpMemberDecorate %26 0 Offset 0
+ OpMemberDecorate %26 1 Offset 4
+ OpMemberDecorate %26 2 Offset 8
+ OpMemberDecorate %26 3 Offset 12
+ OpMemberDecorate %27 0 Offset 0
+ OpMemberDecorate %27 1 Offset 16
+ OpMemberDecorate %27 2 Offset 20
+ OpMemberDecorate %27 3 Offset 24
+ OpMemberDecorate %27 4 Offset 28
+ OpMemberDecorate %27 5 Offset 32
+ OpMemberDecorate %27 6 Offset 48
+ OpMemberDecorate %27 7 Offset 64
+ OpMemberDecorate %27 2 RelaxedPrecision
+ OpMemberDecorate %27 4 RelaxedPrecision
+ OpDecorate %27 Block
+ OpDecorate %29 DescriptorSet 0
+-OpDecorate %29 Binding 4
++OpDecorate %29 Binding 0
+ OpDecorate %32 RelaxedPrecision
+ OpDecorate %33 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %38 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %43 RelaxedPrecision
+ OpDecorate %48 RelaxedPrecision
+ OpDecorate %49 RelaxedPrecision
+ OpDecorate %50 RelaxedPrecision
+ OpDecorate %52 RelaxedPrecision
+ OpDecorate %53 RelaxedPrecision
+ OpDecorate %54 RelaxedPrecision
+ OpDecorate %55 RelaxedPrecision
+ OpDecorate %56 RelaxedPrecision
+ OpDecorate %57 RelaxedPrecision
+ OpDecorate %58 RelaxedPrecision
+ OpDecorate %59 RelaxedPrecision
+ OpDecorate %60 RelaxedPrecision
+ OpDecorate %67 RelaxedPrecision
+ OpDecorate %68 RelaxedPrecision
+ OpDecorate %72 RelaxedPrecision
+ OpDecorate %73 RelaxedPrecision
+ OpDecorate %75 RelaxedPrecision
+ OpDecorate %76 RelaxedPrecision
+ OpDecorate %77 RelaxedPrecision
+ OpDecorate %80 RelaxedPrecision
+ OpDecorate %81 RelaxedPrecision
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %5 = OpTypeImage %1 2D 0 0 0 1 Unknown
+ %6 = OpTypeSampledImage %5
+ %9 = OpTypeImage %1 2D 0 0 0 2 Rgba8
+ %12 = OpTypeStruct %2
+ %15 = OpTypeStruct %2
+ %16 = OpTypeInt 32 0
+ %17 = OpConstant %16 2
+ %18 = OpTypeArray %15 %17
+ %23 = OpTypeInt 32 1
+ %24 = OpTypeVector %23 4
+ %25 = OpTypeVector %16 4
+ %26 = OpTypeStruct %1 %1 %1 %1
+ %27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26
+ %35 = OpTypeVector %1 2
+ %40 = OpTypeVector %23 2
+ %61 = OpTypeVoid
+ %69 = OpConstant %16 0
+ %78 = OpConstant %16 1
++%82 = OpTypePointer Private %2
+ %3 = OpTypePointer Input %2
+ %7 = OpTypePointer UniformConstant %6
+ %10 = OpTypePointer UniformConstant %9
+ %13 = OpTypePointer Uniform %12
+ %19 = OpTypePointer Uniform %18
++%83 = OpTypePointer Private %2
+ %21 = OpTypePointer Output %2
+ %28 = OpTypePointer Uniform %27
+ %30 = OpTypePointer Function %2
+ %70 = OpTypePointer Uniform %2
+ %31 = OpTypeFunction %2 %30
+ %47 = OpTypeFunction %2 %30 %30
+ %62 = OpTypeFunction %61
+ %4 = OpVariable %3 Input
+ %8 = OpVariable %7 UniformConstant
+ %11 = OpVariable %10 UniformConstant
+ %14 = OpVariable %13 Uniform
+ %20 = OpVariable %19 Uniform
+ %22 = OpVariable %21 Output
+ %29 = OpVariable %28 Uniform
++%84 = OpConstant %23 0
++%85 = OpConstant %1 0.5
+ %32 = OpFunction %2 None %31
+ %33 = OpFunctionParameter %30
+ %34 = OpLabel
+ %36 = OpLoad %6 %8
+ %37 = OpLoad %2 %33
+ %38 = OpVectorShuffle %35 %37 %37 0 1
+ %39 = OpImageSampleImplicitLod %2 %36 %38
+ %41 = OpLoad %2 %33
+ %42 = OpVectorShuffle %35 %41 %41 2 3
+ %43 = OpConvertFToS %40 %42
+ %44 = OpLoad %9 %11
+ %45 = OpImageRead %2 %44 %43
+ %46 = OpFAdd %2 %39 %45
+ OpReturnValue %46
+ OpFunctionEnd
+ %48 = OpFunction %2 None %47
+ %49 = OpFunctionParameter %30
+ %50 = OpFunctionParameter %30
+ %51 = OpLabel
+ %52 = OpLoad %2 %49
+ %53 = OpVectorShuffle %35 %52 %52 0 1
+ %54 = OpLoad %2 %50
+ %55 = OpVectorShuffle %35 %54 %54 2 3
+ %56 = OpCompositeExtract %1 %53 0
+ %57 = OpCompositeExtract %1 %53 1
+ %58 = OpCompositeExtract %1 %55 0
+ %59 = OpCompositeExtract %1 %55 1
+ %60 = OpCompositeConstruct %2 %56 %57 %58 %59
+ OpReturnValue %60
+ OpFunctionEnd
+ %63 = OpFunction %61 None %62
+ %64 = OpLabel
+ %65 = OpVariable %30 Function
+ %68 = OpVariable %30 Function
+ %73 = OpVariable %30 Function
+ %66 = OpLoad %2 %4
+ OpStore %65 %66
+ %67 = OpFunctionCall %2 %32 %65
+ %71 = OpAccessChain %70 %14 %69
+ %72 = OpLoad %2 %71
+ OpStore %68 %72
+ %74 = OpAccessChain %70 %20 %69 %69
+ %75 = OpLoad %2 %74
+ OpStore %73 %75
+ %76 = OpFunctionCall %2 %48 %68 %73
+ %77 = OpFAdd %2 %67 %76
+ %79 = OpAccessChain %70 %20 %78 %69
+ %80 = OpLoad %2 %79
+ %81 = OpFAdd %2 %77 %80
+ OpStore %22 %81
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, DifferentDecorationsFragmentNoDebug) {
+ constexpr char kSrcNoDebug[] = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %63 "main" %4 %22
+OpExecutionMode %63 OriginUpperLeft
+OpSource GLSL 450
+OpDecorate %4 Location 0
+OpDecorate %8 RelaxedPrecision
+OpDecorate %8 DescriptorSet 0
+OpDecorate %8 Binding 0
+OpDecorate %11 DescriptorSet 0
+OpDecorate %11 Binding 1
+OpMemberDecorate %12 0 Offset 0
+OpMemberDecorate %12 0 RelaxedPrecision
+OpDecorate %12 Block
+OpDecorate %14 DescriptorSet 0
+OpDecorate %14 Binding 2
+OpMemberDecorate %15 0 Offset 0
+OpMemberDecorate %15 0 RelaxedPrecision
+OpDecorate %15 BufferBlock
+OpDecorate %20 DescriptorSet 0
+OpDecorate %20 Binding 3
+OpDecorate %22 RelaxedPrecision
+OpDecorate %22 Location 0
+OpMemberDecorate %26 0 Offset 0
+OpMemberDecorate %26 1 Offset 4
+OpMemberDecorate %26 2 Offset 8
+OpMemberDecorate %26 3 Offset 12
+OpMemberDecorate %27 0 Offset 0
+OpMemberDecorate %27 1 Offset 16
+OpMemberDecorate %27 2 Offset 20
+OpMemberDecorate %27 3 Offset 24
+OpMemberDecorate %27 4 Offset 28
+OpMemberDecorate %27 5 Offset 32
+OpMemberDecorate %27 6 Offset 48
+OpMemberDecorate %27 7 Offset 64
+OpMemberDecorate %27 2 RelaxedPrecision
+OpMemberDecorate %27 4 RelaxedPrecision
+OpDecorate %27 Block
+OpDecorate %29 DescriptorSet 0
+OpDecorate %29 Binding 4
+OpDecorate %32 RelaxedPrecision
+OpDecorate %33 RelaxedPrecision
+OpDecorate %36 RelaxedPrecision
+OpDecorate %37 RelaxedPrecision
+OpDecorate %38 RelaxedPrecision
+OpDecorate %39 RelaxedPrecision
+OpDecorate %41 RelaxedPrecision
+OpDecorate %42 RelaxedPrecision
+OpDecorate %43 RelaxedPrecision
+OpDecorate %48 RelaxedPrecision
+OpDecorate %49 RelaxedPrecision
+OpDecorate %50 RelaxedPrecision
+OpDecorate %52 RelaxedPrecision
+OpDecorate %53 RelaxedPrecision
+OpDecorate %54 RelaxedPrecision
+OpDecorate %55 RelaxedPrecision
+OpDecorate %56 RelaxedPrecision
+OpDecorate %57 RelaxedPrecision
+OpDecorate %58 RelaxedPrecision
+OpDecorate %59 RelaxedPrecision
+OpDecorate %60 RelaxedPrecision
+OpDecorate %67 RelaxedPrecision
+OpDecorate %68 RelaxedPrecision
+OpDecorate %72 RelaxedPrecision
+OpDecorate %73 RelaxedPrecision
+OpDecorate %75 RelaxedPrecision
+OpDecorate %76 RelaxedPrecision
+OpDecorate %77 RelaxedPrecision
+OpDecorate %80 RelaxedPrecision
+OpDecorate %81 RelaxedPrecision
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeImage %1 2D 0 0 0 1 Unknown
+%6 = OpTypeSampledImage %5
+%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8
+%12 = OpTypeStruct %2
+%15 = OpTypeStruct %2
+%16 = OpTypeInt 32 0
+%17 = OpConstant %16 2
+%18 = OpTypeArray %15 %17
+%23 = OpTypeInt 32 1
+%24 = OpTypeVector %23 4
+%25 = OpTypeVector %16 4
+%26 = OpTypeStruct %1 %1 %1 %1
+%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26
+%35 = OpTypeVector %1 2
+%40 = OpTypeVector %23 2
+%61 = OpTypeVoid
+%69 = OpConstant %16 0
+%78 = OpConstant %16 1
+%3 = OpTypePointer Input %2
+%7 = OpTypePointer UniformConstant %6
+%10 = OpTypePointer UniformConstant %9
+%13 = OpTypePointer Uniform %12
+%19 = OpTypePointer Uniform %18
+%21 = OpTypePointer Output %2
+%28 = OpTypePointer Uniform %27
+%30 = OpTypePointer Function %2
+%70 = OpTypePointer Uniform %2
+%31 = OpTypeFunction %2 %30
+%47 = OpTypeFunction %2 %30 %30
+%62 = OpTypeFunction %61
+%4 = OpVariable %3 Input
+%8 = OpVariable %7 UniformConstant
+%11 = OpVariable %10 UniformConstant
+%14 = OpVariable %13 Uniform
+%20 = OpVariable %19 Uniform
+%22 = OpVariable %21 Output
+%29 = OpVariable %28 Uniform
+%32 = OpFunction %2 None %31
+%33 = OpFunctionParameter %30
+%34 = OpLabel
+%36 = OpLoad %6 %8
+%37 = OpLoad %2 %33
+%38 = OpVectorShuffle %35 %37 %37 0 1
+%39 = OpImageSampleImplicitLod %2 %36 %38
+%41 = OpLoad %2 %33
+%42 = OpVectorShuffle %35 %41 %41 2 3
+%43 = OpConvertFToS %40 %42
+%44 = OpLoad %9 %11
+%45 = OpImageRead %2 %44 %43
+%46 = OpFAdd %2 %39 %45
+OpReturnValue %46
+OpFunctionEnd
+%48 = OpFunction %2 None %47
+%49 = OpFunctionParameter %30
+%50 = OpFunctionParameter %30
+%51 = OpLabel
+%52 = OpLoad %2 %49
+%53 = OpVectorShuffle %35 %52 %52 0 1
+%54 = OpLoad %2 %50
+%55 = OpVectorShuffle %35 %54 %54 2 3
+%56 = OpCompositeExtract %1 %53 0
+%57 = OpCompositeExtract %1 %53 1
+%58 = OpCompositeExtract %1 %55 0
+%59 = OpCompositeExtract %1 %55 1
+%60 = OpCompositeConstruct %2 %56 %57 %58 %59
+OpReturnValue %60
+OpFunctionEnd
+%63 = OpFunction %61 None %62
+%64 = OpLabel
+%65 = OpVariable %30 Function
+%68 = OpVariable %30 Function
+%73 = OpVariable %30 Function
+%66 = OpLoad %2 %4
+OpStore %65 %66
+%67 = OpFunctionCall %2 %32 %65
+%71 = OpAccessChain %70 %14 %69
+%72 = OpLoad %2 %71
+OpStore %68 %72
+%74 = OpAccessChain %70 %20 %69 %69
+%75 = OpLoad %2 %74
+OpStore %73 %75
+%76 = OpFunctionCall %2 %48 %68 %73
+%77 = OpFAdd %2 %67 %76
+%79 = OpAccessChain %70 %20 %78 %69
+%80 = OpLoad %2 %79
+%81 = OpFAdd %2 %77 %80
+OpStore %22 %81
+OpReturn
+OpFunctionEnd
+
+)";
+ constexpr char kDstNoDebug[] = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %63 "main" %4 %22
+OpExecutionMode %63 OriginUpperLeft
+OpSource GLSL 450
+OpDecorate %4 Location 1
+OpDecorate %8 RelaxedPrecision
+OpDecorate %8 DescriptorSet 2
+OpDecorate %8 Binding 0
+OpDecorate %11 DescriptorSet 3
+OpDecorate %11 Binding 0
+OpMemberDecorate %12 0 Offset 0
+OpMemberDecorate %12 0 RelaxedPrecision
+OpDecorate %12 Block
+OpDecorate %14 DescriptorSet 3
+OpDecorate %14 Binding 1
+OpMemberDecorate %15 0 Offset 0
+OpMemberDecorate %15 0 RelaxedPrecision
+OpDecorate %15 BufferBlock
+OpDecorate %20 DescriptorSet 3
+OpDecorate %20 Binding 2
+OpDecorate %22 RelaxedPrecision
+OpDecorate %22 Location 1
+OpMemberDecorate %26 0 Offset 0
+OpMemberDecorate %26 1 Offset 4
+OpMemberDecorate %26 2 Offset 8
+OpMemberDecorate %26 3 Offset 12
+OpMemberDecorate %27 0 Offset 0
+OpMemberDecorate %27 1 Offset 16
+OpMemberDecorate %27 2 Offset 20
+OpMemberDecorate %27 3 Offset 24
+OpMemberDecorate %27 4 Offset 28
+OpMemberDecorate %27 5 Offset 32
+OpMemberDecorate %27 6 Offset 48
+OpMemberDecorate %27 7 Offset 64
+OpMemberDecorate %27 2 RelaxedPrecision
+OpMemberDecorate %27 4 RelaxedPrecision
+OpDecorate %27 Block
+OpDecorate %29 DescriptorSet 0
+OpDecorate %29 Binding 0
+OpDecorate %32 RelaxedPrecision
+OpDecorate %33 RelaxedPrecision
+OpDecorate %36 RelaxedPrecision
+OpDecorate %37 RelaxedPrecision
+OpDecorate %38 RelaxedPrecision
+OpDecorate %39 RelaxedPrecision
+OpDecorate %41 RelaxedPrecision
+OpDecorate %42 RelaxedPrecision
+OpDecorate %43 RelaxedPrecision
+OpDecorate %48 RelaxedPrecision
+OpDecorate %49 RelaxedPrecision
+OpDecorate %50 RelaxedPrecision
+OpDecorate %52 RelaxedPrecision
+OpDecorate %53 RelaxedPrecision
+OpDecorate %54 RelaxedPrecision
+OpDecorate %55 RelaxedPrecision
+OpDecorate %56 RelaxedPrecision
+OpDecorate %57 RelaxedPrecision
+OpDecorate %58 RelaxedPrecision
+OpDecorate %59 RelaxedPrecision
+OpDecorate %60 RelaxedPrecision
+OpDecorate %67 RelaxedPrecision
+OpDecorate %68 RelaxedPrecision
+OpDecorate %72 RelaxedPrecision
+OpDecorate %73 RelaxedPrecision
+OpDecorate %75 RelaxedPrecision
+OpDecorate %76 RelaxedPrecision
+OpDecorate %77 RelaxedPrecision
+OpDecorate %80 RelaxedPrecision
+OpDecorate %81 RelaxedPrecision
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeImage %1 2D 0 0 0 1 Unknown
+%6 = OpTypeSampledImage %5
+%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8
+%12 = OpTypeStruct %2
+%15 = OpTypeStruct %2
+%16 = OpTypeInt 32 0
+%17 = OpConstant %16 2
+%18 = OpTypeArray %15 %17
+%23 = OpTypeInt 32 1
+%24 = OpTypeVector %23 4
+%25 = OpTypeVector %16 4
+%26 = OpTypeStruct %1 %1 %1 %1
+%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26
+%35 = OpTypeVector %1 2
+%40 = OpTypeVector %23 2
+%61 = OpTypeVoid
+%69 = OpConstant %16 0
+%78 = OpConstant %16 1
+%82 = OpTypePointer Private %2
+%3 = OpTypePointer Input %2
+%7 = OpTypePointer UniformConstant %6
+%10 = OpTypePointer UniformConstant %9
+%13 = OpTypePointer Uniform %12
+%19 = OpTypePointer Uniform %18
+%83 = OpTypePointer Private %2
+%21 = OpTypePointer Output %2
+%28 = OpTypePointer Uniform %27
+%30 = OpTypePointer Function %2
+%70 = OpTypePointer Uniform %2
+%31 = OpTypeFunction %2 %30
+%47 = OpTypeFunction %2 %30 %30
+%62 = OpTypeFunction %61
+%4 = OpVariable %3 Input
+%8 = OpVariable %7 UniformConstant
+%11 = OpVariable %10 UniformConstant
+%14 = OpVariable %13 Uniform
+%20 = OpVariable %19 Uniform
+%22 = OpVariable %21 Output
+%29 = OpVariable %28 Uniform
+%84 = OpConstant %23 0
+%85 = OpConstant %1 0.5
+%32 = OpFunction %2 None %31
+%33 = OpFunctionParameter %30
+%34 = OpLabel
+%36 = OpLoad %6 %8
+%37 = OpLoad %2 %33
+%38 = OpVectorShuffle %35 %37 %37 0 1
+%39 = OpImageSampleImplicitLod %2 %36 %38
+%41 = OpLoad %2 %33
+%42 = OpVectorShuffle %35 %41 %41 2 3
+%43 = OpConvertFToS %40 %42
+%44 = OpLoad %9 %11
+%45 = OpImageRead %2 %44 %43
+%46 = OpFAdd %2 %39 %45
+OpReturnValue %46
+OpFunctionEnd
+%48 = OpFunction %2 None %47
+%49 = OpFunctionParameter %30
+%50 = OpFunctionParameter %30
+%51 = OpLabel
+%52 = OpLoad %2 %49
+%53 = OpVectorShuffle %35 %52 %52 0 1
+%54 = OpLoad %2 %50
+%55 = OpVectorShuffle %35 %54 %54 2 3
+%56 = OpCompositeExtract %1 %53 0
+%57 = OpCompositeExtract %1 %53 1
+%58 = OpCompositeExtract %1 %55 0
+%59 = OpCompositeExtract %1 %55 1
+%60 = OpCompositeConstruct %2 %56 %57 %58 %59
+OpReturnValue %60
+OpFunctionEnd
+%63 = OpFunction %61 None %62
+%64 = OpLabel
+%65 = OpVariable %30 Function
+%68 = OpVariable %30 Function
+%73 = OpVariable %30 Function
+%66 = OpLoad %2 %4
+OpStore %65 %66
+%67 = OpFunctionCall %2 %32 %65
+%71 = OpAccessChain %70 %14 %69
+%72 = OpLoad %2 %71
+OpStore %68 %72
+%74 = OpAccessChain %70 %20 %69 %69
+%75 = OpLoad %2 %74
+OpStore %73 %75
+%76 = OpFunctionCall %2 %48 %68 %73
+%77 = OpFAdd %2 %67 %76
+%79 = OpAccessChain %70 %20 %78 %69
+%80 = OpLoad %2 %79
+%81 = OpFAdd %2 %77 %80
+OpStore %22 %81
+OpReturn
+OpFunctionEnd
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 82
++; Bound: 92
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %63 "main" %4 %22
+ OpExecutionMode %63 OriginUpperLeft
+ OpSource GLSL 450
+-OpDecorate %4 Location 0
++OpDecorate %4 Location 1
+ OpDecorate %8 RelaxedPrecision
+-OpDecorate %8 DescriptorSet 0
++OpDecorate %8 DescriptorSet 2
+ OpDecorate %8 Binding 0
+-OpDecorate %11 DescriptorSet 0
++OpDecorate %11 DescriptorSet 3
+-OpDecorate %11 Binding 1
++OpDecorate %11 Binding 0
+ OpMemberDecorate %12 0 Offset 0
+ OpMemberDecorate %12 0 RelaxedPrecision
+ OpDecorate %12 Block
++OpDecorate %82 DescriptorSet 3
++OpDecorate %82 Binding 1
+-OpDecorate %14 DescriptorSet 0
++OpDecorate %14 DescriptorSet 3
+ OpDecorate %14 Binding 2
+ OpMemberDecorate %15 0 Offset 0
+ OpMemberDecorate %15 0 RelaxedPrecision
+ OpDecorate %15 BufferBlock
+-OpDecorate %20 DescriptorSet 0
+-OpDecorate %20 Binding 3
+ OpDecorate %22 RelaxedPrecision
+-OpDecorate %22 Location 0
++OpDecorate %22 Location 1
+ OpMemberDecorate %26 0 Offset 0
+ OpMemberDecorate %26 1 Offset 4
+ OpMemberDecorate %26 2 Offset 8
+ OpMemberDecorate %26 3 Offset 12
+ OpMemberDecorate %27 0 Offset 0
+ OpMemberDecorate %27 1 Offset 16
+ OpMemberDecorate %27 2 Offset 20
+ OpMemberDecorate %27 3 Offset 24
+ OpMemberDecorate %27 4 Offset 28
+ OpMemberDecorate %27 5 Offset 32
+ OpMemberDecorate %27 6 Offset 48
+ OpMemberDecorate %27 7 Offset 64
+ OpMemberDecorate %27 2 RelaxedPrecision
+ OpMemberDecorate %27 4 RelaxedPrecision
+ OpDecorate %27 Block
+-OpDecorate %29 DescriptorSet 0
+-OpDecorate %29 Binding 4
++OpDecorate %83 DescriptorSet 0
++OpDecorate %83 Binding 0
+ OpDecorate %32 RelaxedPrecision
+-OpDecorate %33 RelaxedPrecision
++OpDecorate %84 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %38 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %43 RelaxedPrecision
+ OpDecorate %48 RelaxedPrecision
+-OpDecorate %49 RelaxedPrecision
+-OpDecorate %50 RelaxedPrecision
++OpDecorate %85 RelaxedPrecision
++OpDecorate %86 RelaxedPrecision
+ OpDecorate %52 RelaxedPrecision
+ OpDecorate %53 RelaxedPrecision
+ OpDecorate %54 RelaxedPrecision
+ OpDecorate %55 RelaxedPrecision
+ OpDecorate %56 RelaxedPrecision
+ OpDecorate %57 RelaxedPrecision
+ OpDecorate %58 RelaxedPrecision
+ OpDecorate %59 RelaxedPrecision
+ OpDecorate %60 RelaxedPrecision
+ OpDecorate %67 RelaxedPrecision
+ OpDecorate %68 RelaxedPrecision
+ OpDecorate %72 RelaxedPrecision
+ OpDecorate %73 RelaxedPrecision
+ OpDecorate %75 RelaxedPrecision
+ OpDecorate %76 RelaxedPrecision
+ OpDecorate %77 RelaxedPrecision
+ OpDecorate %80 RelaxedPrecision
+ OpDecorate %81 RelaxedPrecision
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %5 = OpTypeImage %1 2D 0 0 0 1 Unknown
+ %6 = OpTypeSampledImage %5
+ %9 = OpTypeImage %1 2D 0 0 0 2 Rgba8
+ %12 = OpTypeStruct %2
+ %15 = OpTypeStruct %2
+ %16 = OpTypeInt 32 0
+ %17 = OpConstant %16 2
+ %18 = OpTypeArray %15 %17
+ %23 = OpTypeInt 32 1
+ %24 = OpTypeVector %23 4
+ %25 = OpTypeVector %16 4
+ %26 = OpTypeStruct %1 %1 %1 %1
+ %27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26
+ %35 = OpTypeVector %1 2
+ %40 = OpTypeVector %23 2
+ %61 = OpTypeVoid
+ %69 = OpConstant %16 0
+ %78 = OpConstant %16 1
++%88 = OpTypePointer Private %2
+ %3 = OpTypePointer Input %2
+ %7 = OpTypePointer UniformConstant %6
+ %10 = OpTypePointer UniformConstant %9
+ %13 = OpTypePointer Uniform %12
+ %19 = OpTypePointer Uniform %18
++%89 = OpTypePointer Private %2
+ %21 = OpTypePointer Output %2
+ %28 = OpTypePointer Uniform %27
+ %30 = OpTypePointer Function %2
+ %70 = OpTypePointer Uniform %2
+ %31 = OpTypeFunction %2 %30
+ %47 = OpTypeFunction %2 %30 %30
+ %62 = OpTypeFunction %61
+ %4 = OpVariable %3 Input
+ %8 = OpVariable %7 UniformConstant
+ %11 = OpVariable %10 UniformConstant
++%82 = OpVariable %13 Uniform
+-%14 = OpVariable %13 Uniform
++%14 = OpVariable %19 Uniform
+-%20 = OpVariable %19 Uniform
+ %22 = OpVariable %21 Output
+-%29 = OpVariable %28 Uniform
++%83 = OpVariable %28 Uniform
++%90 = OpConstant %23 0
++%91 = OpConstant %1 0.5
+ %32 = OpFunction %2 None %31
+-%33 = OpFunctionParameter %30
++%84 = OpFunctionParameter %30
+ %34 = OpLabel
+ %36 = OpLoad %6 %8
+-%37 = OpLoad %2 %33
++%37 = OpLoad %2 %84
+ %38 = OpVectorShuffle %35 %37 %37 0 1
+ %39 = OpImageSampleImplicitLod %2 %36 %38
+-%41 = OpLoad %2 %33
++%41 = OpLoad %2 %84
+ %42 = OpVectorShuffle %35 %41 %41 2 3
+ %43 = OpConvertFToS %40 %42
+ %44 = OpLoad %9 %11
+ %45 = OpImageRead %2 %44 %43
+ %46 = OpFAdd %2 %39 %45
+ OpReturnValue %46
+ OpFunctionEnd
+ %48 = OpFunction %2 None %47
+-%49 = OpFunctionParameter %30
+-%50 = OpFunctionParameter %30
++%85 = OpFunctionParameter %30
++%86 = OpFunctionParameter %30
+ %51 = OpLabel
+-%52 = OpLoad %2 %49
++%52 = OpLoad %2 %85
+ %53 = OpVectorShuffle %35 %52 %52 0 1
+-%54 = OpLoad %2 %50
++%54 = OpLoad %2 %86
+ %55 = OpVectorShuffle %35 %54 %54 2 3
+ %56 = OpCompositeExtract %1 %53 0
+ %57 = OpCompositeExtract %1 %53 1
+ %58 = OpCompositeExtract %1 %55 0
+ %59 = OpCompositeExtract %1 %55 1
+ %60 = OpCompositeConstruct %2 %56 %57 %58 %59
+ OpReturnValue %60
+ OpFunctionEnd
+ %63 = OpFunction %61 None %62
+ %64 = OpLabel
+ %65 = OpVariable %30 Function
+ %68 = OpVariable %30 Function
+ %73 = OpVariable %30 Function
+ %66 = OpLoad %2 %4
+ OpStore %65 %66
+ %67 = OpFunctionCall %2 %32 %65
+-%71 = OpAccessChain %70 %14 %69
++%87 = OpAccessChain %70 %82 %69
+-%72 = OpLoad %2 %71
++%72 = OpLoad %2 %87
+ OpStore %68 %72
+-%74 = OpAccessChain %70 %20 %69 %69
++%74 = OpAccessChain %70 %14 %69 %69
+ %75 = OpLoad %2 %74
+ OpStore %73 %75
+ %76 = OpFunctionCall %2 %48 %68 %73
+ %77 = OpFAdd %2 %67 %76
+-%79 = OpAccessChain %70 %20 %78 %69
++%79 = OpAccessChain %70 %14 %78 %69
+ %80 = OpLoad %2 %79
+ %81 = OpFAdd %2 %77 %80
+ OpStore %22 %81
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+TEST(DiffTest, DifferentDecorationsFragmentIgnoreLocation) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 82
++; Bound: 86
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %63 "main" %4 %22
+ OpExecutionMode %63 OriginUpperLeft
+ OpSource GLSL 450
+ OpName %4 "_ue"
+ OpName %8 "_uf"
+ OpName %11 "_ug"
+ OpName %12 "_uA"
+ OpMemberName %12 0 "_ux"
+ OpName %14 "_uc"
+ OpName %15 "_uB"
+ OpMemberName %15 0 "_ux"
+ OpName %20 "_ud"
+ OpName %22 "_ucol"
+ OpName %26 "ANGLEDepthRangeParams"
+ OpMemberName %26 0 "near"
+ OpMemberName %26 1 "far"
+ OpMemberName %26 2 "diff"
+ OpMemberName %26 3 "reserved"
+ OpName %27 "ANGLEUniformBlock"
+ OpMemberName %27 0 "viewport"
+ OpMemberName %27 1 "clipDistancesEnabled"
+ OpMemberName %27 2 "xfbActiveUnpaused"
+ OpMemberName %27 3 "xfbVerticesPerInstance"
+ OpMemberName %27 4 "numSamples"
+ OpMemberName %27 5 "xfbBufferOffsets"
+ OpMemberName %27 6 "acbBufferOffsets"
+ OpMemberName %27 7 "depthRange"
+ OpName %29 "ANGLEUniforms"
+ OpName %33 "_uc"
+ OpName %32 "_uh"
+ OpName %49 "_ux"
+ OpName %50 "_uy"
+ OpName %48 "_ui"
+ OpName %63 "main"
+ OpName %65 "param"
+ OpName %68 "param"
+ OpName %73 "param"
+-OpDecorate %4 Location 0
++OpDecorate %4 Location 1
+ OpDecorate %8 RelaxedPrecision
+-OpDecorate %8 DescriptorSet 0
++OpDecorate %8 DescriptorSet 2
+ OpDecorate %8 Binding 0
+-OpDecorate %11 DescriptorSet 0
++OpDecorate %11 DescriptorSet 3
+-OpDecorate %11 Binding 1
++OpDecorate %11 Binding 0
+ OpMemberDecorate %12 0 Offset 0
+ OpMemberDecorate %12 0 RelaxedPrecision
+ OpDecorate %12 Block
+-OpDecorate %14 DescriptorSet 0
++OpDecorate %14 DescriptorSet 3
+-OpDecorate %14 Binding 2
++OpDecorate %14 Binding 1
+ OpMemberDecorate %15 0 Offset 0
+ OpMemberDecorate %15 0 RelaxedPrecision
+ OpDecorate %15 BufferBlock
+-OpDecorate %20 DescriptorSet 0
++OpDecorate %20 DescriptorSet 3
+-OpDecorate %20 Binding 3
++OpDecorate %20 Binding 2
+ OpDecorate %22 RelaxedPrecision
+-OpDecorate %22 Location 0
++OpDecorate %22 Location 1
+ OpMemberDecorate %26 0 Offset 0
+ OpMemberDecorate %26 1 Offset 4
+ OpMemberDecorate %26 2 Offset 8
+ OpMemberDecorate %26 3 Offset 12
+ OpMemberDecorate %27 0 Offset 0
+ OpMemberDecorate %27 1 Offset 16
+ OpMemberDecorate %27 2 Offset 20
+ OpMemberDecorate %27 3 Offset 24
+ OpMemberDecorate %27 4 Offset 28
+ OpMemberDecorate %27 5 Offset 32
+ OpMemberDecorate %27 6 Offset 48
+ OpMemberDecorate %27 7 Offset 64
+ OpMemberDecorate %27 2 RelaxedPrecision
+ OpMemberDecorate %27 4 RelaxedPrecision
+ OpDecorate %27 Block
+ OpDecorate %29 DescriptorSet 0
+-OpDecorate %29 Binding 4
++OpDecorate %29 Binding 0
+ OpDecorate %32 RelaxedPrecision
+ OpDecorate %33 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %38 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %43 RelaxedPrecision
+ OpDecorate %48 RelaxedPrecision
+ OpDecorate %49 RelaxedPrecision
+ OpDecorate %50 RelaxedPrecision
+ OpDecorate %52 RelaxedPrecision
+ OpDecorate %53 RelaxedPrecision
+ OpDecorate %54 RelaxedPrecision
+ OpDecorate %55 RelaxedPrecision
+ OpDecorate %56 RelaxedPrecision
+ OpDecorate %57 RelaxedPrecision
+ OpDecorate %58 RelaxedPrecision
+ OpDecorate %59 RelaxedPrecision
+ OpDecorate %60 RelaxedPrecision
+ OpDecorate %67 RelaxedPrecision
+ OpDecorate %68 RelaxedPrecision
+ OpDecorate %72 RelaxedPrecision
+ OpDecorate %73 RelaxedPrecision
+ OpDecorate %75 RelaxedPrecision
+ OpDecorate %76 RelaxedPrecision
+ OpDecorate %77 RelaxedPrecision
+ OpDecorate %80 RelaxedPrecision
+ OpDecorate %81 RelaxedPrecision
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %5 = OpTypeImage %1 2D 0 0 0 1 Unknown
+ %6 = OpTypeSampledImage %5
+ %9 = OpTypeImage %1 2D 0 0 0 2 Rgba8
+ %12 = OpTypeStruct %2
+ %15 = OpTypeStruct %2
+ %16 = OpTypeInt 32 0
+ %17 = OpConstant %16 2
+ %18 = OpTypeArray %15 %17
+ %23 = OpTypeInt 32 1
+ %24 = OpTypeVector %23 4
+ %25 = OpTypeVector %16 4
+ %26 = OpTypeStruct %1 %1 %1 %1
+ %27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26
+ %35 = OpTypeVector %1 2
+ %40 = OpTypeVector %23 2
+ %61 = OpTypeVoid
+ %69 = OpConstant %16 0
+ %78 = OpConstant %16 1
++%82 = OpTypePointer Private %2
+ %3 = OpTypePointer Input %2
+ %7 = OpTypePointer UniformConstant %6
+ %10 = OpTypePointer UniformConstant %9
+ %13 = OpTypePointer Uniform %12
+ %19 = OpTypePointer Uniform %18
++%83 = OpTypePointer Private %2
+ %21 = OpTypePointer Output %2
+ %28 = OpTypePointer Uniform %27
+ %30 = OpTypePointer Function %2
+ %70 = OpTypePointer Uniform %2
+ %31 = OpTypeFunction %2 %30
+ %47 = OpTypeFunction %2 %30 %30
+ %62 = OpTypeFunction %61
+ %4 = OpVariable %3 Input
+ %8 = OpVariable %7 UniformConstant
+ %11 = OpVariable %10 UniformConstant
+ %14 = OpVariable %13 Uniform
+ %20 = OpVariable %19 Uniform
+ %22 = OpVariable %21 Output
+ %29 = OpVariable %28 Uniform
++%84 = OpConstant %23 0
++%85 = OpConstant %1 0.5
+ %32 = OpFunction %2 None %31
+ %33 = OpFunctionParameter %30
+ %34 = OpLabel
+ %36 = OpLoad %6 %8
+ %37 = OpLoad %2 %33
+ %38 = OpVectorShuffle %35 %37 %37 0 1
+ %39 = OpImageSampleImplicitLod %2 %36 %38
+ %41 = OpLoad %2 %33
+ %42 = OpVectorShuffle %35 %41 %41 2 3
+ %43 = OpConvertFToS %40 %42
+ %44 = OpLoad %9 %11
+ %45 = OpImageRead %2 %44 %43
+ %46 = OpFAdd %2 %39 %45
+ OpReturnValue %46
+ OpFunctionEnd
+ %48 = OpFunction %2 None %47
+ %49 = OpFunctionParameter %30
+ %50 = OpFunctionParameter %30
+ %51 = OpLabel
+ %52 = OpLoad %2 %49
+ %53 = OpVectorShuffle %35 %52 %52 0 1
+ %54 = OpLoad %2 %50
+ %55 = OpVectorShuffle %35 %54 %54 2 3
+ %56 = OpCompositeExtract %1 %53 0
+ %57 = OpCompositeExtract %1 %53 1
+ %58 = OpCompositeExtract %1 %55 0
+ %59 = OpCompositeExtract %1 %55 1
+ %60 = OpCompositeConstruct %2 %56 %57 %58 %59
+ OpReturnValue %60
+ OpFunctionEnd
+ %63 = OpFunction %61 None %62
+ %64 = OpLabel
+ %65 = OpVariable %30 Function
+ %68 = OpVariable %30 Function
+ %73 = OpVariable %30 Function
+ %66 = OpLoad %2 %4
+ OpStore %65 %66
+ %67 = OpFunctionCall %2 %32 %65
+ %71 = OpAccessChain %70 %14 %69
+ %72 = OpLoad %2 %71
+ OpStore %68 %72
+ %74 = OpAccessChain %70 %20 %69 %69
+ %75 = OpLoad %2 %74
+ OpStore %73 %75
+ %76 = OpFunctionCall %2 %48 %68 %73
+ %77 = OpFAdd %2 %67 %76
+ %79 = OpAccessChain %70 %20 %78 %69
+ %80 = OpLoad %2 %79
+ %81 = OpFAdd %2 %77 %80
+ OpStore %22 %81
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ options.ignore_location = true;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, DifferentDecorationsFragmentIgnoreSetBindingLocation) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 82
++; Bound: 86
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %63 "main" %4 %22
+ OpExecutionMode %63 OriginUpperLeft
+ OpSource GLSL 450
+ OpName %4 "_ue"
+ OpName %8 "_uf"
+ OpName %11 "_ug"
+ OpName %12 "_uA"
+ OpMemberName %12 0 "_ux"
+ OpName %14 "_uc"
+ OpName %15 "_uB"
+ OpMemberName %15 0 "_ux"
+ OpName %20 "_ud"
+ OpName %22 "_ucol"
+ OpName %26 "ANGLEDepthRangeParams"
+ OpMemberName %26 0 "near"
+ OpMemberName %26 1 "far"
+ OpMemberName %26 2 "diff"
+ OpMemberName %26 3 "reserved"
+ OpName %27 "ANGLEUniformBlock"
+ OpMemberName %27 0 "viewport"
+ OpMemberName %27 1 "clipDistancesEnabled"
+ OpMemberName %27 2 "xfbActiveUnpaused"
+ OpMemberName %27 3 "xfbVerticesPerInstance"
+ OpMemberName %27 4 "numSamples"
+ OpMemberName %27 5 "xfbBufferOffsets"
+ OpMemberName %27 6 "acbBufferOffsets"
+ OpMemberName %27 7 "depthRange"
+ OpName %29 "ANGLEUniforms"
+ OpName %33 "_uc"
+ OpName %32 "_uh"
+ OpName %49 "_ux"
+ OpName %50 "_uy"
+ OpName %48 "_ui"
+ OpName %63 "main"
+ OpName %65 "param"
+ OpName %68 "param"
+ OpName %73 "param"
+-OpDecorate %4 Location 0
++OpDecorate %4 Location 1
+ OpDecorate %8 RelaxedPrecision
+-OpDecorate %8 DescriptorSet 0
++OpDecorate %8 DescriptorSet 2
+ OpDecorate %8 Binding 0
+-OpDecorate %11 DescriptorSet 0
++OpDecorate %11 DescriptorSet 3
+-OpDecorate %11 Binding 1
++OpDecorate %11 Binding 0
+ OpMemberDecorate %12 0 Offset 0
+ OpMemberDecorate %12 0 RelaxedPrecision
+ OpDecorate %12 Block
+-OpDecorate %14 DescriptorSet 0
++OpDecorate %14 DescriptorSet 3
+-OpDecorate %14 Binding 2
++OpDecorate %14 Binding 1
+ OpMemberDecorate %15 0 Offset 0
+ OpMemberDecorate %15 0 RelaxedPrecision
+ OpDecorate %15 BufferBlock
+-OpDecorate %20 DescriptorSet 0
++OpDecorate %20 DescriptorSet 3
+-OpDecorate %20 Binding 3
++OpDecorate %20 Binding 2
+ OpDecorate %22 RelaxedPrecision
+-OpDecorate %22 Location 0
++OpDecorate %22 Location 1
+ OpMemberDecorate %26 0 Offset 0
+ OpMemberDecorate %26 1 Offset 4
+ OpMemberDecorate %26 2 Offset 8
+ OpMemberDecorate %26 3 Offset 12
+ OpMemberDecorate %27 0 Offset 0
+ OpMemberDecorate %27 1 Offset 16
+ OpMemberDecorate %27 2 Offset 20
+ OpMemberDecorate %27 3 Offset 24
+ OpMemberDecorate %27 4 Offset 28
+ OpMemberDecorate %27 5 Offset 32
+ OpMemberDecorate %27 6 Offset 48
+ OpMemberDecorate %27 7 Offset 64
+ OpMemberDecorate %27 2 RelaxedPrecision
+ OpMemberDecorate %27 4 RelaxedPrecision
+ OpDecorate %27 Block
+ OpDecorate %29 DescriptorSet 0
+-OpDecorate %29 Binding 4
++OpDecorate %29 Binding 0
+ OpDecorate %32 RelaxedPrecision
+ OpDecorate %33 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %38 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %43 RelaxedPrecision
+ OpDecorate %48 RelaxedPrecision
+ OpDecorate %49 RelaxedPrecision
+ OpDecorate %50 RelaxedPrecision
+ OpDecorate %52 RelaxedPrecision
+ OpDecorate %53 RelaxedPrecision
+ OpDecorate %54 RelaxedPrecision
+ OpDecorate %55 RelaxedPrecision
+ OpDecorate %56 RelaxedPrecision
+ OpDecorate %57 RelaxedPrecision
+ OpDecorate %58 RelaxedPrecision
+ OpDecorate %59 RelaxedPrecision
+ OpDecorate %60 RelaxedPrecision
+ OpDecorate %67 RelaxedPrecision
+ OpDecorate %68 RelaxedPrecision
+ OpDecorate %72 RelaxedPrecision
+ OpDecorate %73 RelaxedPrecision
+ OpDecorate %75 RelaxedPrecision
+ OpDecorate %76 RelaxedPrecision
+ OpDecorate %77 RelaxedPrecision
+ OpDecorate %80 RelaxedPrecision
+ OpDecorate %81 RelaxedPrecision
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %5 = OpTypeImage %1 2D 0 0 0 1 Unknown
+ %6 = OpTypeSampledImage %5
+ %9 = OpTypeImage %1 2D 0 0 0 2 Rgba8
+ %12 = OpTypeStruct %2
+ %15 = OpTypeStruct %2
+ %16 = OpTypeInt 32 0
+ %17 = OpConstant %16 2
+ %18 = OpTypeArray %15 %17
+ %23 = OpTypeInt 32 1
+ %24 = OpTypeVector %23 4
+ %25 = OpTypeVector %16 4
+ %26 = OpTypeStruct %1 %1 %1 %1
+ %27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26
+ %35 = OpTypeVector %1 2
+ %40 = OpTypeVector %23 2
+ %61 = OpTypeVoid
+ %69 = OpConstant %16 0
+ %78 = OpConstant %16 1
++%82 = OpTypePointer Private %2
+ %3 = OpTypePointer Input %2
+ %7 = OpTypePointer UniformConstant %6
+ %10 = OpTypePointer UniformConstant %9
+ %13 = OpTypePointer Uniform %12
+ %19 = OpTypePointer Uniform %18
++%83 = OpTypePointer Private %2
+ %21 = OpTypePointer Output %2
+ %28 = OpTypePointer Uniform %27
+ %30 = OpTypePointer Function %2
+ %70 = OpTypePointer Uniform %2
+ %31 = OpTypeFunction %2 %30
+ %47 = OpTypeFunction %2 %30 %30
+ %62 = OpTypeFunction %61
+ %4 = OpVariable %3 Input
+ %8 = OpVariable %7 UniformConstant
+ %11 = OpVariable %10 UniformConstant
+ %14 = OpVariable %13 Uniform
+ %20 = OpVariable %19 Uniform
+ %22 = OpVariable %21 Output
+ %29 = OpVariable %28 Uniform
++%84 = OpConstant %23 0
++%85 = OpConstant %1 0.5
+ %32 = OpFunction %2 None %31
+ %33 = OpFunctionParameter %30
+ %34 = OpLabel
+ %36 = OpLoad %6 %8
+ %37 = OpLoad %2 %33
+ %38 = OpVectorShuffle %35 %37 %37 0 1
+ %39 = OpImageSampleImplicitLod %2 %36 %38
+ %41 = OpLoad %2 %33
+ %42 = OpVectorShuffle %35 %41 %41 2 3
+ %43 = OpConvertFToS %40 %42
+ %44 = OpLoad %9 %11
+ %45 = OpImageRead %2 %44 %43
+ %46 = OpFAdd %2 %39 %45
+ OpReturnValue %46
+ OpFunctionEnd
+ %48 = OpFunction %2 None %47
+ %49 = OpFunctionParameter %30
+ %50 = OpFunctionParameter %30
+ %51 = OpLabel
+ %52 = OpLoad %2 %49
+ %53 = OpVectorShuffle %35 %52 %52 0 1
+ %54 = OpLoad %2 %50
+ %55 = OpVectorShuffle %35 %54 %54 2 3
+ %56 = OpCompositeExtract %1 %53 0
+ %57 = OpCompositeExtract %1 %53 1
+ %58 = OpCompositeExtract %1 %55 0
+ %59 = OpCompositeExtract %1 %55 1
+ %60 = OpCompositeConstruct %2 %56 %57 %58 %59
+ OpReturnValue %60
+ OpFunctionEnd
+ %63 = OpFunction %61 None %62
+ %64 = OpLabel
+ %65 = OpVariable %30 Function
+ %68 = OpVariable %30 Function
+ %73 = OpVariable %30 Function
+ %66 = OpLoad %2 %4
+ OpStore %65 %66
+ %67 = OpFunctionCall %2 %32 %65
+ %71 = OpAccessChain %70 %14 %69
+ %72 = OpLoad %2 %71
+ OpStore %68 %72
+ %74 = OpAccessChain %70 %20 %69 %69
+ %75 = OpLoad %2 %74
+ OpStore %73 %75
+ %76 = OpFunctionCall %2 %48 %68 %73
+ %77 = OpFAdd %2 %67 %76
+ %79 = OpAccessChain %70 %20 %78 %69
+ %80 = OpLoad %2 %79
+ %81 = OpFAdd %2 %77 %80
+ OpStore %22 %81
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ options.ignore_set_binding = true;
+ options.ignore_location = true;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/different_decorations_fragment_dst.spvasm b/test/diff/diff_files/different_decorations_fragment_dst.spvasm
new file mode 100644
index 00000000..b5d1c9f7
--- /dev/null
+++ b/test/diff/diff_files/different_decorations_fragment_dst.spvasm
@@ -0,0 +1,199 @@
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %63 "main" %4 %22
+OpExecutionMode %63 OriginUpperLeft
+OpSource GLSL 450
+OpName %4 "_ue"
+OpName %8 "_uf"
+OpName %11 "_ug"
+OpName %12 "_uA"
+OpMemberName %12 0 "_ux"
+OpName %14 "_uc"
+OpName %15 "_uB"
+OpMemberName %15 0 "_ux"
+OpName %20 "_ud"
+OpName %22 "_ucol"
+OpName %26 "ANGLEDepthRangeParams"
+OpMemberName %26 0 "near"
+OpMemberName %26 1 "far"
+OpMemberName %26 2 "diff"
+OpMemberName %26 3 "reserved"
+OpName %27 "ANGLEUniformBlock"
+OpMemberName %27 0 "viewport"
+OpMemberName %27 1 "clipDistancesEnabled"
+OpMemberName %27 2 "xfbActiveUnpaused"
+OpMemberName %27 3 "xfbVerticesPerInstance"
+OpMemberName %27 4 "numSamples"
+OpMemberName %27 5 "xfbBufferOffsets"
+OpMemberName %27 6 "acbBufferOffsets"
+OpMemberName %27 7 "depthRange"
+OpName %29 "ANGLEUniforms"
+OpName %33 "_uc"
+OpName %32 "_uh"
+OpName %49 "_ux"
+OpName %50 "_uy"
+OpName %48 "_ui"
+OpName %63 "main"
+OpName %65 "param"
+OpName %68 "param"
+OpName %73 "param"
+OpDecorate %4 Location 1
+OpDecorate %8 RelaxedPrecision
+OpDecorate %8 DescriptorSet 2
+OpDecorate %8 Binding 0
+OpDecorate %11 DescriptorSet 3
+OpDecorate %11 Binding 0
+OpMemberDecorate %12 0 Offset 0
+OpMemberDecorate %12 0 RelaxedPrecision
+OpDecorate %12 Block
+OpDecorate %14 DescriptorSet 3
+OpDecorate %14 Binding 1
+OpMemberDecorate %15 0 Offset 0
+OpMemberDecorate %15 0 RelaxedPrecision
+OpDecorate %15 BufferBlock
+OpDecorate %20 DescriptorSet 3
+OpDecorate %20 Binding 2
+OpDecorate %22 RelaxedPrecision
+OpDecorate %22 Location 1
+OpMemberDecorate %26 0 Offset 0
+OpMemberDecorate %26 1 Offset 4
+OpMemberDecorate %26 2 Offset 8
+OpMemberDecorate %26 3 Offset 12
+OpMemberDecorate %27 0 Offset 0
+OpMemberDecorate %27 1 Offset 16
+OpMemberDecorate %27 2 Offset 20
+OpMemberDecorate %27 3 Offset 24
+OpMemberDecorate %27 4 Offset 28
+OpMemberDecorate %27 5 Offset 32
+OpMemberDecorate %27 6 Offset 48
+OpMemberDecorate %27 7 Offset 64
+OpMemberDecorate %27 2 RelaxedPrecision
+OpMemberDecorate %27 4 RelaxedPrecision
+OpDecorate %27 Block
+OpDecorate %29 DescriptorSet 0
+OpDecorate %29 Binding 0
+OpDecorate %32 RelaxedPrecision
+OpDecorate %33 RelaxedPrecision
+OpDecorate %36 RelaxedPrecision
+OpDecorate %37 RelaxedPrecision
+OpDecorate %38 RelaxedPrecision
+OpDecorate %39 RelaxedPrecision
+OpDecorate %41 RelaxedPrecision
+OpDecorate %42 RelaxedPrecision
+OpDecorate %43 RelaxedPrecision
+OpDecorate %48 RelaxedPrecision
+OpDecorate %49 RelaxedPrecision
+OpDecorate %50 RelaxedPrecision
+OpDecorate %52 RelaxedPrecision
+OpDecorate %53 RelaxedPrecision
+OpDecorate %54 RelaxedPrecision
+OpDecorate %55 RelaxedPrecision
+OpDecorate %56 RelaxedPrecision
+OpDecorate %57 RelaxedPrecision
+OpDecorate %58 RelaxedPrecision
+OpDecorate %59 RelaxedPrecision
+OpDecorate %60 RelaxedPrecision
+OpDecorate %67 RelaxedPrecision
+OpDecorate %68 RelaxedPrecision
+OpDecorate %72 RelaxedPrecision
+OpDecorate %73 RelaxedPrecision
+OpDecorate %75 RelaxedPrecision
+OpDecorate %76 RelaxedPrecision
+OpDecorate %77 RelaxedPrecision
+OpDecorate %80 RelaxedPrecision
+OpDecorate %81 RelaxedPrecision
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeImage %1 2D 0 0 0 1 Unknown
+%6 = OpTypeSampledImage %5
+%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8
+%12 = OpTypeStruct %2
+%15 = OpTypeStruct %2
+%16 = OpTypeInt 32 0
+%17 = OpConstant %16 2
+%18 = OpTypeArray %15 %17
+%23 = OpTypeInt 32 1
+%24 = OpTypeVector %23 4
+%25 = OpTypeVector %16 4
+%26 = OpTypeStruct %1 %1 %1 %1
+%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26
+%35 = OpTypeVector %1 2
+%40 = OpTypeVector %23 2
+%61 = OpTypeVoid
+%69 = OpConstant %16 0
+%78 = OpConstant %16 1
+%82 = OpTypePointer Private %2
+%3 = OpTypePointer Input %2
+%7 = OpTypePointer UniformConstant %6
+%10 = OpTypePointer UniformConstant %9
+%13 = OpTypePointer Uniform %12
+%19 = OpTypePointer Uniform %18
+%83 = OpTypePointer Private %2
+%21 = OpTypePointer Output %2
+%28 = OpTypePointer Uniform %27
+%30 = OpTypePointer Function %2
+%70 = OpTypePointer Uniform %2
+%31 = OpTypeFunction %2 %30
+%47 = OpTypeFunction %2 %30 %30
+%62 = OpTypeFunction %61
+%4 = OpVariable %3 Input
+%8 = OpVariable %7 UniformConstant
+%11 = OpVariable %10 UniformConstant
+%14 = OpVariable %13 Uniform
+%20 = OpVariable %19 Uniform
+%22 = OpVariable %21 Output
+%29 = OpVariable %28 Uniform
+%84 = OpConstant %23 0
+%85 = OpConstant %1 0.5
+%32 = OpFunction %2 None %31
+%33 = OpFunctionParameter %30
+%34 = OpLabel
+%36 = OpLoad %6 %8
+%37 = OpLoad %2 %33
+%38 = OpVectorShuffle %35 %37 %37 0 1
+%39 = OpImageSampleImplicitLod %2 %36 %38
+%41 = OpLoad %2 %33
+%42 = OpVectorShuffle %35 %41 %41 2 3
+%43 = OpConvertFToS %40 %42
+%44 = OpLoad %9 %11
+%45 = OpImageRead %2 %44 %43
+%46 = OpFAdd %2 %39 %45
+OpReturnValue %46
+OpFunctionEnd
+%48 = OpFunction %2 None %47
+%49 = OpFunctionParameter %30
+%50 = OpFunctionParameter %30
+%51 = OpLabel
+%52 = OpLoad %2 %49
+%53 = OpVectorShuffle %35 %52 %52 0 1
+%54 = OpLoad %2 %50
+%55 = OpVectorShuffle %35 %54 %54 2 3
+%56 = OpCompositeExtract %1 %53 0
+%57 = OpCompositeExtract %1 %53 1
+%58 = OpCompositeExtract %1 %55 0
+%59 = OpCompositeExtract %1 %55 1
+%60 = OpCompositeConstruct %2 %56 %57 %58 %59
+OpReturnValue %60
+OpFunctionEnd
+%63 = OpFunction %61 None %62
+%64 = OpLabel
+%65 = OpVariable %30 Function
+%68 = OpVariable %30 Function
+%73 = OpVariable %30 Function
+%66 = OpLoad %2 %4
+OpStore %65 %66
+%67 = OpFunctionCall %2 %32 %65
+%71 = OpAccessChain %70 %14 %69
+%72 = OpLoad %2 %71
+OpStore %68 %72
+%74 = OpAccessChain %70 %20 %69 %69
+%75 = OpLoad %2 %74
+OpStore %73 %75
+%76 = OpFunctionCall %2 %48 %68 %73
+%77 = OpFAdd %2 %67 %76
+%79 = OpAccessChain %70 %20 %78 %69
+%80 = OpLoad %2 %79
+%81 = OpFAdd %2 %77 %80
+OpStore %22 %81
+OpReturn
+OpFunctionEnd
diff --git a/test/diff/diff_files/different_decorations_fragment_src.spvasm b/test/diff/diff_files/different_decorations_fragment_src.spvasm
new file mode 100644
index 00000000..2c8cd644
--- /dev/null
+++ b/test/diff/diff_files/different_decorations_fragment_src.spvasm
@@ -0,0 +1,198 @@
+;; Test where variable set/binding/location decorations are different between
+;; src and dst fragment shaders.
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %63 "main" %4 %22
+OpExecutionMode %63 OriginUpperLeft
+OpSource GLSL 450
+OpName %4 "_ue"
+OpName %8 "_uf"
+OpName %11 "_ug"
+OpName %12 "_uA"
+OpMemberName %12 0 "_ux"
+OpName %14 "_uc"
+OpName %15 "_uB"
+OpMemberName %15 0 "_ux"
+OpName %20 "_ud"
+OpName %22 "_ucol"
+OpName %26 "ANGLEDepthRangeParams"
+OpMemberName %26 0 "near"
+OpMemberName %26 1 "far"
+OpMemberName %26 2 "diff"
+OpMemberName %26 3 "reserved"
+OpName %27 "ANGLEUniformBlock"
+OpMemberName %27 0 "viewport"
+OpMemberName %27 1 "clipDistancesEnabled"
+OpMemberName %27 2 "xfbActiveUnpaused"
+OpMemberName %27 3 "xfbVerticesPerInstance"
+OpMemberName %27 4 "numSamples"
+OpMemberName %27 5 "xfbBufferOffsets"
+OpMemberName %27 6 "acbBufferOffsets"
+OpMemberName %27 7 "depthRange"
+OpName %29 "ANGLEUniforms"
+OpName %33 "_uc"
+OpName %32 "_uh"
+OpName %49 "_ux"
+OpName %50 "_uy"
+OpName %48 "_ui"
+OpName %63 "main"
+OpName %65 "param"
+OpName %68 "param"
+OpName %73 "param"
+OpDecorate %4 Location 0
+OpDecorate %8 RelaxedPrecision
+OpDecorate %8 DescriptorSet 0
+OpDecorate %8 Binding 0
+OpDecorate %11 DescriptorSet 0
+OpDecorate %11 Binding 1
+OpMemberDecorate %12 0 Offset 0
+OpMemberDecorate %12 0 RelaxedPrecision
+OpDecorate %12 Block
+OpDecorate %14 DescriptorSet 0
+OpDecorate %14 Binding 2
+OpMemberDecorate %15 0 Offset 0
+OpMemberDecorate %15 0 RelaxedPrecision
+OpDecorate %15 BufferBlock
+OpDecorate %20 DescriptorSet 0
+OpDecorate %20 Binding 3
+OpDecorate %22 RelaxedPrecision
+OpDecorate %22 Location 0
+OpMemberDecorate %26 0 Offset 0
+OpMemberDecorate %26 1 Offset 4
+OpMemberDecorate %26 2 Offset 8
+OpMemberDecorate %26 3 Offset 12
+OpMemberDecorate %27 0 Offset 0
+OpMemberDecorate %27 1 Offset 16
+OpMemberDecorate %27 2 Offset 20
+OpMemberDecorate %27 3 Offset 24
+OpMemberDecorate %27 4 Offset 28
+OpMemberDecorate %27 5 Offset 32
+OpMemberDecorate %27 6 Offset 48
+OpMemberDecorate %27 7 Offset 64
+OpMemberDecorate %27 2 RelaxedPrecision
+OpMemberDecorate %27 4 RelaxedPrecision
+OpDecorate %27 Block
+OpDecorate %29 DescriptorSet 0
+OpDecorate %29 Binding 4
+OpDecorate %32 RelaxedPrecision
+OpDecorate %33 RelaxedPrecision
+OpDecorate %36 RelaxedPrecision
+OpDecorate %37 RelaxedPrecision
+OpDecorate %38 RelaxedPrecision
+OpDecorate %39 RelaxedPrecision
+OpDecorate %41 RelaxedPrecision
+OpDecorate %42 RelaxedPrecision
+OpDecorate %43 RelaxedPrecision
+OpDecorate %48 RelaxedPrecision
+OpDecorate %49 RelaxedPrecision
+OpDecorate %50 RelaxedPrecision
+OpDecorate %52 RelaxedPrecision
+OpDecorate %53 RelaxedPrecision
+OpDecorate %54 RelaxedPrecision
+OpDecorate %55 RelaxedPrecision
+OpDecorate %56 RelaxedPrecision
+OpDecorate %57 RelaxedPrecision
+OpDecorate %58 RelaxedPrecision
+OpDecorate %59 RelaxedPrecision
+OpDecorate %60 RelaxedPrecision
+OpDecorate %67 RelaxedPrecision
+OpDecorate %68 RelaxedPrecision
+OpDecorate %72 RelaxedPrecision
+OpDecorate %73 RelaxedPrecision
+OpDecorate %75 RelaxedPrecision
+OpDecorate %76 RelaxedPrecision
+OpDecorate %77 RelaxedPrecision
+OpDecorate %80 RelaxedPrecision
+OpDecorate %81 RelaxedPrecision
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeImage %1 2D 0 0 0 1 Unknown
+%6 = OpTypeSampledImage %5
+%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8
+%12 = OpTypeStruct %2
+%15 = OpTypeStruct %2
+%16 = OpTypeInt 32 0
+%17 = OpConstant %16 2
+%18 = OpTypeArray %15 %17
+%23 = OpTypeInt 32 1
+%24 = OpTypeVector %23 4
+%25 = OpTypeVector %16 4
+%26 = OpTypeStruct %1 %1 %1 %1
+%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26
+%35 = OpTypeVector %1 2
+%40 = OpTypeVector %23 2
+%61 = OpTypeVoid
+%69 = OpConstant %16 0
+%78 = OpConstant %16 1
+%3 = OpTypePointer Input %2
+%7 = OpTypePointer UniformConstant %6
+%10 = OpTypePointer UniformConstant %9
+%13 = OpTypePointer Uniform %12
+%19 = OpTypePointer Uniform %18
+%21 = OpTypePointer Output %2
+%28 = OpTypePointer Uniform %27
+%30 = OpTypePointer Function %2
+%70 = OpTypePointer Uniform %2
+%31 = OpTypeFunction %2 %30
+%47 = OpTypeFunction %2 %30 %30
+%62 = OpTypeFunction %61
+%4 = OpVariable %3 Input
+%8 = OpVariable %7 UniformConstant
+%11 = OpVariable %10 UniformConstant
+%14 = OpVariable %13 Uniform
+%20 = OpVariable %19 Uniform
+%22 = OpVariable %21 Output
+%29 = OpVariable %28 Uniform
+%32 = OpFunction %2 None %31
+%33 = OpFunctionParameter %30
+%34 = OpLabel
+%36 = OpLoad %6 %8
+%37 = OpLoad %2 %33
+%38 = OpVectorShuffle %35 %37 %37 0 1
+%39 = OpImageSampleImplicitLod %2 %36 %38
+%41 = OpLoad %2 %33
+%42 = OpVectorShuffle %35 %41 %41 2 3
+%43 = OpConvertFToS %40 %42
+%44 = OpLoad %9 %11
+%45 = OpImageRead %2 %44 %43
+%46 = OpFAdd %2 %39 %45
+OpReturnValue %46
+OpFunctionEnd
+%48 = OpFunction %2 None %47
+%49 = OpFunctionParameter %30
+%50 = OpFunctionParameter %30
+%51 = OpLabel
+%52 = OpLoad %2 %49
+%53 = OpVectorShuffle %35 %52 %52 0 1
+%54 = OpLoad %2 %50
+%55 = OpVectorShuffle %35 %54 %54 2 3
+%56 = OpCompositeExtract %1 %53 0
+%57 = OpCompositeExtract %1 %53 1
+%58 = OpCompositeExtract %1 %55 0
+%59 = OpCompositeExtract %1 %55 1
+%60 = OpCompositeConstruct %2 %56 %57 %58 %59
+OpReturnValue %60
+OpFunctionEnd
+%63 = OpFunction %61 None %62
+%64 = OpLabel
+%65 = OpVariable %30 Function
+%68 = OpVariable %30 Function
+%73 = OpVariable %30 Function
+%66 = OpLoad %2 %4
+OpStore %65 %66
+%67 = OpFunctionCall %2 %32 %65
+%71 = OpAccessChain %70 %14 %69
+%72 = OpLoad %2 %71
+OpStore %68 %72
+%74 = OpAccessChain %70 %20 %69 %69
+%75 = OpLoad %2 %74
+OpStore %73 %75
+%76 = OpFunctionCall %2 %48 %68 %73
+%77 = OpFAdd %2 %67 %76
+%79 = OpAccessChain %70 %20 %78 %69
+%80 = OpLoad %2 %79
+%81 = OpFAdd %2 %77 %80
+OpStore %22 %81
+OpReturn
+OpFunctionEnd
+
diff --git a/test/diff/diff_files/different_decorations_vertex_autogen.cpp b/test/diff/diff_files/different_decorations_vertex_autogen.cpp
new file mode 100644
index 00000000..f65ee5a1
--- /dev/null
+++ b/test/diff/diff_files/different_decorations_vertex_autogen.cpp
@@ -0,0 +1,1322 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Test where variable set/binding/location decorations are different between
+// src and dst vertex shaders.
+constexpr char kSrc[] = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25
+OpSource GLSL 450
+OpName %4 "_ub"
+OpName %5 "_uc"
+OpName %6 "_ud"
+OpName %8 "_ue"
+OpName %9 "defaultUniformsVS"
+OpMemberName %9 0 "_ua"
+OpName %11 ""
+OpName %16 "ANGLEDepthRangeParams"
+OpMemberName %16 0 "near"
+OpMemberName %16 1 "far"
+OpMemberName %16 2 "diff"
+OpMemberName %16 3 "reserved"
+OpName %17 "ANGLEUniformBlock"
+OpMemberName %17 0 "viewport"
+OpMemberName %17 1 "clipDistancesEnabled"
+OpMemberName %17 2 "xfbActiveUnpaused"
+OpMemberName %17 3 "xfbVerticesPerInstance"
+OpMemberName %17 4 "numSamples"
+OpMemberName %17 5 "xfbBufferOffsets"
+OpMemberName %17 6 "acbBufferOffsets"
+OpMemberName %17 7 "depthRange"
+OpName %19 "ANGLEUniforms"
+OpName %20 "ANGLEXfbPosition"
+OpName %23 "gl_PerVertex"
+OpMemberName %23 0 "gl_Position"
+OpMemberName %23 1 "gl_PointSize"
+OpMemberName %23 2 "gl_ClipDistance"
+OpMemberName %23 3 "gl_CullDistance"
+OpName %25 ""
+OpName %29 "_ua"
+OpName %28 "_uf"
+OpName %33 "_uf"
+OpName %32 "_ug"
+OpName %40 "main"
+OpName %42 "param"
+OpName %50 "param"
+OpName %53 "param"
+OpDecorate %4 Location 0
+OpDecorate %5 Location 1
+OpDecorate %6 Location 2
+OpDecorate %8 Location 0
+OpMemberDecorate %9 0 Offset 0
+OpDecorate %9 Block
+OpDecorate %11 DescriptorSet 0
+OpDecorate %11 Binding 0
+OpMemberDecorate %16 0 Offset 0
+OpMemberDecorate %16 1 Offset 4
+OpMemberDecorate %16 2 Offset 8
+OpMemberDecorate %16 3 Offset 12
+OpMemberDecorate %17 0 Offset 0
+OpMemberDecorate %17 1 Offset 16
+OpMemberDecorate %17 2 Offset 20
+OpMemberDecorate %17 3 Offset 24
+OpMemberDecorate %17 4 Offset 28
+OpMemberDecorate %17 5 Offset 32
+OpMemberDecorate %17 6 Offset 48
+OpMemberDecorate %17 7 Offset 64
+OpMemberDecorate %17 2 RelaxedPrecision
+OpMemberDecorate %17 4 RelaxedPrecision
+OpDecorate %17 Block
+OpDecorate %19 DescriptorSet 0
+OpDecorate %19 Binding 1
+OpDecorate %20 Location 1
+OpMemberDecorate %23 0 BuiltIn Position
+OpMemberDecorate %23 1 BuiltIn PointSize
+OpMemberDecorate %23 2 BuiltIn ClipDistance
+OpMemberDecorate %23 3 BuiltIn CullDistance
+OpDecorate %23 Block
+OpDecorate %28 RelaxedPrecision
+OpDecorate %29 RelaxedPrecision
+OpDecorate %31 RelaxedPrecision
+OpDecorate %32 RelaxedPrecision
+OpDecorate %33 RelaxedPrecision
+OpDecorate %35 RelaxedPrecision
+OpDecorate %36 RelaxedPrecision
+OpDecorate %37 RelaxedPrecision
+OpDecorate %44 RelaxedPrecision
+OpDecorate %52 RelaxedPrecision
+OpDecorate %55 RelaxedPrecision
+OpDecorate %56 RelaxedPrecision
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%9 = OpTypeStruct %2
+%12 = OpTypeInt 32 0
+%13 = OpTypeInt 32 1
+%14 = OpTypeVector %13 4
+%15 = OpTypeVector %12 4
+%16 = OpTypeStruct %1 %1 %1 %1
+%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16
+%21 = OpConstant %12 8
+%22 = OpTypeArray %1 %21
+%23 = OpTypeStruct %2 %1 %22 %22
+%38 = OpTypeVoid
+%45 = OpConstant %12 0
+%3 = OpTypePointer Input %2
+%7 = OpTypePointer Output %2
+%10 = OpTypePointer Uniform %9
+%18 = OpTypePointer Uniform %17
+%24 = OpTypePointer Output %23
+%26 = OpTypePointer Function %2
+%46 = OpTypePointer Uniform %2
+%27 = OpTypeFunction %2 %26
+%39 = OpTypeFunction %38
+%4 = OpVariable %3 Input
+%5 = OpVariable %3 Input
+%6 = OpVariable %3 Input
+%8 = OpVariable %7 Output
+%11 = OpVariable %10 Uniform
+%19 = OpVariable %18 Uniform
+%20 = OpVariable %7 Output
+%25 = OpVariable %24 Output
+%28 = OpFunction %2 None %27
+%29 = OpFunctionParameter %26
+%30 = OpLabel
+%31 = OpLoad %2 %29
+OpReturnValue %31
+OpFunctionEnd
+%32 = OpFunction %2 None %27
+%33 = OpFunctionParameter %26
+%34 = OpLabel
+%35 = OpLoad %2 %33
+%36 = OpLoad %2 %33
+%37 = OpFAdd %2 %35 %36
+OpReturnValue %37
+OpFunctionEnd
+%40 = OpFunction %38 None %39
+%41 = OpLabel
+%42 = OpVariable %26 Function
+%50 = OpVariable %26 Function
+%53 = OpVariable %26 Function
+%43 = OpLoad %2 %4
+OpStore %42 %43
+%44 = OpFunctionCall %2 %28 %42
+%47 = OpAccessChain %46 %11 %45
+%48 = OpLoad %2 %47
+%49 = OpFAdd %2 %44 %48
+OpStore %8 %49
+%51 = OpLoad %2 %5
+OpStore %50 %51
+%52 = OpFunctionCall %2 %32 %50
+%54 = OpLoad %2 %6
+OpStore %53 %54
+%55 = OpFunctionCall %2 %28 %53
+%56 = OpFAdd %2 %52 %55
+%57 = OpAccessChain %7 %25 %45
+OpStore %57 %56
+OpReturn
+OpFunctionEnd
+)";
+constexpr char kDst[] = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25
+OpSource GLSL 450
+OpName %4 "_ub"
+OpName %5 "_uc"
+OpName %6 "_ud"
+OpName %8 "_ue"
+OpName %9 "defaultUniformsVS"
+OpMemberName %9 0 "_ua"
+OpName %11 ""
+OpName %16 "ANGLEDepthRangeParams"
+OpMemberName %16 0 "near"
+OpMemberName %16 1 "far"
+OpMemberName %16 2 "diff"
+OpMemberName %16 3 "reserved"
+OpName %17 "ANGLEUniformBlock"
+OpMemberName %17 0 "viewport"
+OpMemberName %17 1 "clipDistancesEnabled"
+OpMemberName %17 2 "xfbActiveUnpaused"
+OpMemberName %17 3 "xfbVerticesPerInstance"
+OpMemberName %17 4 "numSamples"
+OpMemberName %17 5 "xfbBufferOffsets"
+OpMemberName %17 6 "acbBufferOffsets"
+OpMemberName %17 7 "depthRange"
+OpName %19 "ANGLEUniforms"
+OpName %23 "gl_PerVertex"
+OpMemberName %23 0 "gl_Position"
+OpName %25 ""
+OpName %29 "_ua"
+OpName %28 "_uf"
+OpName %33 "_uf"
+OpName %32 "_ug"
+OpName %40 "main"
+OpName %42 "param"
+OpName %50 "param"
+OpName %53 "param"
+OpDecorate %4 Location 1
+OpDecorate %5 Location 2
+OpDecorate %6 Location 0
+OpDecorate %8 Location 1
+OpMemberDecorate %9 0 Offset 0
+OpDecorate %9 Block
+OpDecorate %11 DescriptorSet 0
+OpDecorate %11 Binding 1
+OpMemberDecorate %16 0 Offset 0
+OpMemberDecorate %16 1 Offset 4
+OpMemberDecorate %16 2 Offset 8
+OpMemberDecorate %16 3 Offset 12
+OpMemberDecorate %17 0 Offset 0
+OpMemberDecorate %17 1 Offset 16
+OpMemberDecorate %17 2 Offset 20
+OpMemberDecorate %17 3 Offset 24
+OpMemberDecorate %17 4 Offset 28
+OpMemberDecorate %17 5 Offset 32
+OpMemberDecorate %17 6 Offset 48
+OpMemberDecorate %17 7 Offset 64
+OpMemberDecorate %17 2 RelaxedPrecision
+OpMemberDecorate %17 4 RelaxedPrecision
+OpDecorate %17 Block
+OpDecorate %19 DescriptorSet 2
+OpDecorate %19 Binding 0
+OpMemberDecorate %23 0 BuiltIn Position
+OpDecorate %23 Block
+OpDecorate %28 RelaxedPrecision
+OpDecorate %29 RelaxedPrecision
+OpDecorate %31 RelaxedPrecision
+OpDecorate %32 RelaxedPrecision
+OpDecorate %33 RelaxedPrecision
+OpDecorate %35 RelaxedPrecision
+OpDecorate %36 RelaxedPrecision
+OpDecorate %37 RelaxedPrecision
+OpDecorate %44 RelaxedPrecision
+OpDecorate %52 RelaxedPrecision
+OpDecorate %55 RelaxedPrecision
+OpDecorate %56 RelaxedPrecision
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%9 = OpTypeStruct %2
+%12 = OpTypeInt 32 0
+%13 = OpTypeInt 32 1
+%14 = OpTypeVector %13 4
+%15 = OpTypeVector %12 4
+%16 = OpTypeStruct %1 %1 %1 %1
+%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16
+%21 = OpConstant %12 8
+%22 = OpTypeArray %1 %21
+%23 = OpTypeStruct %2
+%38 = OpTypeVoid
+%45 = OpConstant %12 0
+%58 = OpTypePointer Private %2
+%3 = OpTypePointer Input %2
+%59 = OpTypePointer Private %2
+%7 = OpTypePointer Output %2
+%10 = OpTypePointer Uniform %9
+%18 = OpTypePointer Uniform %17
+%24 = OpTypePointer Output %23
+%26 = OpTypePointer Function %2
+%46 = OpTypePointer Uniform %2
+%27 = OpTypeFunction %2 %26
+%39 = OpTypeFunction %38
+%4 = OpVariable %3 Input
+%5 = OpVariable %3 Input
+%6 = OpVariable %3 Input
+%8 = OpVariable %7 Output
+%11 = OpVariable %10 Uniform
+%19 = OpVariable %18 Uniform
+%20 = OpVariable %59 Private
+%25 = OpVariable %24 Output
+%60 = OpConstant %13 0
+%61 = OpConstant %1 0.5
+%28 = OpFunction %2 None %27
+%29 = OpFunctionParameter %26
+%30 = OpLabel
+%31 = OpLoad %2 %29
+OpReturnValue %31
+OpFunctionEnd
+%32 = OpFunction %2 None %27
+%33 = OpFunctionParameter %26
+%34 = OpLabel
+%35 = OpLoad %2 %33
+%36 = OpLoad %2 %33
+%37 = OpFAdd %2 %35 %36
+OpReturnValue %37
+OpFunctionEnd
+%40 = OpFunction %38 None %39
+%41 = OpLabel
+%42 = OpVariable %26 Function
+%50 = OpVariable %26 Function
+%53 = OpVariable %26 Function
+%43 = OpLoad %2 %4
+OpStore %42 %43
+%44 = OpFunctionCall %2 %28 %42
+%47 = OpAccessChain %46 %11 %45
+%48 = OpLoad %2 %47
+%49 = OpFAdd %2 %44 %48
+OpStore %8 %49
+%51 = OpLoad %2 %5
+OpStore %50 %51
+%52 = OpFunctionCall %2 %32 %50
+%54 = OpLoad %2 %6
+OpStore %53 %54
+%55 = OpFunctionCall %2 %28 %53
+%56 = OpFAdd %2 %52 %55
+%57 = OpAccessChain %7 %25 %45
+OpStore %57 %56
+%62 = OpAccessChain %7 %25 %60
+%63 = OpLoad %2 %62
+%64 = OpCompositeExtract %1 %63 0
+%65 = OpCompositeExtract %1 %63 1
+%66 = OpCompositeExtract %1 %63 2
+%67 = OpCompositeExtract %1 %63 3
+%69 = OpFNegate %1 %64
+%70 = OpFAdd %1 %66 %67
+%71 = OpFMul %1 %70 %61
+%68 = OpCompositeConstruct %2 %65 %69 %71 %67
+OpStore %62 %68
+OpReturn
+OpFunctionEnd
+)";
+
+TEST(DiffTest, DifferentDecorationsVertex) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 58
++; Bound: 73
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+-OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25
++OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25
+ OpSource GLSL 450
+ OpName %4 "_ub"
+ OpName %5 "_uc"
+ OpName %6 "_ud"
+ OpName %8 "_ue"
+ OpName %9 "defaultUniformsVS"
+ OpMemberName %9 0 "_ua"
+ OpName %11 ""
+ OpName %16 "ANGLEDepthRangeParams"
+ OpMemberName %16 0 "near"
+ OpMemberName %16 1 "far"
+ OpMemberName %16 2 "diff"
+ OpMemberName %16 3 "reserved"
+ OpName %17 "ANGLEUniformBlock"
+ OpMemberName %17 0 "viewport"
+ OpMemberName %17 1 "clipDistancesEnabled"
+ OpMemberName %17 2 "xfbActiveUnpaused"
+ OpMemberName %17 3 "xfbVerticesPerInstance"
+ OpMemberName %17 4 "numSamples"
+ OpMemberName %17 5 "xfbBufferOffsets"
+ OpMemberName %17 6 "acbBufferOffsets"
+ OpMemberName %17 7 "depthRange"
+ OpName %19 "ANGLEUniforms"
+-OpName %20 "ANGLEXfbPosition"
+ OpName %23 "gl_PerVertex"
+ OpMemberName %23 0 "gl_Position"
+-OpMemberName %23 1 "gl_PointSize"
+-OpMemberName %23 2 "gl_ClipDistance"
+-OpMemberName %23 3 "gl_CullDistance"
+ OpName %25 ""
+ OpName %29 "_ua"
+ OpName %28 "_uf"
+ OpName %33 "_uf"
+ OpName %32 "_ug"
+ OpName %40 "main"
+ OpName %42 "param"
+ OpName %50 "param"
+ OpName %53 "param"
+-OpDecorate %4 Location 0
++OpDecorate %4 Location 1
+-OpDecorate %5 Location 1
++OpDecorate %5 Location 2
+-OpDecorate %6 Location 2
++OpDecorate %6 Location 0
+-OpDecorate %8 Location 0
++OpDecorate %8 Location 1
+ OpMemberDecorate %9 0 Offset 0
+ OpDecorate %9 Block
+ OpDecorate %11 DescriptorSet 0
+-OpDecorate %11 Binding 0
++OpDecorate %11 Binding 1
+ OpMemberDecorate %16 0 Offset 0
+ OpMemberDecorate %16 1 Offset 4
+ OpMemberDecorate %16 2 Offset 8
+ OpMemberDecorate %16 3 Offset 12
+ OpMemberDecorate %17 0 Offset 0
+ OpMemberDecorate %17 1 Offset 16
+ OpMemberDecorate %17 2 Offset 20
+ OpMemberDecorate %17 3 Offset 24
+ OpMemberDecorate %17 4 Offset 28
+ OpMemberDecorate %17 5 Offset 32
+ OpMemberDecorate %17 6 Offset 48
+ OpMemberDecorate %17 7 Offset 64
+ OpMemberDecorate %17 2 RelaxedPrecision
+ OpMemberDecorate %17 4 RelaxedPrecision
+ OpDecorate %17 Block
+-OpDecorate %19 DescriptorSet 0
++OpDecorate %19 DescriptorSet 2
+-OpDecorate %19 Binding 1
++OpDecorate %19 Binding 0
+-OpDecorate %20 Location 1
+ OpMemberDecorate %23 0 BuiltIn Position
+-OpMemberDecorate %23 1 BuiltIn PointSize
+-OpMemberDecorate %23 2 BuiltIn ClipDistance
+-OpMemberDecorate %23 3 BuiltIn CullDistance
+ OpDecorate %23 Block
+ OpDecorate %28 RelaxedPrecision
+ OpDecorate %29 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %32 RelaxedPrecision
+ OpDecorate %33 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %44 RelaxedPrecision
+ OpDecorate %52 RelaxedPrecision
+ OpDecorate %55 RelaxedPrecision
+ OpDecorate %56 RelaxedPrecision
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %9 = OpTypeStruct %2
+ %12 = OpTypeInt 32 0
+ %13 = OpTypeInt 32 1
+ %14 = OpTypeVector %13 4
+ %15 = OpTypeVector %12 4
+ %16 = OpTypeStruct %1 %1 %1 %1
+ %17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16
+ %21 = OpConstant %12 8
+ %22 = OpTypeArray %1 %21
+-%23 = OpTypeStruct %2 %1 %22 %22
++%23 = OpTypeStruct %2
+ %38 = OpTypeVoid
+ %45 = OpConstant %12 0
++%59 = OpTypePointer Private %2
+ %3 = OpTypePointer Input %2
++%60 = OpTypePointer Private %2
+ %7 = OpTypePointer Output %2
+ %10 = OpTypePointer Uniform %9
+ %18 = OpTypePointer Uniform %17
+ %24 = OpTypePointer Output %23
+ %26 = OpTypePointer Function %2
+ %46 = OpTypePointer Uniform %2
+ %27 = OpTypeFunction %2 %26
+ %39 = OpTypeFunction %38
+ %4 = OpVariable %3 Input
+ %5 = OpVariable %3 Input
+ %6 = OpVariable %3 Input
+ %8 = OpVariable %7 Output
+ %11 = OpVariable %10 Uniform
+ %19 = OpVariable %18 Uniform
+-%20 = OpVariable %7 Output
++%58 = OpVariable %60 Private
+ %25 = OpVariable %24 Output
++%61 = OpConstant %13 0
++%62 = OpConstant %1 0.5
+ %28 = OpFunction %2 None %27
+ %29 = OpFunctionParameter %26
+ %30 = OpLabel
+ %31 = OpLoad %2 %29
+ OpReturnValue %31
+ OpFunctionEnd
+ %32 = OpFunction %2 None %27
+ %33 = OpFunctionParameter %26
+ %34 = OpLabel
+ %35 = OpLoad %2 %33
+ %36 = OpLoad %2 %33
+ %37 = OpFAdd %2 %35 %36
+ OpReturnValue %37
+ OpFunctionEnd
+ %40 = OpFunction %38 None %39
+ %41 = OpLabel
+ %42 = OpVariable %26 Function
+ %50 = OpVariable %26 Function
+ %53 = OpVariable %26 Function
+ %43 = OpLoad %2 %4
+ OpStore %42 %43
+ %44 = OpFunctionCall %2 %28 %42
+ %47 = OpAccessChain %46 %11 %45
+ %48 = OpLoad %2 %47
+ %49 = OpFAdd %2 %44 %48
+ OpStore %8 %49
+ %51 = OpLoad %2 %5
+ OpStore %50 %51
+ %52 = OpFunctionCall %2 %32 %50
+ %54 = OpLoad %2 %6
+ OpStore %53 %54
+ %55 = OpFunctionCall %2 %28 %53
+ %56 = OpFAdd %2 %52 %55
+ %57 = OpAccessChain %7 %25 %45
+ OpStore %57 %56
++%63 = OpAccessChain %7 %25 %61
++%64 = OpLoad %2 %63
++%65 = OpCompositeExtract %1 %64 0
++%66 = OpCompositeExtract %1 %64 1
++%67 = OpCompositeExtract %1 %64 2
++%68 = OpCompositeExtract %1 %64 3
++%70 = OpFNegate %1 %65
++%71 = OpFAdd %1 %67 %68
++%72 = OpFMul %1 %71 %62
++%69 = OpCompositeConstruct %2 %66 %70 %72 %68
++OpStore %63 %69
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, DifferentDecorationsVertexNoDebug) {
+ constexpr char kSrcNoDebug[] = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25
+OpSource GLSL 450
+OpDecorate %4 Location 0
+OpDecorate %5 Location 1
+OpDecorate %6 Location 2
+OpDecorate %8 Location 0
+OpMemberDecorate %9 0 Offset 0
+OpDecorate %9 Block
+OpDecorate %11 DescriptorSet 0
+OpDecorate %11 Binding 0
+OpMemberDecorate %16 0 Offset 0
+OpMemberDecorate %16 1 Offset 4
+OpMemberDecorate %16 2 Offset 8
+OpMemberDecorate %16 3 Offset 12
+OpMemberDecorate %17 0 Offset 0
+OpMemberDecorate %17 1 Offset 16
+OpMemberDecorate %17 2 Offset 20
+OpMemberDecorate %17 3 Offset 24
+OpMemberDecorate %17 4 Offset 28
+OpMemberDecorate %17 5 Offset 32
+OpMemberDecorate %17 6 Offset 48
+OpMemberDecorate %17 7 Offset 64
+OpMemberDecorate %17 2 RelaxedPrecision
+OpMemberDecorate %17 4 RelaxedPrecision
+OpDecorate %17 Block
+OpDecorate %19 DescriptorSet 0
+OpDecorate %19 Binding 1
+OpDecorate %20 Location 1
+OpMemberDecorate %23 0 BuiltIn Position
+OpMemberDecorate %23 1 BuiltIn PointSize
+OpMemberDecorate %23 2 BuiltIn ClipDistance
+OpMemberDecorate %23 3 BuiltIn CullDistance
+OpDecorate %23 Block
+OpDecorate %28 RelaxedPrecision
+OpDecorate %29 RelaxedPrecision
+OpDecorate %31 RelaxedPrecision
+OpDecorate %32 RelaxedPrecision
+OpDecorate %33 RelaxedPrecision
+OpDecorate %35 RelaxedPrecision
+OpDecorate %36 RelaxedPrecision
+OpDecorate %37 RelaxedPrecision
+OpDecorate %44 RelaxedPrecision
+OpDecorate %52 RelaxedPrecision
+OpDecorate %55 RelaxedPrecision
+OpDecorate %56 RelaxedPrecision
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%9 = OpTypeStruct %2
+%12 = OpTypeInt 32 0
+%13 = OpTypeInt 32 1
+%14 = OpTypeVector %13 4
+%15 = OpTypeVector %12 4
+%16 = OpTypeStruct %1 %1 %1 %1
+%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16
+%21 = OpConstant %12 8
+%22 = OpTypeArray %1 %21
+%23 = OpTypeStruct %2 %1 %22 %22
+%38 = OpTypeVoid
+%45 = OpConstant %12 0
+%3 = OpTypePointer Input %2
+%7 = OpTypePointer Output %2
+%10 = OpTypePointer Uniform %9
+%18 = OpTypePointer Uniform %17
+%24 = OpTypePointer Output %23
+%26 = OpTypePointer Function %2
+%46 = OpTypePointer Uniform %2
+%27 = OpTypeFunction %2 %26
+%39 = OpTypeFunction %38
+%4 = OpVariable %3 Input
+%5 = OpVariable %3 Input
+%6 = OpVariable %3 Input
+%8 = OpVariable %7 Output
+%11 = OpVariable %10 Uniform
+%19 = OpVariable %18 Uniform
+%20 = OpVariable %7 Output
+%25 = OpVariable %24 Output
+%28 = OpFunction %2 None %27
+%29 = OpFunctionParameter %26
+%30 = OpLabel
+%31 = OpLoad %2 %29
+OpReturnValue %31
+OpFunctionEnd
+%32 = OpFunction %2 None %27
+%33 = OpFunctionParameter %26
+%34 = OpLabel
+%35 = OpLoad %2 %33
+%36 = OpLoad %2 %33
+%37 = OpFAdd %2 %35 %36
+OpReturnValue %37
+OpFunctionEnd
+%40 = OpFunction %38 None %39
+%41 = OpLabel
+%42 = OpVariable %26 Function
+%50 = OpVariable %26 Function
+%53 = OpVariable %26 Function
+%43 = OpLoad %2 %4
+OpStore %42 %43
+%44 = OpFunctionCall %2 %28 %42
+%47 = OpAccessChain %46 %11 %45
+%48 = OpLoad %2 %47
+%49 = OpFAdd %2 %44 %48
+OpStore %8 %49
+%51 = OpLoad %2 %5
+OpStore %50 %51
+%52 = OpFunctionCall %2 %32 %50
+%54 = OpLoad %2 %6
+OpStore %53 %54
+%55 = OpFunctionCall %2 %28 %53
+%56 = OpFAdd %2 %52 %55
+%57 = OpAccessChain %7 %25 %45
+OpStore %57 %56
+OpReturn
+OpFunctionEnd
+
+)";
+ constexpr char kDstNoDebug[] = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25
+OpSource GLSL 450
+OpDecorate %4 Location 1
+OpDecorate %5 Location 2
+OpDecorate %6 Location 0
+OpDecorate %8 Location 1
+OpMemberDecorate %9 0 Offset 0
+OpDecorate %9 Block
+OpDecorate %11 DescriptorSet 0
+OpDecorate %11 Binding 1
+OpMemberDecorate %16 0 Offset 0
+OpMemberDecorate %16 1 Offset 4
+OpMemberDecorate %16 2 Offset 8
+OpMemberDecorate %16 3 Offset 12
+OpMemberDecorate %17 0 Offset 0
+OpMemberDecorate %17 1 Offset 16
+OpMemberDecorate %17 2 Offset 20
+OpMemberDecorate %17 3 Offset 24
+OpMemberDecorate %17 4 Offset 28
+OpMemberDecorate %17 5 Offset 32
+OpMemberDecorate %17 6 Offset 48
+OpMemberDecorate %17 7 Offset 64
+OpMemberDecorate %17 2 RelaxedPrecision
+OpMemberDecorate %17 4 RelaxedPrecision
+OpDecorate %17 Block
+OpDecorate %19 DescriptorSet 2
+OpDecorate %19 Binding 0
+OpMemberDecorate %23 0 BuiltIn Position
+OpDecorate %23 Block
+OpDecorate %28 RelaxedPrecision
+OpDecorate %29 RelaxedPrecision
+OpDecorate %31 RelaxedPrecision
+OpDecorate %32 RelaxedPrecision
+OpDecorate %33 RelaxedPrecision
+OpDecorate %35 RelaxedPrecision
+OpDecorate %36 RelaxedPrecision
+OpDecorate %37 RelaxedPrecision
+OpDecorate %44 RelaxedPrecision
+OpDecorate %52 RelaxedPrecision
+OpDecorate %55 RelaxedPrecision
+OpDecorate %56 RelaxedPrecision
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%9 = OpTypeStruct %2
+%12 = OpTypeInt 32 0
+%13 = OpTypeInt 32 1
+%14 = OpTypeVector %13 4
+%15 = OpTypeVector %12 4
+%16 = OpTypeStruct %1 %1 %1 %1
+%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16
+%21 = OpConstant %12 8
+%22 = OpTypeArray %1 %21
+%23 = OpTypeStruct %2
+%38 = OpTypeVoid
+%45 = OpConstant %12 0
+%58 = OpTypePointer Private %2
+%3 = OpTypePointer Input %2
+%59 = OpTypePointer Private %2
+%7 = OpTypePointer Output %2
+%10 = OpTypePointer Uniform %9
+%18 = OpTypePointer Uniform %17
+%24 = OpTypePointer Output %23
+%26 = OpTypePointer Function %2
+%46 = OpTypePointer Uniform %2
+%27 = OpTypeFunction %2 %26
+%39 = OpTypeFunction %38
+%4 = OpVariable %3 Input
+%5 = OpVariable %3 Input
+%6 = OpVariable %3 Input
+%8 = OpVariable %7 Output
+%11 = OpVariable %10 Uniform
+%19 = OpVariable %18 Uniform
+%20 = OpVariable %59 Private
+%25 = OpVariable %24 Output
+%60 = OpConstant %13 0
+%61 = OpConstant %1 0.5
+%28 = OpFunction %2 None %27
+%29 = OpFunctionParameter %26
+%30 = OpLabel
+%31 = OpLoad %2 %29
+OpReturnValue %31
+OpFunctionEnd
+%32 = OpFunction %2 None %27
+%33 = OpFunctionParameter %26
+%34 = OpLabel
+%35 = OpLoad %2 %33
+%36 = OpLoad %2 %33
+%37 = OpFAdd %2 %35 %36
+OpReturnValue %37
+OpFunctionEnd
+%40 = OpFunction %38 None %39
+%41 = OpLabel
+%42 = OpVariable %26 Function
+%50 = OpVariable %26 Function
+%53 = OpVariable %26 Function
+%43 = OpLoad %2 %4
+OpStore %42 %43
+%44 = OpFunctionCall %2 %28 %42
+%47 = OpAccessChain %46 %11 %45
+%48 = OpLoad %2 %47
+%49 = OpFAdd %2 %44 %48
+OpStore %8 %49
+%51 = OpLoad %2 %5
+OpStore %50 %51
+%52 = OpFunctionCall %2 %32 %50
+%54 = OpLoad %2 %6
+OpStore %53 %54
+%55 = OpFunctionCall %2 %28 %53
+%56 = OpFAdd %2 %52 %55
+%57 = OpAccessChain %7 %25 %45
+OpStore %57 %56
+%62 = OpAccessChain %7 %25 %60
+%63 = OpLoad %2 %62
+%64 = OpCompositeExtract %1 %63 0
+%65 = OpCompositeExtract %1 %63 1
+%66 = OpCompositeExtract %1 %63 2
+%67 = OpCompositeExtract %1 %63 3
+%69 = OpFNegate %1 %64
+%70 = OpFAdd %1 %66 %67
+%71 = OpFMul %1 %70 %61
+%68 = OpCompositeConstruct %2 %65 %69 %71 %67
+OpStore %62 %68
+OpReturn
+OpFunctionEnd
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 58
++; Bound: 79
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+-OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25
++OpEntryPoint Vertex %40 "main" %5 %6 %4 %20 %25
+ OpSource GLSL 450
+ OpDecorate %4 Location 0
+ OpDecorate %5 Location 1
+ OpDecorate %6 Location 2
+-OpDecorate %8 Location 0
+ OpMemberDecorate %9 0 Offset 0
+ OpDecorate %9 Block
+-OpDecorate %11 DescriptorSet 0
++OpDecorate %11 DescriptorSet 2
+ OpDecorate %11 Binding 0
+ OpMemberDecorate %16 0 Offset 0
+ OpMemberDecorate %16 1 Offset 4
+ OpMemberDecorate %16 2 Offset 8
+ OpMemberDecorate %16 3 Offset 12
+ OpMemberDecorate %17 0 Offset 0
+ OpMemberDecorate %17 1 Offset 16
+ OpMemberDecorate %17 2 Offset 20
+ OpMemberDecorate %17 3 Offset 24
+ OpMemberDecorate %17 4 Offset 28
+ OpMemberDecorate %17 5 Offset 32
+ OpMemberDecorate %17 6 Offset 48
+ OpMemberDecorate %17 7 Offset 64
+ OpMemberDecorate %17 2 RelaxedPrecision
+ OpMemberDecorate %17 4 RelaxedPrecision
+ OpDecorate %17 Block
+ OpDecorate %19 DescriptorSet 0
+ OpDecorate %19 Binding 1
+ OpDecorate %20 Location 1
+ OpMemberDecorate %23 0 BuiltIn Position
+-OpMemberDecorate %23 1 BuiltIn PointSize
+-OpMemberDecorate %23 2 BuiltIn ClipDistance
+-OpMemberDecorate %23 3 BuiltIn CullDistance
+ OpDecorate %23 Block
+ OpDecorate %28 RelaxedPrecision
+-OpDecorate %29 RelaxedPrecision
++OpDecorate %59 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %32 RelaxedPrecision
+-OpDecorate %33 RelaxedPrecision
++OpDecorate %60 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %44 RelaxedPrecision
+ OpDecorate %52 RelaxedPrecision
+ OpDecorate %55 RelaxedPrecision
+ OpDecorate %56 RelaxedPrecision
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %9 = OpTypeStruct %2
+ %12 = OpTypeInt 32 0
+ %13 = OpTypeInt 32 1
+ %14 = OpTypeVector %13 4
+ %15 = OpTypeVector %12 4
+ %16 = OpTypeStruct %1 %1 %1 %1
+ %17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16
+ %21 = OpConstant %12 8
+ %22 = OpTypeArray %1 %21
+-%23 = OpTypeStruct %2 %1 %22 %22
++%23 = OpTypeStruct %2
+ %38 = OpTypeVoid
+ %45 = OpConstant %12 0
++%65 = OpTypePointer Private %2
+ %3 = OpTypePointer Input %2
++%66 = OpTypePointer Private %2
+ %7 = OpTypePointer Output %2
+ %10 = OpTypePointer Uniform %9
+ %18 = OpTypePointer Uniform %17
+ %24 = OpTypePointer Output %23
+ %26 = OpTypePointer Function %2
+ %46 = OpTypePointer Uniform %2
+ %27 = OpTypeFunction %2 %26
+ %39 = OpTypeFunction %38
+ %4 = OpVariable %3 Input
+ %5 = OpVariable %3 Input
+ %6 = OpVariable %3 Input
+-%8 = OpVariable %7 Output
+-%11 = OpVariable %10 Uniform
++%11 = OpVariable %18 Uniform
+-%19 = OpVariable %18 Uniform
++%19 = OpVariable %10 Uniform
+ %20 = OpVariable %7 Output
++%58 = OpVariable %66 Private
+ %25 = OpVariable %24 Output
++%67 = OpConstant %13 0
++%68 = OpConstant %1 0.5
+ %28 = OpFunction %2 None %27
+-%29 = OpFunctionParameter %26
++%59 = OpFunctionParameter %26
+ %30 = OpLabel
+-%31 = OpLoad %2 %29
++%31 = OpLoad %2 %59
+ OpReturnValue %31
+ OpFunctionEnd
+ %32 = OpFunction %2 None %27
+-%33 = OpFunctionParameter %26
++%60 = OpFunctionParameter %26
+ %34 = OpLabel
+-%35 = OpLoad %2 %33
++%35 = OpLoad %2 %60
+-%36 = OpLoad %2 %33
++%36 = OpLoad %2 %60
+ %37 = OpFAdd %2 %35 %36
+ OpReturnValue %37
+ OpFunctionEnd
+ %40 = OpFunction %38 None %39
+ %41 = OpLabel
+ %42 = OpVariable %26 Function
+ %50 = OpVariable %26 Function
+ %53 = OpVariable %26 Function
+-%43 = OpLoad %2 %4
++%61 = OpLoad %2 %5
+-OpStore %42 %43
++OpStore %42 %61
+ %44 = OpFunctionCall %2 %28 %42
+-%47 = OpAccessChain %46 %11 %45
++%62 = OpAccessChain %46 %19 %45
+-%48 = OpLoad %2 %47
++%48 = OpLoad %2 %62
+ %49 = OpFAdd %2 %44 %48
+-OpStore %8 %49
++OpStore %20 %49
+-%51 = OpLoad %2 %5
++%63 = OpLoad %2 %6
+-OpStore %50 %51
++OpStore %50 %63
+ %52 = OpFunctionCall %2 %32 %50
+-%54 = OpLoad %2 %6
++%64 = OpLoad %2 %4
+-OpStore %53 %54
++OpStore %53 %64
+ %55 = OpFunctionCall %2 %28 %53
+ %56 = OpFAdd %2 %52 %55
+ %57 = OpAccessChain %7 %25 %45
+ OpStore %57 %56
++%69 = OpAccessChain %7 %25 %67
++%70 = OpLoad %2 %69
++%71 = OpCompositeExtract %1 %70 0
++%72 = OpCompositeExtract %1 %70 1
++%73 = OpCompositeExtract %1 %70 2
++%74 = OpCompositeExtract %1 %70 3
++%76 = OpFNegate %1 %71
++%77 = OpFAdd %1 %73 %74
++%78 = OpFMul %1 %77 %68
++%75 = OpCompositeConstruct %2 %72 %76 %78 %74
++OpStore %69 %75
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+TEST(DiffTest, DifferentDecorationsVertexIgnoreSetBinding) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 58
++; Bound: 73
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+-OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25
++OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25
+ OpSource GLSL 450
+ OpName %4 "_ub"
+ OpName %5 "_uc"
+ OpName %6 "_ud"
+ OpName %8 "_ue"
+ OpName %9 "defaultUniformsVS"
+ OpMemberName %9 0 "_ua"
+ OpName %11 ""
+ OpName %16 "ANGLEDepthRangeParams"
+ OpMemberName %16 0 "near"
+ OpMemberName %16 1 "far"
+ OpMemberName %16 2 "diff"
+ OpMemberName %16 3 "reserved"
+ OpName %17 "ANGLEUniformBlock"
+ OpMemberName %17 0 "viewport"
+ OpMemberName %17 1 "clipDistancesEnabled"
+ OpMemberName %17 2 "xfbActiveUnpaused"
+ OpMemberName %17 3 "xfbVerticesPerInstance"
+ OpMemberName %17 4 "numSamples"
+ OpMemberName %17 5 "xfbBufferOffsets"
+ OpMemberName %17 6 "acbBufferOffsets"
+ OpMemberName %17 7 "depthRange"
+ OpName %19 "ANGLEUniforms"
+-OpName %20 "ANGLEXfbPosition"
+ OpName %23 "gl_PerVertex"
+ OpMemberName %23 0 "gl_Position"
+-OpMemberName %23 1 "gl_PointSize"
+-OpMemberName %23 2 "gl_ClipDistance"
+-OpMemberName %23 3 "gl_CullDistance"
+ OpName %25 ""
+ OpName %29 "_ua"
+ OpName %28 "_uf"
+ OpName %33 "_uf"
+ OpName %32 "_ug"
+ OpName %40 "main"
+ OpName %42 "param"
+ OpName %50 "param"
+ OpName %53 "param"
+-OpDecorate %4 Location 0
++OpDecorate %4 Location 1
+-OpDecorate %5 Location 1
++OpDecorate %5 Location 2
+-OpDecorate %6 Location 2
++OpDecorate %6 Location 0
+-OpDecorate %8 Location 0
++OpDecorate %8 Location 1
+ OpMemberDecorate %9 0 Offset 0
+ OpDecorate %9 Block
+ OpDecorate %11 DescriptorSet 0
+-OpDecorate %11 Binding 0
++OpDecorate %11 Binding 1
+ OpMemberDecorate %16 0 Offset 0
+ OpMemberDecorate %16 1 Offset 4
+ OpMemberDecorate %16 2 Offset 8
+ OpMemberDecorate %16 3 Offset 12
+ OpMemberDecorate %17 0 Offset 0
+ OpMemberDecorate %17 1 Offset 16
+ OpMemberDecorate %17 2 Offset 20
+ OpMemberDecorate %17 3 Offset 24
+ OpMemberDecorate %17 4 Offset 28
+ OpMemberDecorate %17 5 Offset 32
+ OpMemberDecorate %17 6 Offset 48
+ OpMemberDecorate %17 7 Offset 64
+ OpMemberDecorate %17 2 RelaxedPrecision
+ OpMemberDecorate %17 4 RelaxedPrecision
+ OpDecorate %17 Block
+-OpDecorate %19 DescriptorSet 0
++OpDecorate %19 DescriptorSet 2
+-OpDecorate %19 Binding 1
++OpDecorate %19 Binding 0
+-OpDecorate %20 Location 1
+ OpMemberDecorate %23 0 BuiltIn Position
+-OpMemberDecorate %23 1 BuiltIn PointSize
+-OpMemberDecorate %23 2 BuiltIn ClipDistance
+-OpMemberDecorate %23 3 BuiltIn CullDistance
+ OpDecorate %23 Block
+ OpDecorate %28 RelaxedPrecision
+ OpDecorate %29 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %32 RelaxedPrecision
+ OpDecorate %33 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %44 RelaxedPrecision
+ OpDecorate %52 RelaxedPrecision
+ OpDecorate %55 RelaxedPrecision
+ OpDecorate %56 RelaxedPrecision
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %9 = OpTypeStruct %2
+ %12 = OpTypeInt 32 0
+ %13 = OpTypeInt 32 1
+ %14 = OpTypeVector %13 4
+ %15 = OpTypeVector %12 4
+ %16 = OpTypeStruct %1 %1 %1 %1
+ %17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16
+ %21 = OpConstant %12 8
+ %22 = OpTypeArray %1 %21
+-%23 = OpTypeStruct %2 %1 %22 %22
++%23 = OpTypeStruct %2
+ %38 = OpTypeVoid
+ %45 = OpConstant %12 0
++%59 = OpTypePointer Private %2
+ %3 = OpTypePointer Input %2
++%60 = OpTypePointer Private %2
+ %7 = OpTypePointer Output %2
+ %10 = OpTypePointer Uniform %9
+ %18 = OpTypePointer Uniform %17
+ %24 = OpTypePointer Output %23
+ %26 = OpTypePointer Function %2
+ %46 = OpTypePointer Uniform %2
+ %27 = OpTypeFunction %2 %26
+ %39 = OpTypeFunction %38
+ %4 = OpVariable %3 Input
+ %5 = OpVariable %3 Input
+ %6 = OpVariable %3 Input
+ %8 = OpVariable %7 Output
+ %11 = OpVariable %10 Uniform
+ %19 = OpVariable %18 Uniform
+-%20 = OpVariable %7 Output
++%58 = OpVariable %60 Private
+ %25 = OpVariable %24 Output
++%61 = OpConstant %13 0
++%62 = OpConstant %1 0.5
+ %28 = OpFunction %2 None %27
+ %29 = OpFunctionParameter %26
+ %30 = OpLabel
+ %31 = OpLoad %2 %29
+ OpReturnValue %31
+ OpFunctionEnd
+ %32 = OpFunction %2 None %27
+ %33 = OpFunctionParameter %26
+ %34 = OpLabel
+ %35 = OpLoad %2 %33
+ %36 = OpLoad %2 %33
+ %37 = OpFAdd %2 %35 %36
+ OpReturnValue %37
+ OpFunctionEnd
+ %40 = OpFunction %38 None %39
+ %41 = OpLabel
+ %42 = OpVariable %26 Function
+ %50 = OpVariable %26 Function
+ %53 = OpVariable %26 Function
+ %43 = OpLoad %2 %4
+ OpStore %42 %43
+ %44 = OpFunctionCall %2 %28 %42
+ %47 = OpAccessChain %46 %11 %45
+ %48 = OpLoad %2 %47
+ %49 = OpFAdd %2 %44 %48
+ OpStore %8 %49
+ %51 = OpLoad %2 %5
+ OpStore %50 %51
+ %52 = OpFunctionCall %2 %32 %50
+ %54 = OpLoad %2 %6
+ OpStore %53 %54
+ %55 = OpFunctionCall %2 %28 %53
+ %56 = OpFAdd %2 %52 %55
+ %57 = OpAccessChain %7 %25 %45
+ OpStore %57 %56
++%63 = OpAccessChain %7 %25 %61
++%64 = OpLoad %2 %63
++%65 = OpCompositeExtract %1 %64 0
++%66 = OpCompositeExtract %1 %64 1
++%67 = OpCompositeExtract %1 %64 2
++%68 = OpCompositeExtract %1 %64 3
++%70 = OpFNegate %1 %65
++%71 = OpFAdd %1 %67 %68
++%72 = OpFMul %1 %71 %62
++%69 = OpCompositeConstruct %2 %66 %70 %72 %68
++OpStore %63 %69
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ options.ignore_set_binding = true;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, DifferentDecorationsVertexIgnoreSetBindingLocation) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 58
++; Bound: 73
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+-OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25
++OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25
+ OpSource GLSL 450
+ OpName %4 "_ub"
+ OpName %5 "_uc"
+ OpName %6 "_ud"
+ OpName %8 "_ue"
+ OpName %9 "defaultUniformsVS"
+ OpMemberName %9 0 "_ua"
+ OpName %11 ""
+ OpName %16 "ANGLEDepthRangeParams"
+ OpMemberName %16 0 "near"
+ OpMemberName %16 1 "far"
+ OpMemberName %16 2 "diff"
+ OpMemberName %16 3 "reserved"
+ OpName %17 "ANGLEUniformBlock"
+ OpMemberName %17 0 "viewport"
+ OpMemberName %17 1 "clipDistancesEnabled"
+ OpMemberName %17 2 "xfbActiveUnpaused"
+ OpMemberName %17 3 "xfbVerticesPerInstance"
+ OpMemberName %17 4 "numSamples"
+ OpMemberName %17 5 "xfbBufferOffsets"
+ OpMemberName %17 6 "acbBufferOffsets"
+ OpMemberName %17 7 "depthRange"
+ OpName %19 "ANGLEUniforms"
+-OpName %20 "ANGLEXfbPosition"
+ OpName %23 "gl_PerVertex"
+ OpMemberName %23 0 "gl_Position"
+-OpMemberName %23 1 "gl_PointSize"
+-OpMemberName %23 2 "gl_ClipDistance"
+-OpMemberName %23 3 "gl_CullDistance"
+ OpName %25 ""
+ OpName %29 "_ua"
+ OpName %28 "_uf"
+ OpName %33 "_uf"
+ OpName %32 "_ug"
+ OpName %40 "main"
+ OpName %42 "param"
+ OpName %50 "param"
+ OpName %53 "param"
+-OpDecorate %4 Location 0
++OpDecorate %4 Location 1
+-OpDecorate %5 Location 1
++OpDecorate %5 Location 2
+-OpDecorate %6 Location 2
++OpDecorate %6 Location 0
+-OpDecorate %8 Location 0
++OpDecorate %8 Location 1
+ OpMemberDecorate %9 0 Offset 0
+ OpDecorate %9 Block
+ OpDecorate %11 DescriptorSet 0
+-OpDecorate %11 Binding 0
++OpDecorate %11 Binding 1
+ OpMemberDecorate %16 0 Offset 0
+ OpMemberDecorate %16 1 Offset 4
+ OpMemberDecorate %16 2 Offset 8
+ OpMemberDecorate %16 3 Offset 12
+ OpMemberDecorate %17 0 Offset 0
+ OpMemberDecorate %17 1 Offset 16
+ OpMemberDecorate %17 2 Offset 20
+ OpMemberDecorate %17 3 Offset 24
+ OpMemberDecorate %17 4 Offset 28
+ OpMemberDecorate %17 5 Offset 32
+ OpMemberDecorate %17 6 Offset 48
+ OpMemberDecorate %17 7 Offset 64
+ OpMemberDecorate %17 2 RelaxedPrecision
+ OpMemberDecorate %17 4 RelaxedPrecision
+ OpDecorate %17 Block
+-OpDecorate %19 DescriptorSet 0
++OpDecorate %19 DescriptorSet 2
+-OpDecorate %19 Binding 1
++OpDecorate %19 Binding 0
+-OpDecorate %20 Location 1
+ OpMemberDecorate %23 0 BuiltIn Position
+-OpMemberDecorate %23 1 BuiltIn PointSize
+-OpMemberDecorate %23 2 BuiltIn ClipDistance
+-OpMemberDecorate %23 3 BuiltIn CullDistance
+ OpDecorate %23 Block
+ OpDecorate %28 RelaxedPrecision
+ OpDecorate %29 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %32 RelaxedPrecision
+ OpDecorate %33 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %44 RelaxedPrecision
+ OpDecorate %52 RelaxedPrecision
+ OpDecorate %55 RelaxedPrecision
+ OpDecorate %56 RelaxedPrecision
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %9 = OpTypeStruct %2
+ %12 = OpTypeInt 32 0
+ %13 = OpTypeInt 32 1
+ %14 = OpTypeVector %13 4
+ %15 = OpTypeVector %12 4
+ %16 = OpTypeStruct %1 %1 %1 %1
+ %17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16
+ %21 = OpConstant %12 8
+ %22 = OpTypeArray %1 %21
+-%23 = OpTypeStruct %2 %1 %22 %22
++%23 = OpTypeStruct %2
+ %38 = OpTypeVoid
+ %45 = OpConstant %12 0
++%59 = OpTypePointer Private %2
+ %3 = OpTypePointer Input %2
++%60 = OpTypePointer Private %2
+ %7 = OpTypePointer Output %2
+ %10 = OpTypePointer Uniform %9
+ %18 = OpTypePointer Uniform %17
+ %24 = OpTypePointer Output %23
+ %26 = OpTypePointer Function %2
+ %46 = OpTypePointer Uniform %2
+ %27 = OpTypeFunction %2 %26
+ %39 = OpTypeFunction %38
+ %4 = OpVariable %3 Input
+ %5 = OpVariable %3 Input
+ %6 = OpVariable %3 Input
+ %8 = OpVariable %7 Output
+ %11 = OpVariable %10 Uniform
+ %19 = OpVariable %18 Uniform
+-%20 = OpVariable %7 Output
++%58 = OpVariable %60 Private
+ %25 = OpVariable %24 Output
++%61 = OpConstant %13 0
++%62 = OpConstant %1 0.5
+ %28 = OpFunction %2 None %27
+ %29 = OpFunctionParameter %26
+ %30 = OpLabel
+ %31 = OpLoad %2 %29
+ OpReturnValue %31
+ OpFunctionEnd
+ %32 = OpFunction %2 None %27
+ %33 = OpFunctionParameter %26
+ %34 = OpLabel
+ %35 = OpLoad %2 %33
+ %36 = OpLoad %2 %33
+ %37 = OpFAdd %2 %35 %36
+ OpReturnValue %37
+ OpFunctionEnd
+ %40 = OpFunction %38 None %39
+ %41 = OpLabel
+ %42 = OpVariable %26 Function
+ %50 = OpVariable %26 Function
+ %53 = OpVariable %26 Function
+ %43 = OpLoad %2 %4
+ OpStore %42 %43
+ %44 = OpFunctionCall %2 %28 %42
+ %47 = OpAccessChain %46 %11 %45
+ %48 = OpLoad %2 %47
+ %49 = OpFAdd %2 %44 %48
+ OpStore %8 %49
+ %51 = OpLoad %2 %5
+ OpStore %50 %51
+ %52 = OpFunctionCall %2 %32 %50
+ %54 = OpLoad %2 %6
+ OpStore %53 %54
+ %55 = OpFunctionCall %2 %28 %53
+ %56 = OpFAdd %2 %52 %55
+ %57 = OpAccessChain %7 %25 %45
+ OpStore %57 %56
++%63 = OpAccessChain %7 %25 %61
++%64 = OpLoad %2 %63
++%65 = OpCompositeExtract %1 %64 0
++%66 = OpCompositeExtract %1 %64 1
++%67 = OpCompositeExtract %1 %64 2
++%68 = OpCompositeExtract %1 %64 3
++%70 = OpFNegate %1 %65
++%71 = OpFAdd %1 %67 %68
++%72 = OpFMul %1 %71 %62
++%69 = OpCompositeConstruct %2 %66 %70 %72 %68
++OpStore %63 %69
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ options.ignore_set_binding = true;
+ options.ignore_location = true;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/different_decorations_vertex_dst.spvasm b/test/diff/diff_files/different_decorations_vertex_dst.spvasm
new file mode 100644
index 00000000..33c6a9c1
--- /dev/null
+++ b/test/diff/diff_files/different_decorations_vertex_dst.spvasm
@@ -0,0 +1,159 @@
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25
+OpSource GLSL 450
+OpName %4 "_ub"
+OpName %5 "_uc"
+OpName %6 "_ud"
+OpName %8 "_ue"
+OpName %9 "defaultUniformsVS"
+OpMemberName %9 0 "_ua"
+OpName %11 ""
+OpName %16 "ANGLEDepthRangeParams"
+OpMemberName %16 0 "near"
+OpMemberName %16 1 "far"
+OpMemberName %16 2 "diff"
+OpMemberName %16 3 "reserved"
+OpName %17 "ANGLEUniformBlock"
+OpMemberName %17 0 "viewport"
+OpMemberName %17 1 "clipDistancesEnabled"
+OpMemberName %17 2 "xfbActiveUnpaused"
+OpMemberName %17 3 "xfbVerticesPerInstance"
+OpMemberName %17 4 "numSamples"
+OpMemberName %17 5 "xfbBufferOffsets"
+OpMemberName %17 6 "acbBufferOffsets"
+OpMemberName %17 7 "depthRange"
+OpName %19 "ANGLEUniforms"
+OpName %23 "gl_PerVertex"
+OpMemberName %23 0 "gl_Position"
+OpName %25 ""
+OpName %29 "_ua"
+OpName %28 "_uf"
+OpName %33 "_uf"
+OpName %32 "_ug"
+OpName %40 "main"
+OpName %42 "param"
+OpName %50 "param"
+OpName %53 "param"
+OpDecorate %4 Location 1
+OpDecorate %5 Location 2
+OpDecorate %6 Location 0
+OpDecorate %8 Location 1
+OpMemberDecorate %9 0 Offset 0
+OpDecorate %9 Block
+OpDecorate %11 DescriptorSet 0
+OpDecorate %11 Binding 1
+OpMemberDecorate %16 0 Offset 0
+OpMemberDecorate %16 1 Offset 4
+OpMemberDecorate %16 2 Offset 8
+OpMemberDecorate %16 3 Offset 12
+OpMemberDecorate %17 0 Offset 0
+OpMemberDecorate %17 1 Offset 16
+OpMemberDecorate %17 2 Offset 20
+OpMemberDecorate %17 3 Offset 24
+OpMemberDecorate %17 4 Offset 28
+OpMemberDecorate %17 5 Offset 32
+OpMemberDecorate %17 6 Offset 48
+OpMemberDecorate %17 7 Offset 64
+OpMemberDecorate %17 2 RelaxedPrecision
+OpMemberDecorate %17 4 RelaxedPrecision
+OpDecorate %17 Block
+OpDecorate %19 DescriptorSet 2
+OpDecorate %19 Binding 0
+OpMemberDecorate %23 0 BuiltIn Position
+OpDecorate %23 Block
+OpDecorate %28 RelaxedPrecision
+OpDecorate %29 RelaxedPrecision
+OpDecorate %31 RelaxedPrecision
+OpDecorate %32 RelaxedPrecision
+OpDecorate %33 RelaxedPrecision
+OpDecorate %35 RelaxedPrecision
+OpDecorate %36 RelaxedPrecision
+OpDecorate %37 RelaxedPrecision
+OpDecorate %44 RelaxedPrecision
+OpDecorate %52 RelaxedPrecision
+OpDecorate %55 RelaxedPrecision
+OpDecorate %56 RelaxedPrecision
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%9 = OpTypeStruct %2
+%12 = OpTypeInt 32 0
+%13 = OpTypeInt 32 1
+%14 = OpTypeVector %13 4
+%15 = OpTypeVector %12 4
+%16 = OpTypeStruct %1 %1 %1 %1
+%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16
+%21 = OpConstant %12 8
+%22 = OpTypeArray %1 %21
+%23 = OpTypeStruct %2
+%38 = OpTypeVoid
+%45 = OpConstant %12 0
+%58 = OpTypePointer Private %2
+%3 = OpTypePointer Input %2
+%59 = OpTypePointer Private %2
+%7 = OpTypePointer Output %2
+%10 = OpTypePointer Uniform %9
+%18 = OpTypePointer Uniform %17
+%24 = OpTypePointer Output %23
+%26 = OpTypePointer Function %2
+%46 = OpTypePointer Uniform %2
+%27 = OpTypeFunction %2 %26
+%39 = OpTypeFunction %38
+%4 = OpVariable %3 Input
+%5 = OpVariable %3 Input
+%6 = OpVariable %3 Input
+%8 = OpVariable %7 Output
+%11 = OpVariable %10 Uniform
+%19 = OpVariable %18 Uniform
+%20 = OpVariable %59 Private
+%25 = OpVariable %24 Output
+%60 = OpConstant %13 0
+%61 = OpConstant %1 0.5
+%28 = OpFunction %2 None %27
+%29 = OpFunctionParameter %26
+%30 = OpLabel
+%31 = OpLoad %2 %29
+OpReturnValue %31
+OpFunctionEnd
+%32 = OpFunction %2 None %27
+%33 = OpFunctionParameter %26
+%34 = OpLabel
+%35 = OpLoad %2 %33
+%36 = OpLoad %2 %33
+%37 = OpFAdd %2 %35 %36
+OpReturnValue %37
+OpFunctionEnd
+%40 = OpFunction %38 None %39
+%41 = OpLabel
+%42 = OpVariable %26 Function
+%50 = OpVariable %26 Function
+%53 = OpVariable %26 Function
+%43 = OpLoad %2 %4
+OpStore %42 %43
+%44 = OpFunctionCall %2 %28 %42
+%47 = OpAccessChain %46 %11 %45
+%48 = OpLoad %2 %47
+%49 = OpFAdd %2 %44 %48
+OpStore %8 %49
+%51 = OpLoad %2 %5
+OpStore %50 %51
+%52 = OpFunctionCall %2 %32 %50
+%54 = OpLoad %2 %6
+OpStore %53 %54
+%55 = OpFunctionCall %2 %28 %53
+%56 = OpFAdd %2 %52 %55
+%57 = OpAccessChain %7 %25 %45
+OpStore %57 %56
+%62 = OpAccessChain %7 %25 %60
+%63 = OpLoad %2 %62
+%64 = OpCompositeExtract %1 %63 0
+%65 = OpCompositeExtract %1 %63 1
+%66 = OpCompositeExtract %1 %63 2
+%67 = OpCompositeExtract %1 %63 3
+%69 = OpFNegate %1 %64
+%70 = OpFAdd %1 %66 %67
+%71 = OpFMul %1 %70 %61
+%68 = OpCompositeConstruct %2 %65 %69 %71 %67
+OpStore %62 %68
+OpReturn
+OpFunctionEnd
diff --git a/test/diff/diff_files/different_decorations_vertex_src.spvasm b/test/diff/diff_files/different_decorations_vertex_src.spvasm
new file mode 100644
index 00000000..ce1680cb
--- /dev/null
+++ b/test/diff/diff_files/different_decorations_vertex_src.spvasm
@@ -0,0 +1,155 @@
+;; Test where variable set/binding/location decorations are different between
+;; src and dst vertex shaders.
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25
+OpSource GLSL 450
+OpName %4 "_ub"
+OpName %5 "_uc"
+OpName %6 "_ud"
+OpName %8 "_ue"
+OpName %9 "defaultUniformsVS"
+OpMemberName %9 0 "_ua"
+OpName %11 ""
+OpName %16 "ANGLEDepthRangeParams"
+OpMemberName %16 0 "near"
+OpMemberName %16 1 "far"
+OpMemberName %16 2 "diff"
+OpMemberName %16 3 "reserved"
+OpName %17 "ANGLEUniformBlock"
+OpMemberName %17 0 "viewport"
+OpMemberName %17 1 "clipDistancesEnabled"
+OpMemberName %17 2 "xfbActiveUnpaused"
+OpMemberName %17 3 "xfbVerticesPerInstance"
+OpMemberName %17 4 "numSamples"
+OpMemberName %17 5 "xfbBufferOffsets"
+OpMemberName %17 6 "acbBufferOffsets"
+OpMemberName %17 7 "depthRange"
+OpName %19 "ANGLEUniforms"
+OpName %20 "ANGLEXfbPosition"
+OpName %23 "gl_PerVertex"
+OpMemberName %23 0 "gl_Position"
+OpMemberName %23 1 "gl_PointSize"
+OpMemberName %23 2 "gl_ClipDistance"
+OpMemberName %23 3 "gl_CullDistance"
+OpName %25 ""
+OpName %29 "_ua"
+OpName %28 "_uf"
+OpName %33 "_uf"
+OpName %32 "_ug"
+OpName %40 "main"
+OpName %42 "param"
+OpName %50 "param"
+OpName %53 "param"
+OpDecorate %4 Location 0
+OpDecorate %5 Location 1
+OpDecorate %6 Location 2
+OpDecorate %8 Location 0
+OpMemberDecorate %9 0 Offset 0
+OpDecorate %9 Block
+OpDecorate %11 DescriptorSet 0
+OpDecorate %11 Binding 0
+OpMemberDecorate %16 0 Offset 0
+OpMemberDecorate %16 1 Offset 4
+OpMemberDecorate %16 2 Offset 8
+OpMemberDecorate %16 3 Offset 12
+OpMemberDecorate %17 0 Offset 0
+OpMemberDecorate %17 1 Offset 16
+OpMemberDecorate %17 2 Offset 20
+OpMemberDecorate %17 3 Offset 24
+OpMemberDecorate %17 4 Offset 28
+OpMemberDecorate %17 5 Offset 32
+OpMemberDecorate %17 6 Offset 48
+OpMemberDecorate %17 7 Offset 64
+OpMemberDecorate %17 2 RelaxedPrecision
+OpMemberDecorate %17 4 RelaxedPrecision
+OpDecorate %17 Block
+OpDecorate %19 DescriptorSet 0
+OpDecorate %19 Binding 1
+OpDecorate %20 Location 1
+OpMemberDecorate %23 0 BuiltIn Position
+OpMemberDecorate %23 1 BuiltIn PointSize
+OpMemberDecorate %23 2 BuiltIn ClipDistance
+OpMemberDecorate %23 3 BuiltIn CullDistance
+OpDecorate %23 Block
+OpDecorate %28 RelaxedPrecision
+OpDecorate %29 RelaxedPrecision
+OpDecorate %31 RelaxedPrecision
+OpDecorate %32 RelaxedPrecision
+OpDecorate %33 RelaxedPrecision
+OpDecorate %35 RelaxedPrecision
+OpDecorate %36 RelaxedPrecision
+OpDecorate %37 RelaxedPrecision
+OpDecorate %44 RelaxedPrecision
+OpDecorate %52 RelaxedPrecision
+OpDecorate %55 RelaxedPrecision
+OpDecorate %56 RelaxedPrecision
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%9 = OpTypeStruct %2
+%12 = OpTypeInt 32 0
+%13 = OpTypeInt 32 1
+%14 = OpTypeVector %13 4
+%15 = OpTypeVector %12 4
+%16 = OpTypeStruct %1 %1 %1 %1
+%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16
+%21 = OpConstant %12 8
+%22 = OpTypeArray %1 %21
+%23 = OpTypeStruct %2 %1 %22 %22
+%38 = OpTypeVoid
+%45 = OpConstant %12 0
+%3 = OpTypePointer Input %2
+%7 = OpTypePointer Output %2
+%10 = OpTypePointer Uniform %9
+%18 = OpTypePointer Uniform %17
+%24 = OpTypePointer Output %23
+%26 = OpTypePointer Function %2
+%46 = OpTypePointer Uniform %2
+%27 = OpTypeFunction %2 %26
+%39 = OpTypeFunction %38
+%4 = OpVariable %3 Input
+%5 = OpVariable %3 Input
+%6 = OpVariable %3 Input
+%8 = OpVariable %7 Output
+%11 = OpVariable %10 Uniform
+%19 = OpVariable %18 Uniform
+%20 = OpVariable %7 Output
+%25 = OpVariable %24 Output
+%28 = OpFunction %2 None %27
+%29 = OpFunctionParameter %26
+%30 = OpLabel
+%31 = OpLoad %2 %29
+OpReturnValue %31
+OpFunctionEnd
+%32 = OpFunction %2 None %27
+%33 = OpFunctionParameter %26
+%34 = OpLabel
+%35 = OpLoad %2 %33
+%36 = OpLoad %2 %33
+%37 = OpFAdd %2 %35 %36
+OpReturnValue %37
+OpFunctionEnd
+%40 = OpFunction %38 None %39
+%41 = OpLabel
+%42 = OpVariable %26 Function
+%50 = OpVariable %26 Function
+%53 = OpVariable %26 Function
+%43 = OpLoad %2 %4
+OpStore %42 %43
+%44 = OpFunctionCall %2 %28 %42
+%47 = OpAccessChain %46 %11 %45
+%48 = OpLoad %2 %47
+%49 = OpFAdd %2 %44 %48
+OpStore %8 %49
+%51 = OpLoad %2 %5
+OpStore %50 %51
+%52 = OpFunctionCall %2 %32 %50
+%54 = OpLoad %2 %6
+OpStore %53 %54
+%55 = OpFunctionCall %2 %28 %53
+%56 = OpFAdd %2 %52 %55
+%57 = OpAccessChain %7 %25 %45
+OpStore %57 %56
+OpReturn
+OpFunctionEnd
+
diff --git a/test/diff/diff_files/different_function_parameter_count_autogen.cpp b/test/diff/diff_files/different_function_parameter_count_autogen.cpp
new file mode 100644
index 00000000..3a077fb0
--- /dev/null
+++ b/test/diff/diff_files/different_function_parameter_count_autogen.cpp
@@ -0,0 +1,339 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Test where src and dst have a function with different parameter count.
+constexpr char kSrc[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 10
+; Bound: 25
+; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %20
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ OpName %4 "main"
+ OpName %11 "f(vf2;"
+ OpName %10 "v"
+ OpName %20 "o"
+ OpName %23 "param"
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %20 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 2
+ %8 = OpTypePointer Function %7
+ %9 = OpTypeFunction %7 %8
+ %14 = OpConstant %6 0.5
+ %15 = OpConstantComposite %7 %14 %14
+ %19 = OpTypePointer Output %7
+ %20 = OpVariable %19 Output
+ %21 = OpConstant %6 0
+ %22 = OpConstantComposite %7 %21 %21
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %23 = OpVariable %8 Function
+ OpStore %23 %22
+ %24 = OpFunctionCall %7 %11 %23
+ OpStore %20 %24
+ OpReturn
+ OpFunctionEnd
+ %11 = OpFunction %7 None %9
+ %10 = OpFunctionParameter %8
+ %12 = OpLabel
+ %13 = OpLoad %7 %10
+ %16 = OpFAdd %7 %13 %15
+ OpReturnValue %16
+ OpFunctionEnd)";
+constexpr char kDst[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 10
+; Bound: 28
+; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %20
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ OpName %4 "main"
+ OpName %12 "f(vf2;vf2;"
+ OpName %10 "v"
+ OpName %11 "v2"
+ OpName %20 "o"
+ OpName %25 "param"
+ OpName %26 "param"
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %20 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 2
+ %8 = OpTypePointer Function %7
+ %9 = OpTypeFunction %7 %8 %8
+ %19 = OpTypePointer Output %7
+ %20 = OpVariable %19 Output
+ %21 = OpConstant %6 0
+ %22 = OpConstantComposite %7 %21 %21
+ %23 = OpConstant %6 0.5
+ %24 = OpConstantComposite %7 %23 %23
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %25 = OpVariable %8 Function
+ %26 = OpVariable %8 Function
+ OpStore %25 %22
+ OpStore %26 %24
+ %27 = OpFunctionCall %7 %12 %25 %26
+ OpStore %20 %27
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %7 None %9
+ %10 = OpFunctionParameter %8
+ %11 = OpFunctionParameter %8
+ %13 = OpLabel
+ %14 = OpLoad %7 %10
+ %15 = OpLoad %7 %11
+ %16 = OpFAdd %7 %14 %15
+ OpReturnValue %16
+ OpFunctionEnd
+
+)";
+
+TEST(DiffTest, DifferentFunctionParameterCount) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 25
++; Bound: 33
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %20
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ OpName %4 "main"
+-OpName %11 "f(vf2;"
++OpName %11 "f(vf2;vf2;"
+ OpName %10 "v"
++OpName %26 "v2"
+ OpName %20 "o"
+ OpName %23 "param"
++OpName %31 "param"
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %20 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 2
+ %8 = OpTypePointer Function %7
+-%9 = OpTypeFunction %7 %8
++%25 = OpTypeFunction %7 %8 %8
+ %14 = OpConstant %6 0.5
+ %15 = OpConstantComposite %7 %14 %14
+ %19 = OpTypePointer Output %7
+ %20 = OpVariable %19 Output
+ %21 = OpConstant %6 0
+ %22 = OpConstantComposite %7 %21 %21
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %23 = OpVariable %8 Function
++%31 = OpVariable %8 Function
+ OpStore %23 %22
+-%24 = OpFunctionCall %7 %11 %23
++OpStore %31 %15
++%32 = OpFunctionCall %7 %11 %23 %31
+-OpStore %20 %24
++OpStore %20 %32
+ OpReturn
+ OpFunctionEnd
+-%11 = OpFunction %7 None %9
++%11 = OpFunction %7 None %25
+ %10 = OpFunctionParameter %8
++%26 = OpFunctionParameter %8
+ %12 = OpLabel
+ %13 = OpLoad %7 %10
+-%16 = OpFAdd %7 %13 %15
++%27 = OpLoad %7 %26
++%28 = OpFAdd %7 %13 %27
+-OpReturnValue %16
++OpReturnValue %28
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, DifferentFunctionParameterCountNoDebug) {
+ constexpr char kSrcNoDebug[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 10
+; Bound: 25
+; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %20
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %20 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 2
+ %8 = OpTypePointer Function %7
+ %9 = OpTypeFunction %7 %8
+ %14 = OpConstant %6 0.5
+ %15 = OpConstantComposite %7 %14 %14
+ %19 = OpTypePointer Output %7
+ %20 = OpVariable %19 Output
+ %21 = OpConstant %6 0
+ %22 = OpConstantComposite %7 %21 %21
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %23 = OpVariable %8 Function
+ OpStore %23 %22
+ %24 = OpFunctionCall %7 %11 %23
+ OpStore %20 %24
+ OpReturn
+ OpFunctionEnd
+ %11 = OpFunction %7 None %9
+ %10 = OpFunctionParameter %8
+ %12 = OpLabel
+ %13 = OpLoad %7 %10
+ %16 = OpFAdd %7 %13 %15
+ OpReturnValue %16
+ OpFunctionEnd
+)";
+ constexpr char kDstNoDebug[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 10
+; Bound: 28
+; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %20
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %20 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 2
+ %8 = OpTypePointer Function %7
+ %9 = OpTypeFunction %7 %8 %8
+ %19 = OpTypePointer Output %7
+ %20 = OpVariable %19 Output
+ %21 = OpConstant %6 0
+ %22 = OpConstantComposite %7 %21 %21
+ %23 = OpConstant %6 0.5
+ %24 = OpConstantComposite %7 %23 %23
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %25 = OpVariable %8 Function
+ %26 = OpVariable %8 Function
+ OpStore %25 %22
+ OpStore %26 %24
+ %27 = OpFunctionCall %7 %12 %25 %26
+ OpStore %20 %27
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %7 None %9
+ %10 = OpFunctionParameter %8
+ %11 = OpFunctionParameter %8
+ %13 = OpLabel
+ %14 = OpLoad %7 %10
+ %15 = OpLoad %7 %11
+ %16 = OpFAdd %7 %14 %15
+ OpReturnValue %16
+ OpFunctionEnd
+
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 25
++; Bound: 34
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %20
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %20 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 2
+ %8 = OpTypePointer Function %7
+-%9 = OpTypeFunction %7 %8
++%25 = OpTypeFunction %7 %8 %8
+ %14 = OpConstant %6 0.5
+ %15 = OpConstantComposite %7 %14 %14
+ %19 = OpTypePointer Output %7
+ %20 = OpVariable %19 Output
+ %21 = OpConstant %6 0
+ %22 = OpConstantComposite %7 %21 %21
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %23 = OpVariable %8 Function
++%32 = OpVariable %8 Function
+ OpStore %23 %22
+-%24 = OpFunctionCall %7 %11 %23
++OpStore %32 %15
++%33 = OpFunctionCall %7 %11 %23 %32
+-OpStore %20 %24
++OpStore %20 %33
+ OpReturn
+ OpFunctionEnd
+-%11 = OpFunction %7 None %9
++%11 = OpFunction %7 None %25
+-%10 = OpFunctionParameter %8
++%26 = OpFunctionParameter %8
++%27 = OpFunctionParameter %8
+ %12 = OpLabel
+-%13 = OpLoad %7 %10
++%13 = OpLoad %7 %26
+-%16 = OpFAdd %7 %13 %15
++%28 = OpLoad %7 %27
++%29 = OpFAdd %7 %13 %28
+-OpReturnValue %16
++OpReturnValue %29
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/different_function_parameter_count_dst.spvasm b/test/diff/diff_files/different_function_parameter_count_dst.spvasm
new file mode 100644
index 00000000..92442601
--- /dev/null
+++ b/test/diff/diff_files/different_function_parameter_count_dst.spvasm
@@ -0,0 +1,52 @@
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 10
+; Bound: 28
+; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %20
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ OpName %4 "main"
+ OpName %12 "f(vf2;vf2;"
+ OpName %10 "v"
+ OpName %11 "v2"
+ OpName %20 "o"
+ OpName %25 "param"
+ OpName %26 "param"
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %20 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 2
+ %8 = OpTypePointer Function %7
+ %9 = OpTypeFunction %7 %8 %8
+ %19 = OpTypePointer Output %7
+ %20 = OpVariable %19 Output
+ %21 = OpConstant %6 0
+ %22 = OpConstantComposite %7 %21 %21
+ %23 = OpConstant %6 0.5
+ %24 = OpConstantComposite %7 %23 %23
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %25 = OpVariable %8 Function
+ %26 = OpVariable %8 Function
+ OpStore %25 %22
+ OpStore %26 %24
+ %27 = OpFunctionCall %7 %12 %25 %26
+ OpStore %20 %27
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %7 None %9
+ %10 = OpFunctionParameter %8
+ %11 = OpFunctionParameter %8
+ %13 = OpLabel
+ %14 = OpLoad %7 %10
+ %15 = OpLoad %7 %11
+ %16 = OpFAdd %7 %14 %15
+ OpReturnValue %16
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/different_function_parameter_count_src.spvasm b/test/diff/diff_files/different_function_parameter_count_src.spvasm
new file mode 100644
index 00000000..6ee2408a
--- /dev/null
+++ b/test/diff/diff_files/different_function_parameter_count_src.spvasm
@@ -0,0 +1,46 @@
+;; Test where src and dst have a function with different parameter count.
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 10
+; Bound: 25
+; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %20
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ OpName %4 "main"
+ OpName %11 "f(vf2;"
+ OpName %10 "v"
+ OpName %20 "o"
+ OpName %23 "param"
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %20 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 2
+ %8 = OpTypePointer Function %7
+ %9 = OpTypeFunction %7 %8
+ %14 = OpConstant %6 0.5
+ %15 = OpConstantComposite %7 %14 %14
+ %19 = OpTypePointer Output %7
+ %20 = OpVariable %19 Output
+ %21 = OpConstant %6 0
+ %22 = OpConstantComposite %7 %21 %21
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %23 = OpVariable %8 Function
+ OpStore %23 %22
+ %24 = OpFunctionCall %7 %11 %23
+ OpStore %20 %24
+ OpReturn
+ OpFunctionEnd
+ %11 = OpFunction %7 None %9
+ %10 = OpFunctionParameter %8
+ %12 = OpLabel
+ %13 = OpLoad %7 %10
+ %16 = OpFAdd %7 %13 %15
+ OpReturnValue %16
+ OpFunctionEnd
diff --git a/test/diff/diff_files/extra_if_block_autogen.cpp b/test/diff/diff_files/extra_if_block_autogen.cpp
new file mode 100644
index 00000000..4f913198
--- /dev/null
+++ b/test/diff/diff_files/extra_if_block_autogen.cpp
@@ -0,0 +1,867 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Test where src has an extra if block in one function, and dst has an extra
+// if block in another function.
+constexpr char kSrc[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %63 %68
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "f1("
+ OpName %10 "f2("
+ OpName %13 "v"
+ OpName %16 "Buffer"
+ OpMemberName %16 0 "flag1"
+ OpMemberName %16 1 "flag2"
+ OpName %18 ""
+ OpName %45 "v"
+ OpName %63 "color"
+ OpName %68 "v"
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %10 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ OpMemberDecorate %16 0 RelaxedPrecision
+ OpMemberDecorate %16 0 Offset 0
+ OpMemberDecorate %16 1 RelaxedPrecision
+ OpMemberDecorate %16 1 Offset 4
+ OpDecorate %16 Block
+ OpDecorate %18 DescriptorSet 0
+ OpDecorate %18 Binding 0
+ OpDecorate %23 RelaxedPrecision
+ OpDecorate %30 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %34 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %38 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %40 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %45 RelaxedPrecision
+ OpDecorate %47 RelaxedPrecision
+ OpDecorate %48 RelaxedPrecision
+ OpDecorate %50 RelaxedPrecision
+ OpDecorate %51 RelaxedPrecision
+ OpDecorate %54 RelaxedPrecision
+ OpDecorate %55 RelaxedPrecision
+ OpDecorate %56 RelaxedPrecision
+ OpDecorate %57 RelaxedPrecision
+ OpDecorate %58 RelaxedPrecision
+ OpDecorate %63 RelaxedPrecision
+ OpDecorate %63 Location 0
+ OpDecorate %64 RelaxedPrecision
+ OpDecorate %65 RelaxedPrecision
+ OpDecorate %66 RelaxedPrecision
+ OpDecorate %68 RelaxedPrecision
+ OpDecorate %68 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeFunction %6
+ %12 = OpTypePointer Function %6
+ %14 = OpConstant %6 0
+ %15 = OpTypeInt 32 0
+ %16 = OpTypeStruct %15 %15
+ %17 = OpTypePointer Uniform %16
+ %18 = OpVariable %17 Uniform
+ %19 = OpTypeInt 32 1
+ %20 = OpConstant %19 0
+ %21 = OpTypePointer Uniform %15
+ %24 = OpConstant %15 0
+ %25 = OpTypeBool
+ %29 = OpConstant %6 1
+ %32 = OpConstant %19 1
+ %49 = OpConstant %6 10
+ %52 = OpConstant %6 0.5
+ %53 = OpConstant %6 0.699999988
+ %61 = OpTypeVector %6 4
+ %62 = OpTypePointer Output %61
+ %63 = OpVariable %62 Output
+ %67 = OpTypePointer Input %6
+ %68 = OpVariable %67 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %64 = OpFunctionCall %6 %8
+ %65 = OpFunctionCall %6 %10
+ %66 = OpCompositeConstruct %61 %64 %65 %14 %29
+ OpStore %63 %66
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %6 None %7
+ %9 = OpLabel
+ %13 = OpVariable %12 Function
+ OpStore %13 %14
+ %22 = OpAccessChain %21 %18 %20
+ %23 = OpLoad %15 %22
+ %26 = OpINotEqual %25 %23 %24
+ OpSelectionMerge %28 None
+ OpBranchConditional %26 %27 %28
+ %27 = OpLabel
+ %30 = OpLoad %6 %13
+ %31 = OpFAdd %6 %30 %29
+ OpStore %13 %31
+ OpBranch %28
+ %28 = OpLabel
+ %33 = OpAccessChain %21 %18 %32
+ %34 = OpLoad %15 %33
+ %35 = OpConvertUToF %6 %34
+ %36 = OpExtInst %6 %1 Log2 %35
+ %37 = OpLoad %6 %13
+ %38 = OpFAdd %6 %37 %36
+ OpStore %13 %38
+ %39 = OpLoad %6 %13
+ %40 = OpLoad %6 %13
+ %41 = OpExtInst %6 %1 Sqrt %40
+ %42 = OpFSub %6 %39 %41
+ OpReturnValue %42
+ OpFunctionEnd
+ %10 = OpFunction %6 None %7
+ %11 = OpLabel
+ %45 = OpVariable %12 Function
+ %46 = OpAccessChain %21 %18 %20
+ %47 = OpLoad %15 %46
+ %48 = OpConvertUToF %6 %47
+ %50 = OpFDiv %6 %48 %49
+ OpStore %45 %50
+ %51 = OpLoad %6 %45
+ %54 = OpExtInst %6 %1 FClamp %51 %52 %53
+ %55 = OpLoad %6 %45
+ %56 = OpFMul %6 %55 %54
+ OpStore %45 %56
+ %57 = OpLoad %6 %45
+ %58 = OpExtInst %6 %1 Exp %57
+ OpReturnValue %58
+ OpFunctionEnd
+)";
+constexpr char kDst[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %63 %69
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "f1("
+ OpName %10 "f2("
+ OpName %13 "v"
+ OpName %16 "Buffer"
+ OpMemberName %16 0 "flag1"
+ OpMemberName %16 1 "flag2"
+ OpName %18 ""
+ OpName %34 "v"
+ OpName %63 "color"
+ OpName %69 "v"
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %10 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ OpMemberDecorate %16 0 RelaxedPrecision
+ OpMemberDecorate %16 0 Offset 0
+ OpMemberDecorate %16 1 RelaxedPrecision
+ OpMemberDecorate %16 1 Offset 4
+ OpDecorate %16 Block
+ OpDecorate %18 DescriptorSet 0
+ OpDecorate %18 Binding 0
+ OpDecorate %23 RelaxedPrecision
+ OpDecorate %24 RelaxedPrecision
+ OpDecorate %25 RelaxedPrecision
+ OpDecorate %26 RelaxedPrecision
+ OpDecorate %27 RelaxedPrecision
+ OpDecorate %28 RelaxedPrecision
+ OpDecorate %29 RelaxedPrecision
+ OpDecorate %30 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %34 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %38 RelaxedPrecision
+ OpDecorate %40 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %44 RelaxedPrecision
+ OpDecorate %45 RelaxedPrecision
+ OpDecorate %46 RelaxedPrecision
+ OpDecorate %48 RelaxedPrecision
+ OpDecorate %55 RelaxedPrecision
+ OpDecorate %56 RelaxedPrecision
+ OpDecorate %57 RelaxedPrecision
+ OpDecorate %58 RelaxedPrecision
+ OpDecorate %63 RelaxedPrecision
+ OpDecorate %63 Location 0
+ OpDecorate %64 RelaxedPrecision
+ OpDecorate %65 RelaxedPrecision
+ OpDecorate %67 RelaxedPrecision
+ OpDecorate %69 RelaxedPrecision
+ OpDecorate %69 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeFunction %6
+ %12 = OpTypePointer Function %6
+ %14 = OpConstant %6 0
+ %15 = OpTypeInt 32 0
+ %16 = OpTypeStruct %15 %15
+ %17 = OpTypePointer Uniform %16
+ %18 = OpVariable %17 Uniform
+ %19 = OpTypeInt 32 1
+ %20 = OpConstant %19 1
+ %21 = OpTypePointer Uniform %15
+ %35 = OpConstant %19 0
+ %39 = OpConstant %6 10
+ %42 = OpConstant %6 0.5
+ %43 = OpConstant %6 0.699999988
+ %49 = OpConstant %15 0
+ %50 = OpTypeBool
+ %54 = OpConstant %6 0.100000001
+ %61 = OpTypeVector %6 4
+ %62 = OpTypePointer Output %61
+ %63 = OpVariable %62 Output
+ %66 = OpConstant %6 1
+ %68 = OpTypePointer Input %6
+ %69 = OpVariable %68 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %64 = OpFunctionCall %6 %8
+ %65 = OpFunctionCall %6 %10
+ %67 = OpCompositeConstruct %61 %64 %65 %14 %66
+ OpStore %63 %67
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %6 None %7
+ %9 = OpLabel
+ %13 = OpVariable %12 Function
+ OpStore %13 %14
+ %22 = OpAccessChain %21 %18 %20
+ %23 = OpLoad %15 %22
+ %24 = OpConvertUToF %6 %23
+ %25 = OpExtInst %6 %1 Log2 %24
+ %26 = OpLoad %6 %13
+ %27 = OpFAdd %6 %26 %25
+ OpStore %13 %27
+ %28 = OpLoad %6 %13
+ %29 = OpLoad %6 %13
+ %30 = OpExtInst %6 %1 Sqrt %29
+ %31 = OpFSub %6 %28 %30
+ OpReturnValue %31
+ OpFunctionEnd
+ %10 = OpFunction %6 None %7
+ %11 = OpLabel
+ %34 = OpVariable %12 Function
+ %36 = OpAccessChain %21 %18 %35
+ %37 = OpLoad %15 %36
+ %38 = OpConvertUToF %6 %37
+ %40 = OpFDiv %6 %38 %39
+ OpStore %34 %40
+ %41 = OpLoad %6 %34
+ %44 = OpExtInst %6 %1 FClamp %41 %42 %43
+ %45 = OpLoad %6 %34
+ %46 = OpFMul %6 %45 %44
+ OpStore %34 %46
+ %47 = OpAccessChain %21 %18 %20
+ %48 = OpLoad %15 %47
+ %51 = OpINotEqual %50 %48 %49
+ OpSelectionMerge %53 None
+ OpBranchConditional %51 %52 %53
+ %52 = OpLabel
+ %55 = OpLoad %6 %34
+ %56 = OpFSub %6 %55 %54
+ OpStore %34 %56
+ OpBranch %53
+ %53 = OpLabel
+ %57 = OpLoad %6 %34
+ %58 = OpExtInst %6 %1 Exp %57
+ OpReturnValue %58
+ OpFunctionEnd
+
+)";
+
+TEST(DiffTest, ExtraIfBlock) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 69
++; Bound: 81
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %63 %68
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "f1("
+ OpName %10 "f2("
+ OpName %13 "v"
+ OpName %16 "Buffer"
+ OpMemberName %16 0 "flag1"
+ OpMemberName %16 1 "flag2"
+ OpName %18 ""
+ OpName %45 "v"
+ OpName %63 "color"
+ OpName %68 "v"
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %10 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ OpMemberDecorate %16 0 RelaxedPrecision
+ OpMemberDecorate %16 0 Offset 0
+ OpMemberDecorate %16 1 RelaxedPrecision
+ OpMemberDecorate %16 1 Offset 4
+ OpDecorate %16 Block
+ OpDecorate %18 DescriptorSet 0
+ OpDecorate %18 Binding 0
+-OpDecorate %23 RelaxedPrecision
+-OpDecorate %30 RelaxedPrecision
+-OpDecorate %31 RelaxedPrecision
+ OpDecorate %34 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %38 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %40 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %45 RelaxedPrecision
+ OpDecorate %47 RelaxedPrecision
+ OpDecorate %48 RelaxedPrecision
+ OpDecorate %50 RelaxedPrecision
+ OpDecorate %51 RelaxedPrecision
+ OpDecorate %54 RelaxedPrecision
+ OpDecorate %55 RelaxedPrecision
+ OpDecorate %56 RelaxedPrecision
++OpDecorate %72 RelaxedPrecision
+ OpDecorate %57 RelaxedPrecision
++OpDecorate %77 RelaxedPrecision
++OpDecorate %78 RelaxedPrecision
+ OpDecorate %58 RelaxedPrecision
+ OpDecorate %63 RelaxedPrecision
+ OpDecorate %63 Location 0
+ OpDecorate %64 RelaxedPrecision
+ OpDecorate %65 RelaxedPrecision
+ OpDecorate %66 RelaxedPrecision
+ OpDecorate %68 RelaxedPrecision
+ OpDecorate %68 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeFunction %6
+ %12 = OpTypePointer Function %6
+ %14 = OpConstant %6 0
+ %15 = OpTypeInt 32 0
+ %16 = OpTypeStruct %15 %15
+ %17 = OpTypePointer Uniform %16
+ %18 = OpVariable %17 Uniform
+ %19 = OpTypeInt 32 1
+ %20 = OpConstant %19 0
+ %21 = OpTypePointer Uniform %15
+ %24 = OpConstant %15 0
+ %25 = OpTypeBool
+ %29 = OpConstant %6 1
+ %32 = OpConstant %19 1
+ %49 = OpConstant %6 10
+ %52 = OpConstant %6 0.5
++%76 = OpConstant %6 0.100000001
+ %53 = OpConstant %6 0.699999988
+ %61 = OpTypeVector %6 4
+ %62 = OpTypePointer Output %61
+ %63 = OpVariable %62 Output
+ %67 = OpTypePointer Input %6
+ %68 = OpVariable %67 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %64 = OpFunctionCall %6 %8
+ %65 = OpFunctionCall %6 %10
+ %66 = OpCompositeConstruct %61 %64 %65 %14 %29
+ OpStore %63 %66
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %6 None %7
+ %9 = OpLabel
+ %13 = OpVariable %12 Function
+ OpStore %13 %14
+-%22 = OpAccessChain %21 %18 %20
+-%23 = OpLoad %15 %22
+-%26 = OpINotEqual %25 %23 %24
+-OpSelectionMerge %28 None
+-OpBranchConditional %26 %27 %28
+-%27 = OpLabel
+-%30 = OpLoad %6 %13
+-%31 = OpFAdd %6 %30 %29
+-OpStore %13 %31
+-OpBranch %28
+-%28 = OpLabel
+ %33 = OpAccessChain %21 %18 %32
+ %34 = OpLoad %15 %33
+ %35 = OpConvertUToF %6 %34
+ %36 = OpExtInst %6 %1 Log2 %35
+ %37 = OpLoad %6 %13
+ %38 = OpFAdd %6 %37 %36
+ OpStore %13 %38
+ %39 = OpLoad %6 %13
+ %40 = OpLoad %6 %13
+ %41 = OpExtInst %6 %1 Sqrt %40
+ %42 = OpFSub %6 %39 %41
+ OpReturnValue %42
+ OpFunctionEnd
+ %10 = OpFunction %6 None %7
+ %11 = OpLabel
+ %45 = OpVariable %12 Function
+ %46 = OpAccessChain %21 %18 %20
+ %47 = OpLoad %15 %46
+ %48 = OpConvertUToF %6 %47
+ %50 = OpFDiv %6 %48 %49
+ OpStore %45 %50
+ %51 = OpLoad %6 %45
+ %54 = OpExtInst %6 %1 FClamp %51 %52 %53
+ %55 = OpLoad %6 %45
+ %56 = OpFMul %6 %55 %54
+ OpStore %45 %56
++%71 = OpAccessChain %21 %18 %32
++%72 = OpLoad %15 %71
++%73 = OpINotEqual %25 %72 %24
++OpSelectionMerge %75 None
++OpBranchConditional %73 %74 %75
++%74 = OpLabel
+ %57 = OpLoad %6 %45
++%77 = OpFSub %6 %57 %76
++OpStore %45 %77
++OpBranch %75
++%75 = OpLabel
++%78 = OpLoad %6 %45
+-%58 = OpExtInst %6 %1 Exp %57
++%58 = OpExtInst %6 %1 Exp %78
+ OpReturnValue %58
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, ExtraIfBlockNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %63 %68
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %10 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ OpMemberDecorate %16 0 RelaxedPrecision
+ OpMemberDecorate %16 0 Offset 0
+ OpMemberDecorate %16 1 RelaxedPrecision
+ OpMemberDecorate %16 1 Offset 4
+ OpDecorate %16 Block
+ OpDecorate %18 DescriptorSet 0
+ OpDecorate %18 Binding 0
+ OpDecorate %23 RelaxedPrecision
+ OpDecorate %30 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %34 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %38 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %40 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %45 RelaxedPrecision
+ OpDecorate %47 RelaxedPrecision
+ OpDecorate %48 RelaxedPrecision
+ OpDecorate %50 RelaxedPrecision
+ OpDecorate %51 RelaxedPrecision
+ OpDecorate %54 RelaxedPrecision
+ OpDecorate %55 RelaxedPrecision
+ OpDecorate %56 RelaxedPrecision
+ OpDecorate %57 RelaxedPrecision
+ OpDecorate %58 RelaxedPrecision
+ OpDecorate %63 RelaxedPrecision
+ OpDecorate %63 Location 0
+ OpDecorate %64 RelaxedPrecision
+ OpDecorate %65 RelaxedPrecision
+ OpDecorate %66 RelaxedPrecision
+ OpDecorate %68 RelaxedPrecision
+ OpDecorate %68 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeFunction %6
+ %12 = OpTypePointer Function %6
+ %14 = OpConstant %6 0
+ %15 = OpTypeInt 32 0
+ %16 = OpTypeStruct %15 %15
+ %17 = OpTypePointer Uniform %16
+ %18 = OpVariable %17 Uniform
+ %19 = OpTypeInt 32 1
+ %20 = OpConstant %19 0
+ %21 = OpTypePointer Uniform %15
+ %24 = OpConstant %15 0
+ %25 = OpTypeBool
+ %29 = OpConstant %6 1
+ %32 = OpConstant %19 1
+ %49 = OpConstant %6 10
+ %52 = OpConstant %6 0.5
+ %53 = OpConstant %6 0.699999988
+ %61 = OpTypeVector %6 4
+ %62 = OpTypePointer Output %61
+ %63 = OpVariable %62 Output
+ %67 = OpTypePointer Input %6
+ %68 = OpVariable %67 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %64 = OpFunctionCall %6 %8
+ %65 = OpFunctionCall %6 %10
+ %66 = OpCompositeConstruct %61 %64 %65 %14 %29
+ OpStore %63 %66
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %6 None %7
+ %9 = OpLabel
+ %13 = OpVariable %12 Function
+ OpStore %13 %14
+ %22 = OpAccessChain %21 %18 %20
+ %23 = OpLoad %15 %22
+ %26 = OpINotEqual %25 %23 %24
+ OpSelectionMerge %28 None
+ OpBranchConditional %26 %27 %28
+ %27 = OpLabel
+ %30 = OpLoad %6 %13
+ %31 = OpFAdd %6 %30 %29
+ OpStore %13 %31
+ OpBranch %28
+ %28 = OpLabel
+ %33 = OpAccessChain %21 %18 %32
+ %34 = OpLoad %15 %33
+ %35 = OpConvertUToF %6 %34
+ %36 = OpExtInst %6 %1 Log2 %35
+ %37 = OpLoad %6 %13
+ %38 = OpFAdd %6 %37 %36
+ OpStore %13 %38
+ %39 = OpLoad %6 %13
+ %40 = OpLoad %6 %13
+ %41 = OpExtInst %6 %1 Sqrt %40
+ %42 = OpFSub %6 %39 %41
+ OpReturnValue %42
+ OpFunctionEnd
+ %10 = OpFunction %6 None %7
+ %11 = OpLabel
+ %45 = OpVariable %12 Function
+ %46 = OpAccessChain %21 %18 %20
+ %47 = OpLoad %15 %46
+ %48 = OpConvertUToF %6 %47
+ %50 = OpFDiv %6 %48 %49
+ OpStore %45 %50
+ %51 = OpLoad %6 %45
+ %54 = OpExtInst %6 %1 FClamp %51 %52 %53
+ %55 = OpLoad %6 %45
+ %56 = OpFMul %6 %55 %54
+ OpStore %45 %56
+ %57 = OpLoad %6 %45
+ %58 = OpExtInst %6 %1 Exp %57
+ OpReturnValue %58
+ OpFunctionEnd
+
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %63 %69
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %10 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ OpMemberDecorate %16 0 RelaxedPrecision
+ OpMemberDecorate %16 0 Offset 0
+ OpMemberDecorate %16 1 RelaxedPrecision
+ OpMemberDecorate %16 1 Offset 4
+ OpDecorate %16 Block
+ OpDecorate %18 DescriptorSet 0
+ OpDecorate %18 Binding 0
+ OpDecorate %23 RelaxedPrecision
+ OpDecorate %24 RelaxedPrecision
+ OpDecorate %25 RelaxedPrecision
+ OpDecorate %26 RelaxedPrecision
+ OpDecorate %27 RelaxedPrecision
+ OpDecorate %28 RelaxedPrecision
+ OpDecorate %29 RelaxedPrecision
+ OpDecorate %30 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %34 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %38 RelaxedPrecision
+ OpDecorate %40 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %44 RelaxedPrecision
+ OpDecorate %45 RelaxedPrecision
+ OpDecorate %46 RelaxedPrecision
+ OpDecorate %48 RelaxedPrecision
+ OpDecorate %55 RelaxedPrecision
+ OpDecorate %56 RelaxedPrecision
+ OpDecorate %57 RelaxedPrecision
+ OpDecorate %58 RelaxedPrecision
+ OpDecorate %63 RelaxedPrecision
+ OpDecorate %63 Location 0
+ OpDecorate %64 RelaxedPrecision
+ OpDecorate %65 RelaxedPrecision
+ OpDecorate %67 RelaxedPrecision
+ OpDecorate %69 RelaxedPrecision
+ OpDecorate %69 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeFunction %6
+ %12 = OpTypePointer Function %6
+ %14 = OpConstant %6 0
+ %15 = OpTypeInt 32 0
+ %16 = OpTypeStruct %15 %15
+ %17 = OpTypePointer Uniform %16
+ %18 = OpVariable %17 Uniform
+ %19 = OpTypeInt 32 1
+ %20 = OpConstant %19 1
+ %21 = OpTypePointer Uniform %15
+ %35 = OpConstant %19 0
+ %39 = OpConstant %6 10
+ %42 = OpConstant %6 0.5
+ %43 = OpConstant %6 0.699999988
+ %49 = OpConstant %15 0
+ %50 = OpTypeBool
+ %54 = OpConstant %6 0.100000001
+ %61 = OpTypeVector %6 4
+ %62 = OpTypePointer Output %61
+ %63 = OpVariable %62 Output
+ %66 = OpConstant %6 1
+ %68 = OpTypePointer Input %6
+ %69 = OpVariable %68 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %64 = OpFunctionCall %6 %8
+ %65 = OpFunctionCall %6 %10
+ %67 = OpCompositeConstruct %61 %64 %65 %14 %66
+ OpStore %63 %67
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %6 None %7
+ %9 = OpLabel
+ %13 = OpVariable %12 Function
+ OpStore %13 %14
+ %22 = OpAccessChain %21 %18 %20
+ %23 = OpLoad %15 %22
+ %24 = OpConvertUToF %6 %23
+ %25 = OpExtInst %6 %1 Log2 %24
+ %26 = OpLoad %6 %13
+ %27 = OpFAdd %6 %26 %25
+ OpStore %13 %27
+ %28 = OpLoad %6 %13
+ %29 = OpLoad %6 %13
+ %30 = OpExtInst %6 %1 Sqrt %29
+ %31 = OpFSub %6 %28 %30
+ OpReturnValue %31
+ OpFunctionEnd
+ %10 = OpFunction %6 None %7
+ %11 = OpLabel
+ %34 = OpVariable %12 Function
+ %36 = OpAccessChain %21 %18 %35
+ %37 = OpLoad %15 %36
+ %38 = OpConvertUToF %6 %37
+ %40 = OpFDiv %6 %38 %39
+ OpStore %34 %40
+ %41 = OpLoad %6 %34
+ %44 = OpExtInst %6 %1 FClamp %41 %42 %43
+ %45 = OpLoad %6 %34
+ %46 = OpFMul %6 %45 %44
+ OpStore %34 %46
+ %47 = OpAccessChain %21 %18 %20
+ %48 = OpLoad %15 %47
+ %51 = OpINotEqual %50 %48 %49
+ OpSelectionMerge %53 None
+ OpBranchConditional %51 %52 %53
+ %52 = OpLabel
+ %55 = OpLoad %6 %34
+ %56 = OpFSub %6 %55 %54
+ OpStore %34 %56
+ OpBranch %53
+ %53 = OpLabel
+ %57 = OpLoad %6 %34
+ %58 = OpExtInst %6 %1 Exp %57
+ OpReturnValue %58
+ OpFunctionEnd
+
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 69
++; Bound: 81
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %63 %68
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %10 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ OpMemberDecorate %16 0 RelaxedPrecision
+ OpMemberDecorate %16 0 Offset 0
+ OpMemberDecorate %16 1 RelaxedPrecision
+ OpMemberDecorate %16 1 Offset 4
+ OpDecorate %16 Block
+ OpDecorate %18 DescriptorSet 0
+ OpDecorate %18 Binding 0
+-OpDecorate %23 RelaxedPrecision
+-OpDecorate %30 RelaxedPrecision
+-OpDecorate %31 RelaxedPrecision
+ OpDecorate %34 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %38 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %40 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %45 RelaxedPrecision
+ OpDecorate %47 RelaxedPrecision
+ OpDecorate %48 RelaxedPrecision
+ OpDecorate %50 RelaxedPrecision
+ OpDecorate %51 RelaxedPrecision
+ OpDecorate %54 RelaxedPrecision
+ OpDecorate %55 RelaxedPrecision
+ OpDecorate %56 RelaxedPrecision
++OpDecorate %72 RelaxedPrecision
+ OpDecorate %57 RelaxedPrecision
++OpDecorate %77 RelaxedPrecision
++OpDecorate %78 RelaxedPrecision
+ OpDecorate %58 RelaxedPrecision
+ OpDecorate %63 RelaxedPrecision
+ OpDecorate %63 Location 0
+ OpDecorate %64 RelaxedPrecision
+ OpDecorate %65 RelaxedPrecision
+ OpDecorate %66 RelaxedPrecision
+ OpDecorate %68 RelaxedPrecision
+ OpDecorate %68 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeFunction %6
+ %12 = OpTypePointer Function %6
+ %14 = OpConstant %6 0
+ %15 = OpTypeInt 32 0
+ %16 = OpTypeStruct %15 %15
+ %17 = OpTypePointer Uniform %16
+ %18 = OpVariable %17 Uniform
+ %19 = OpTypeInt 32 1
+ %20 = OpConstant %19 0
+ %21 = OpTypePointer Uniform %15
+ %24 = OpConstant %15 0
+ %25 = OpTypeBool
+ %29 = OpConstant %6 1
+ %32 = OpConstant %19 1
+ %49 = OpConstant %6 10
+ %52 = OpConstant %6 0.5
++%76 = OpConstant %6 0.100000001
+ %53 = OpConstant %6 0.699999988
+ %61 = OpTypeVector %6 4
+ %62 = OpTypePointer Output %61
+ %63 = OpVariable %62 Output
+ %67 = OpTypePointer Input %6
+ %68 = OpVariable %67 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %64 = OpFunctionCall %6 %8
+ %65 = OpFunctionCall %6 %10
+ %66 = OpCompositeConstruct %61 %64 %65 %14 %29
+ OpStore %63 %66
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %6 None %7
+ %9 = OpLabel
+ %13 = OpVariable %12 Function
+ OpStore %13 %14
+-%22 = OpAccessChain %21 %18 %20
+-%23 = OpLoad %15 %22
+-%26 = OpINotEqual %25 %23 %24
+-OpSelectionMerge %28 None
+-OpBranchConditional %26 %27 %28
+-%27 = OpLabel
+-%30 = OpLoad %6 %13
+-%31 = OpFAdd %6 %30 %29
+-OpStore %13 %31
+-OpBranch %28
+-%28 = OpLabel
+ %33 = OpAccessChain %21 %18 %32
+ %34 = OpLoad %15 %33
+ %35 = OpConvertUToF %6 %34
+ %36 = OpExtInst %6 %1 Log2 %35
+ %37 = OpLoad %6 %13
+ %38 = OpFAdd %6 %37 %36
+ OpStore %13 %38
+ %39 = OpLoad %6 %13
+ %40 = OpLoad %6 %13
+ %41 = OpExtInst %6 %1 Sqrt %40
+ %42 = OpFSub %6 %39 %41
+ OpReturnValue %42
+ OpFunctionEnd
+ %10 = OpFunction %6 None %7
+ %11 = OpLabel
+ %45 = OpVariable %12 Function
+ %46 = OpAccessChain %21 %18 %20
+ %47 = OpLoad %15 %46
+ %48 = OpConvertUToF %6 %47
+ %50 = OpFDiv %6 %48 %49
+ OpStore %45 %50
+ %51 = OpLoad %6 %45
+ %54 = OpExtInst %6 %1 FClamp %51 %52 %53
+ %55 = OpLoad %6 %45
+ %56 = OpFMul %6 %55 %54
+ OpStore %45 %56
++%71 = OpAccessChain %21 %18 %32
++%72 = OpLoad %15 %71
++%73 = OpINotEqual %25 %72 %24
++OpSelectionMerge %75 None
++OpBranchConditional %73 %74 %75
++%74 = OpLabel
+ %57 = OpLoad %6 %45
++%77 = OpFSub %6 %57 %76
++OpStore %45 %77
++OpBranch %75
++%75 = OpLabel
++%78 = OpLoad %6 %45
+-%58 = OpExtInst %6 %1 Exp %57
++%58 = OpExtInst %6 %1 Exp %78
+ OpReturnValue %58
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/extra_if_block_dst.spvasm b/test/diff/diff_files/extra_if_block_dst.spvasm
new file mode 100644
index 00000000..79bda830
--- /dev/null
+++ b/test/diff/diff_files/extra_if_block_dst.spvasm
@@ -0,0 +1,136 @@
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %63 %69
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "f1("
+ OpName %10 "f2("
+ OpName %13 "v"
+ OpName %16 "Buffer"
+ OpMemberName %16 0 "flag1"
+ OpMemberName %16 1 "flag2"
+ OpName %18 ""
+ OpName %34 "v"
+ OpName %63 "color"
+ OpName %69 "v"
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %10 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ OpMemberDecorate %16 0 RelaxedPrecision
+ OpMemberDecorate %16 0 Offset 0
+ OpMemberDecorate %16 1 RelaxedPrecision
+ OpMemberDecorate %16 1 Offset 4
+ OpDecorate %16 Block
+ OpDecorate %18 DescriptorSet 0
+ OpDecorate %18 Binding 0
+ OpDecorate %23 RelaxedPrecision
+ OpDecorate %24 RelaxedPrecision
+ OpDecorate %25 RelaxedPrecision
+ OpDecorate %26 RelaxedPrecision
+ OpDecorate %27 RelaxedPrecision
+ OpDecorate %28 RelaxedPrecision
+ OpDecorate %29 RelaxedPrecision
+ OpDecorate %30 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %34 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %38 RelaxedPrecision
+ OpDecorate %40 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %44 RelaxedPrecision
+ OpDecorate %45 RelaxedPrecision
+ OpDecorate %46 RelaxedPrecision
+ OpDecorate %48 RelaxedPrecision
+ OpDecorate %55 RelaxedPrecision
+ OpDecorate %56 RelaxedPrecision
+ OpDecorate %57 RelaxedPrecision
+ OpDecorate %58 RelaxedPrecision
+ OpDecorate %63 RelaxedPrecision
+ OpDecorate %63 Location 0
+ OpDecorate %64 RelaxedPrecision
+ OpDecorate %65 RelaxedPrecision
+ OpDecorate %67 RelaxedPrecision
+ OpDecorate %69 RelaxedPrecision
+ OpDecorate %69 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeFunction %6
+ %12 = OpTypePointer Function %6
+ %14 = OpConstant %6 0
+ %15 = OpTypeInt 32 0
+ %16 = OpTypeStruct %15 %15
+ %17 = OpTypePointer Uniform %16
+ %18 = OpVariable %17 Uniform
+ %19 = OpTypeInt 32 1
+ %20 = OpConstant %19 1
+ %21 = OpTypePointer Uniform %15
+ %35 = OpConstant %19 0
+ %39 = OpConstant %6 10
+ %42 = OpConstant %6 0.5
+ %43 = OpConstant %6 0.699999988
+ %49 = OpConstant %15 0
+ %50 = OpTypeBool
+ %54 = OpConstant %6 0.100000001
+ %61 = OpTypeVector %6 4
+ %62 = OpTypePointer Output %61
+ %63 = OpVariable %62 Output
+ %66 = OpConstant %6 1
+ %68 = OpTypePointer Input %6
+ %69 = OpVariable %68 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %64 = OpFunctionCall %6 %8
+ %65 = OpFunctionCall %6 %10
+ %67 = OpCompositeConstruct %61 %64 %65 %14 %66
+ OpStore %63 %67
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %6 None %7
+ %9 = OpLabel
+ %13 = OpVariable %12 Function
+ OpStore %13 %14
+ %22 = OpAccessChain %21 %18 %20
+ %23 = OpLoad %15 %22
+ %24 = OpConvertUToF %6 %23
+ %25 = OpExtInst %6 %1 Log2 %24
+ %26 = OpLoad %6 %13
+ %27 = OpFAdd %6 %26 %25
+ OpStore %13 %27
+ %28 = OpLoad %6 %13
+ %29 = OpLoad %6 %13
+ %30 = OpExtInst %6 %1 Sqrt %29
+ %31 = OpFSub %6 %28 %30
+ OpReturnValue %31
+ OpFunctionEnd
+ %10 = OpFunction %6 None %7
+ %11 = OpLabel
+ %34 = OpVariable %12 Function
+ %36 = OpAccessChain %21 %18 %35
+ %37 = OpLoad %15 %36
+ %38 = OpConvertUToF %6 %37
+ %40 = OpFDiv %6 %38 %39
+ OpStore %34 %40
+ %41 = OpLoad %6 %34
+ %44 = OpExtInst %6 %1 FClamp %41 %42 %43
+ %45 = OpLoad %6 %34
+ %46 = OpFMul %6 %45 %44
+ OpStore %34 %46
+ %47 = OpAccessChain %21 %18 %20
+ %48 = OpLoad %15 %47
+ %51 = OpINotEqual %50 %48 %49
+ OpSelectionMerge %53 None
+ OpBranchConditional %51 %52 %53
+ %52 = OpLabel
+ %55 = OpLoad %6 %34
+ %56 = OpFSub %6 %55 %54
+ OpStore %34 %56
+ OpBranch %53
+ %53 = OpLabel
+ %57 = OpLoad %6 %34
+ %58 = OpExtInst %6 %1 Exp %57
+ OpReturnValue %58
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/extra_if_block_src.spvasm b/test/diff/diff_files/extra_if_block_src.spvasm
new file mode 100644
index 00000000..1b43ccb1
--- /dev/null
+++ b/test/diff/diff_files/extra_if_block_src.spvasm
@@ -0,0 +1,137 @@
+;; Test where src has an extra if block in one function, and dst has an extra
+;; if block in another function.
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %63 %68
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "f1("
+ OpName %10 "f2("
+ OpName %13 "v"
+ OpName %16 "Buffer"
+ OpMemberName %16 0 "flag1"
+ OpMemberName %16 1 "flag2"
+ OpName %18 ""
+ OpName %45 "v"
+ OpName %63 "color"
+ OpName %68 "v"
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %10 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ OpMemberDecorate %16 0 RelaxedPrecision
+ OpMemberDecorate %16 0 Offset 0
+ OpMemberDecorate %16 1 RelaxedPrecision
+ OpMemberDecorate %16 1 Offset 4
+ OpDecorate %16 Block
+ OpDecorate %18 DescriptorSet 0
+ OpDecorate %18 Binding 0
+ OpDecorate %23 RelaxedPrecision
+ OpDecorate %30 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %34 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %38 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %40 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %45 RelaxedPrecision
+ OpDecorate %47 RelaxedPrecision
+ OpDecorate %48 RelaxedPrecision
+ OpDecorate %50 RelaxedPrecision
+ OpDecorate %51 RelaxedPrecision
+ OpDecorate %54 RelaxedPrecision
+ OpDecorate %55 RelaxedPrecision
+ OpDecorate %56 RelaxedPrecision
+ OpDecorate %57 RelaxedPrecision
+ OpDecorate %58 RelaxedPrecision
+ OpDecorate %63 RelaxedPrecision
+ OpDecorate %63 Location 0
+ OpDecorate %64 RelaxedPrecision
+ OpDecorate %65 RelaxedPrecision
+ OpDecorate %66 RelaxedPrecision
+ OpDecorate %68 RelaxedPrecision
+ OpDecorate %68 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeFunction %6
+ %12 = OpTypePointer Function %6
+ %14 = OpConstant %6 0
+ %15 = OpTypeInt 32 0
+ %16 = OpTypeStruct %15 %15
+ %17 = OpTypePointer Uniform %16
+ %18 = OpVariable %17 Uniform
+ %19 = OpTypeInt 32 1
+ %20 = OpConstant %19 0
+ %21 = OpTypePointer Uniform %15
+ %24 = OpConstant %15 0
+ %25 = OpTypeBool
+ %29 = OpConstant %6 1
+ %32 = OpConstant %19 1
+ %49 = OpConstant %6 10
+ %52 = OpConstant %6 0.5
+ %53 = OpConstant %6 0.699999988
+ %61 = OpTypeVector %6 4
+ %62 = OpTypePointer Output %61
+ %63 = OpVariable %62 Output
+ %67 = OpTypePointer Input %6
+ %68 = OpVariable %67 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %64 = OpFunctionCall %6 %8
+ %65 = OpFunctionCall %6 %10
+ %66 = OpCompositeConstruct %61 %64 %65 %14 %29
+ OpStore %63 %66
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %6 None %7
+ %9 = OpLabel
+ %13 = OpVariable %12 Function
+ OpStore %13 %14
+ %22 = OpAccessChain %21 %18 %20
+ %23 = OpLoad %15 %22
+ %26 = OpINotEqual %25 %23 %24
+ OpSelectionMerge %28 None
+ OpBranchConditional %26 %27 %28
+ %27 = OpLabel
+ %30 = OpLoad %6 %13
+ %31 = OpFAdd %6 %30 %29
+ OpStore %13 %31
+ OpBranch %28
+ %28 = OpLabel
+ %33 = OpAccessChain %21 %18 %32
+ %34 = OpLoad %15 %33
+ %35 = OpConvertUToF %6 %34
+ %36 = OpExtInst %6 %1 Log2 %35
+ %37 = OpLoad %6 %13
+ %38 = OpFAdd %6 %37 %36
+ OpStore %13 %38
+ %39 = OpLoad %6 %13
+ %40 = OpLoad %6 %13
+ %41 = OpExtInst %6 %1 Sqrt %40
+ %42 = OpFSub %6 %39 %41
+ OpReturnValue %42
+ OpFunctionEnd
+ %10 = OpFunction %6 None %7
+ %11 = OpLabel
+ %45 = OpVariable %12 Function
+ %46 = OpAccessChain %21 %18 %20
+ %47 = OpLoad %15 %46
+ %48 = OpConvertUToF %6 %47
+ %50 = OpFDiv %6 %48 %49
+ OpStore %45 %50
+ %51 = OpLoad %6 %45
+ %54 = OpExtInst %6 %1 FClamp %51 %52 %53
+ %55 = OpLoad %6 %45
+ %56 = OpFMul %6 %55 %54
+ OpStore %45 %56
+ %57 = OpLoad %6 %45
+ %58 = OpExtInst %6 %1 Exp %57
+ OpReturnValue %58
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/generate_tests.py b/test/diff/diff_files/generate_tests.py
new file mode 100755
index 00000000..cc3175d3
--- /dev/null
+++ b/test/diff/diff_files/generate_tests.py
@@ -0,0 +1,304 @@
+#! /usr/bin/python3
+#
+# Copyright (c) 2022 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.
+
+import glob
+import os
+import subprocess
+import sys
+
+# A handful of relevant tests are hand-picked to generate extra unit tests with
+# specific options of spirv-diff.
+IGNORE_SET_BINDING_TESTS = ['different_decorations_vertex']
+IGNORE_LOCATION_TESTS = ['different_decorations_fragment']
+IGNORE_DECORATIONS_TESTS = ['different_decorations_vertex', 'different_decorations_fragment']
+DUMP_IDS_TESTS = ['basic', 'int_vs_uint_constants', 'multiple_same_entry_points', 'small_functions_small_diffs']
+
+LICENSE = u"""Copyright (c) 2022 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.
+"""
+
+TEMPLATE_TEST_FILE = u"""// GENERATED FILE - DO NOT EDIT.
+// Generated by {script_name}
+//
+{license}
+
+#include "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {{
+namespace diff {{
+namespace {{
+
+{test_comment}
+constexpr char kSrc[] = R"({src_spirv})";
+constexpr char kDst[] = R"({dst_spirv})";
+
+TEST(DiffTest, {test_name}) {{
+ constexpr char kDiff[] = R"({diff_spirv})";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}}
+
+TEST(DiffTest, {test_name}NoDebug) {{
+ constexpr char kSrcNoDebug[] = R"({src_spirv_no_debug})";
+ constexpr char kDstNoDebug[] = R"({dst_spirv_no_debug})";
+ constexpr char kDiff[] = R"({diff_spirv_no_debug})";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}}
+{extra_tests}
+}} // namespace
+}} // namespace diff
+}} // namespace spvtools
+"""
+
+TEMPLATE_TEST_FUNC = u"""
+TEST(DiffTest, {test_name}{test_tag}) {{
+ constexpr char kDiff[] = R"({diff_spirv})";
+ Options options;
+ {test_options}
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}}
+"""
+
+TEMPLATE_TEST_FILES_CMAKE = u"""# GENERATED FILE - DO NOT EDIT.
+# Generated by {script_name}
+#
+{license}
+
+list(APPEND DIFF_TEST_FILES
+{test_files}
+)
+"""
+
+VARIANT_NONE = 0
+VARIANT_IGNORE_SET_BINDING = 1
+VARIANT_IGNORE_LOCATION = 2
+VARIANT_IGNORE_DECORATIONS = 3
+VARIANT_DUMP_IDS = 4
+
+def print_usage():
+ print("Usage: {} <path-to-spirv-diff>".format(sys.argv[0]))
+
+def remove_debug_info(in_path):
+ tmp_dir = '.no_dbg'
+
+ if not os.path.exists(tmp_dir):
+ os.makedirs(tmp_dir)
+
+ (in_basename, in_ext) = os.path.splitext(in_path)
+ out_name = in_basename + '_no_dbg' + in_ext
+ out_path = os.path.join(tmp_dir, out_name)
+
+ with open(in_path, 'r') as fin:
+ with open(out_path, 'w') as fout:
+ for line in fin:
+ ops = line.strip().split()
+ op = ops[0] if len(ops) > 0 else ''
+ if (op != ';;' and op != 'OpName' and op != 'OpMemberName' and op != 'OpString' and
+ op != 'OpLine' and op != 'OpNoLine' and op != 'OpModuleProcessed'):
+ fout.write(line)
+
+ return out_path
+
+def make_src_file(test_name):
+ return '{}_src.spvasm'.format(test_name)
+
+def make_dst_file(test_name):
+ return '{}_dst.spvasm'.format(test_name)
+
+def make_cpp_file(test_name):
+ return '{}_autogen.cpp'.format(test_name)
+
+def make_camel_case(test_name):
+ return test_name.replace('_', ' ').title().replace(' ', '')
+
+def make_comment(text, comment_prefix):
+ return '\n'.join([comment_prefix + (' ' if line.strip() else '') + line for line in text.splitlines()])
+
+def read_file(file_name):
+ with open(file_name, 'r') as f:
+ content = f.read()
+
+ # Use unix line endings.
+ content = content.replace('\r\n', '\n')
+
+ return content
+
+def parse_test_comment(src_spirv_file_name, src_spirv):
+ src_spirv_lines = src_spirv.splitlines()
+ comment_line_count = 0
+ while comment_line_count < len(src_spirv_lines):
+ if not src_spirv_lines[comment_line_count].strip().startswith(';;'):
+ break
+ comment_line_count += 1
+
+ if comment_line_count == 0:
+ print("Expected comment on test file '{}'. See README.md next to this file.".format(src_spirv_file_name))
+ sys.exit(1)
+
+ comment_block = src_spirv_lines[:comment_line_count]
+ spirv_block = src_spirv_lines[comment_line_count:]
+
+ comment_block = ['// ' + line.replace(';;', '').strip() for line in comment_block]
+
+ return '\n'.join(spirv_block), '\n'.join(comment_block)
+
+def run_diff_tool(diff_tool, src_file, dst_file, variant):
+ args = [diff_tool]
+
+ if variant == VARIANT_IGNORE_SET_BINDING or variant == VARIANT_IGNORE_DECORATIONS:
+ args.append('--ignore-set-binding')
+
+ if variant == VARIANT_IGNORE_LOCATION or variant == VARIANT_IGNORE_DECORATIONS:
+ args.append('--ignore-location')
+
+ if variant == VARIANT_DUMP_IDS:
+ args.append('--with-id-map')
+
+ args.append('--no-color')
+ args.append('--no-indent')
+
+ args.append(src_file)
+ args.append(dst_file)
+
+ success = True
+ print(' '.join(args))
+ process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+ out, err = process.communicate()
+
+ if process.returncode != 0:
+ print(err)
+ sys.exit(process.returncode)
+
+ # Use unix line endings.
+ out = out.replace('\r\n', '\n')
+
+ return out
+
+def generate_extra_test(diff_tool, src_file, dst_file, variant, test_name_camel_case, test_tag, test_options):
+ diff = run_diff_tool(diff_tool, src_file, dst_file, variant)
+ return TEMPLATE_TEST_FUNC.format(
+ test_name = test_name_camel_case,
+ test_tag = test_tag,
+ test_options = test_options,
+ diff_spirv = diff)
+
+def generate_test(diff_tool, test_name):
+ src_file = make_src_file(test_name)
+ dst_file = make_dst_file(test_name)
+ src_file_no_debug = remove_debug_info(src_file)
+ dst_file_no_debug = remove_debug_info(dst_file)
+
+ src_spirv = read_file(src_file)
+ dst_spirv = read_file(dst_file)
+ src_spirv_no_debug = read_file(src_file_no_debug)
+ dst_spirv_no_debug = read_file(dst_file_no_debug)
+
+ test_name_camel_case = make_camel_case(test_name)
+
+ diff_spirv = run_diff_tool(diff_tool, src_file, dst_file, VARIANT_NONE)
+ diff_spirv_no_debug = run_diff_tool(diff_tool, src_file_no_debug, dst_file_no_debug, VARIANT_NONE)
+
+ extra_tests = []
+
+ if test_name in IGNORE_SET_BINDING_TESTS:
+ extra_tests.append(generate_extra_test(diff_tool, src_file, dst_file, VARIANT_IGNORE_SET_BINDING,
+ test_name_camel_case, 'IgnoreSetBinding', 'options.ignore_set_binding = true;'))
+
+ if test_name in IGNORE_LOCATION_TESTS:
+ extra_tests.append(generate_extra_test(diff_tool, src_file, dst_file, VARIANT_IGNORE_LOCATION,
+ test_name_camel_case, 'IgnoreLocation', 'options.ignore_location = true;'))
+
+ if test_name in IGNORE_DECORATIONS_TESTS:
+ extra_tests.append(generate_extra_test(diff_tool, src_file, dst_file, VARIANT_IGNORE_DECORATIONS,
+ test_name_camel_case, 'IgnoreSetBindingLocation',
+ '\n '.join(['options.ignore_set_binding = true;', 'options.ignore_location = true;'])))
+
+ if test_name in DUMP_IDS_TESTS:
+ extra_tests.append(generate_extra_test(diff_tool, src_file, dst_file, VARIANT_DUMP_IDS,
+ test_name_camel_case, 'DumpIds', 'options.dump_id_map = true;'))
+
+ src_spirv, test_comment = parse_test_comment(src_file, src_spirv)
+
+ test_file = TEMPLATE_TEST_FILE.format(
+ script_name = os.path.basename(__file__),
+ license = make_comment(LICENSE, '//'),
+ test_comment = test_comment,
+ test_name = test_name_camel_case,
+ src_spirv = src_spirv,
+ dst_spirv = dst_spirv,
+ diff_spirv = diff_spirv,
+ src_spirv_no_debug = src_spirv_no_debug,
+ dst_spirv_no_debug = dst_spirv_no_debug,
+ diff_spirv_no_debug = diff_spirv_no_debug,
+ extra_tests = ''.join(extra_tests))
+
+ test_file_name = make_cpp_file(test_name)
+ with open(test_file_name, 'wb') as fout:
+ fout.write(str.encode(test_file))
+
+ return test_file_name
+
+def generate_tests(diff_tool, test_names):
+ return [generate_test(diff_tool, test_name) for test_name in test_names]
+
+def generate_cmake(test_files):
+ cmake = TEMPLATE_TEST_FILES_CMAKE.format(
+ script_name = os.path.basename(__file__),
+ license = make_comment(LICENSE, '#'),
+ test_files = '\n'.join(['"diff_files/{}"'.format(f) for f in test_files]))
+
+ with open('diff_test_files_autogen.cmake', 'wb') as fout:
+ fout.write(str.encode(cmake))
+
+def main():
+
+ if len(sys.argv) != 2:
+ print_usage()
+ return 1
+
+ diff_tool = sys.argv[1]
+ if not os.path.exists(diff_tool):
+ print("No such file: {}".format(diff_tool))
+ print_usage()
+ return 1
+
+ diff_tool = os.path.realpath(diff_tool)
+ os.chdir(os.path.dirname(__file__))
+
+ test_names = sorted([f[:-11] for f in glob.glob("*_src.spvasm")])
+
+ test_files = generate_tests(diff_tool, test_names)
+
+ generate_cmake(test_files)
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/test/diff/diff_files/index_signedness_autogen.cpp b/test/diff/diff_files/index_signedness_autogen.cpp
new file mode 100644
index 00000000..ab650be1
--- /dev/null
+++ b/test/diff/diff_files/index_signedness_autogen.cpp
@@ -0,0 +1,733 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Test where signedness of indices are different between src and dst.
+constexpr char kSrc[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %13 "BufferOut"
+ OpMemberName %13 0 "o1"
+ OpMemberName %13 1 "o2"
+ OpMemberName %13 2 "o3"
+ OpName %15 ""
+ OpName %22 "BufferIn"
+ OpMemberName %22 0 "i1"
+ OpMemberName %22 1 "i2"
+ OpName %24 ""
+ OpDecorate %8 ArrayStride 4
+ OpDecorate %9 ArrayStride 4
+ OpDecorate %11 ArrayStride 4
+ OpDecorate %12 ArrayStride 8
+ OpMemberDecorate %13 0 Offset 0
+ OpMemberDecorate %13 1 Offset 12
+ OpMemberDecorate %13 2 Offset 24
+ OpDecorate %13 BufferBlock
+ OpDecorate %15 DescriptorSet 0
+ OpDecorate %15 Binding 1
+ OpDecorate %18 ArrayStride 16
+ OpDecorate %19 ArrayStride 48
+ OpDecorate %21 ArrayStride 16
+ OpMemberDecorate %22 0 Offset 0
+ OpMemberDecorate %22 1 Offset 96
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpConstant %6 3
+ %8 = OpTypeArray %6 %7
+ %9 = OpTypeArray %6 %7
+ %10 = OpConstant %6 2
+ %11 = OpTypeArray %6 %10
+ %12 = OpTypeArray %11 %10
+ %13 = OpTypeStruct %8 %9 %12
+ %14 = OpTypePointer Uniform %13
+ %15 = OpVariable %14 Uniform
+ %16 = OpTypeInt 32 1
+ %17 = OpConstant %16 0
+ %18 = OpTypeArray %6 %7
+ %19 = OpTypeArray %18 %10
+ %20 = OpConstant %6 4
+ %21 = OpTypeArray %6 %20
+ %22 = OpTypeStruct %19 %21
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %6
+ %28 = OpConstant %6 1
+ %31 = OpConstant %16 1
+ %34 = OpConstant %6 0
+ %37 = OpConstant %16 2
+ %61 = OpConstant %16 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %26 = OpAccessChain %25 %24 %17 %17 %17
+ %27 = OpLoad %6 %26
+ %29 = OpIAdd %6 %27 %28
+ %30 = OpAccessChain %25 %15 %17 %17
+ OpStore %30 %29
+ %32 = OpAccessChain %25 %24 %17 %31 %17
+ %33 = OpLoad %6 %32
+ %35 = OpIAdd %6 %33 %34
+ %36 = OpAccessChain %25 %15 %17 %31
+ OpStore %36 %35
+ %38 = OpAccessChain %25 %24 %17 %31 %31
+ %39 = OpLoad %6 %38
+ %40 = OpIAdd %6 %39 %10
+ %41 = OpAccessChain %25 %15 %17 %37
+ OpStore %41 %40
+ %42 = OpAccessChain %25 %24 %17 %17 %37
+ %43 = OpLoad %6 %42
+ %44 = OpAccessChain %25 %15 %31 %17
+ OpStore %44 %43
+ %45 = OpAccessChain %25 %24 %17 %17 %31
+ %46 = OpLoad %6 %45
+ %47 = OpIMul %6 %46 %7
+ %48 = OpAccessChain %25 %15 %31 %31
+ OpStore %48 %47
+ %49 = OpAccessChain %25 %24 %17 %31 %37
+ %50 = OpLoad %6 %49
+ %51 = OpAccessChain %25 %15 %31 %37
+ OpStore %51 %50
+ %52 = OpAccessChain %25 %24 %31 %17
+ %53 = OpLoad %6 %52
+ %54 = OpAccessChain %25 %15 %37 %17 %17
+ OpStore %54 %53
+ %55 = OpAccessChain %25 %24 %31 %31
+ %56 = OpLoad %6 %55
+ %57 = OpAccessChain %25 %15 %37 %17 %31
+ OpStore %57 %56
+ %58 = OpAccessChain %25 %24 %31 %37
+ %59 = OpLoad %6 %58
+ %60 = OpAccessChain %25 %15 %37 %31 %17
+ OpStore %60 %59
+ %62 = OpAccessChain %25 %24 %31 %61
+ %63 = OpLoad %6 %62
+ %64 = OpAccessChain %25 %15 %37 %31 %31
+ OpStore %64 %63
+ OpReturn
+ OpFunctionEnd
+)";
+constexpr char kDst[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %13 "BufferOut"
+ OpMemberName %13 0 "o1"
+ OpMemberName %13 1 "o2"
+ OpMemberName %13 2 "o3"
+ OpName %15 ""
+ OpName %22 "BufferIn"
+ OpMemberName %22 0 "i1"
+ OpMemberName %22 1 "i2"
+ OpName %24 ""
+ OpDecorate %8 ArrayStride 4
+ OpDecorate %9 ArrayStride 4
+ OpDecorate %11 ArrayStride 4
+ OpDecorate %12 ArrayStride 8
+ OpMemberDecorate %13 0 Offset 0
+ OpMemberDecorate %13 1 Offset 12
+ OpMemberDecorate %13 2 Offset 24
+ OpDecorate %13 BufferBlock
+ OpDecorate %15 DescriptorSet 0
+ OpDecorate %15 Binding 1
+ OpDecorate %18 ArrayStride 16
+ OpDecorate %19 ArrayStride 48
+ OpDecorate %21 ArrayStride 16
+ OpMemberDecorate %22 0 Offset 0
+ OpMemberDecorate %22 1 Offset 96
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %16 = OpTypeInt 32 1
+ %7 = OpConstant %16 3
+ %8 = OpTypeArray %6 %7
+ %9 = OpTypeArray %6 %7
+ %10 = OpConstant %16 2
+ %11 = OpTypeArray %6 %10
+ %12 = OpTypeArray %11 %10
+ %13 = OpTypeStruct %8 %9 %12
+ %14 = OpTypePointer Uniform %13
+ %15 = OpVariable %14 Uniform
+ %18 = OpTypeArray %6 %7
+ %19 = OpTypeArray %18 %10
+ %20 = OpConstant %16 4
+ %21 = OpTypeArray %6 %20
+ %22 = OpTypeStruct %19 %21
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %6
+ %17 = OpConstant %16 0
+ %28 = OpConstant %16 1
+ %31 = OpConstant %6 1
+ %34 = OpConstant %6 0
+ %37 = OpConstant %6 2
+ %61 = OpConstant %6 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %26 = OpAccessChain %25 %24 %17 %17 %17
+ %27 = OpLoad %6 %26
+ %29 = OpIAdd %6 %27 %28
+ %30 = OpAccessChain %25 %15 %17 %17
+ OpStore %30 %29
+ %32 = OpAccessChain %25 %24 %17 %31 %17
+ %33 = OpLoad %6 %32
+ %35 = OpIAdd %6 %33 %34
+ %36 = OpAccessChain %25 %15 %17 %31
+ OpStore %36 %35
+ %38 = OpAccessChain %25 %24 %17 %31 %31
+ %39 = OpLoad %6 %38
+ %40 = OpIAdd %6 %39 %37
+ %41 = OpAccessChain %25 %15 %17 %10
+ OpStore %41 %40
+ %42 = OpAccessChain %25 %24 %17 %17 %10
+ %43 = OpLoad %6 %42
+ %44 = OpAccessChain %25 %15 %31 %17
+ OpStore %44 %43
+ %45 = OpAccessChain %25 %24 %17 %17 %31
+ %46 = OpLoad %6 %45
+ %47 = OpIMul %6 %46 %7
+ %48 = OpAccessChain %25 %15 %31 %31
+ OpStore %48 %47
+ %49 = OpAccessChain %25 %24 %17 %31 %10
+ %50 = OpLoad %6 %49
+ %51 = OpAccessChain %25 %15 %31 %10
+ OpStore %51 %50
+ %52 = OpAccessChain %25 %24 %31 %17
+ %53 = OpLoad %6 %52
+ %54 = OpAccessChain %25 %15 %37 %17 %17
+ OpStore %54 %53
+ %55 = OpAccessChain %25 %24 %31 %31
+ %56 = OpLoad %6 %55
+ %57 = OpAccessChain %25 %15 %37 %17 %31
+ OpStore %57 %56
+ %58 = OpAccessChain %25 %24 %31 %37
+ %59 = OpLoad %6 %58
+ %60 = OpAccessChain %25 %15 %37 %31 %17
+ OpStore %60 %59
+ %62 = OpAccessChain %25 %24 %31 %61
+ %63 = OpLoad %6 %62
+ %64 = OpAccessChain %25 %15 %37 %31 %31
+ OpStore %64 %63
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+TEST(DiffTest, IndexSignedness) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 65
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %13 "BufferOut"
+ OpMemberName %13 0 "o1"
+ OpMemberName %13 1 "o2"
+ OpMemberName %13 2 "o3"
+ OpName %15 ""
+ OpName %22 "BufferIn"
+ OpMemberName %22 0 "i1"
+ OpMemberName %22 1 "i2"
+ OpName %24 ""
+ OpDecorate %8 ArrayStride 4
+ OpDecorate %9 ArrayStride 4
+ OpDecorate %11 ArrayStride 4
+ OpDecorate %12 ArrayStride 8
+ OpMemberDecorate %13 0 Offset 0
+ OpMemberDecorate %13 1 Offset 12
+ OpMemberDecorate %13 2 Offset 24
+ OpDecorate %13 BufferBlock
+ OpDecorate %15 DescriptorSet 0
+ OpDecorate %15 Binding 1
+ OpDecorate %18 ArrayStride 16
+ OpDecorate %19 ArrayStride 48
+ OpDecorate %21 ArrayStride 16
+ OpMemberDecorate %22 0 Offset 0
+ OpMemberDecorate %22 1 Offset 96
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpConstant %6 3
+-%8 = OpTypeArray %6 %7
++%8 = OpTypeArray %6 %61
+-%9 = OpTypeArray %6 %7
++%9 = OpTypeArray %6 %61
+ %10 = OpConstant %6 2
+-%11 = OpTypeArray %6 %10
++%11 = OpTypeArray %6 %37
+-%12 = OpTypeArray %11 %10
++%12 = OpTypeArray %11 %37
+ %13 = OpTypeStruct %8 %9 %12
+ %14 = OpTypePointer Uniform %13
+ %15 = OpVariable %14 Uniform
+ %16 = OpTypeInt 32 1
+ %17 = OpConstant %16 0
+-%18 = OpTypeArray %6 %7
++%18 = OpTypeArray %6 %61
+-%19 = OpTypeArray %18 %10
++%19 = OpTypeArray %18 %37
+-%20 = OpConstant %6 4
++%20 = OpConstant %16 4
+ %21 = OpTypeArray %6 %20
+ %22 = OpTypeStruct %19 %21
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %6
+ %28 = OpConstant %6 1
+ %31 = OpConstant %16 1
+ %34 = OpConstant %6 0
+ %37 = OpConstant %16 2
+ %61 = OpConstant %16 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %26 = OpAccessChain %25 %24 %17 %17 %17
+ %27 = OpLoad %6 %26
+-%29 = OpIAdd %6 %27 %28
++%29 = OpIAdd %6 %27 %31
+ %30 = OpAccessChain %25 %15 %17 %17
+ OpStore %30 %29
+-%32 = OpAccessChain %25 %24 %17 %31 %17
++%32 = OpAccessChain %25 %24 %17 %28 %17
+ %33 = OpLoad %6 %32
+ %35 = OpIAdd %6 %33 %34
+-%36 = OpAccessChain %25 %15 %17 %31
++%36 = OpAccessChain %25 %15 %17 %28
+ OpStore %36 %35
+-%38 = OpAccessChain %25 %24 %17 %31 %31
++%38 = OpAccessChain %25 %24 %17 %28 %28
+ %39 = OpLoad %6 %38
+ %40 = OpIAdd %6 %39 %10
+ %41 = OpAccessChain %25 %15 %17 %37
+ OpStore %41 %40
+ %42 = OpAccessChain %25 %24 %17 %17 %37
+ %43 = OpLoad %6 %42
+-%44 = OpAccessChain %25 %15 %31 %17
++%44 = OpAccessChain %25 %15 %28 %17
+ OpStore %44 %43
+-%45 = OpAccessChain %25 %24 %17 %17 %31
++%45 = OpAccessChain %25 %24 %17 %17 %28
+ %46 = OpLoad %6 %45
+-%47 = OpIMul %6 %46 %7
++%47 = OpIMul %6 %46 %61
+-%48 = OpAccessChain %25 %15 %31 %31
++%48 = OpAccessChain %25 %15 %28 %28
+ OpStore %48 %47
+-%49 = OpAccessChain %25 %24 %17 %31 %37
++%49 = OpAccessChain %25 %24 %17 %28 %37
+ %50 = OpLoad %6 %49
+-%51 = OpAccessChain %25 %15 %31 %37
++%51 = OpAccessChain %25 %15 %28 %37
+ OpStore %51 %50
+-%52 = OpAccessChain %25 %24 %31 %17
++%52 = OpAccessChain %25 %24 %28 %17
+ %53 = OpLoad %6 %52
+-%54 = OpAccessChain %25 %15 %37 %17 %17
++%54 = OpAccessChain %25 %15 %10 %17 %17
+ OpStore %54 %53
+-%55 = OpAccessChain %25 %24 %31 %31
++%55 = OpAccessChain %25 %24 %28 %28
+ %56 = OpLoad %6 %55
+-%57 = OpAccessChain %25 %15 %37 %17 %31
++%57 = OpAccessChain %25 %15 %10 %17 %28
+ OpStore %57 %56
+-%58 = OpAccessChain %25 %24 %31 %37
++%58 = OpAccessChain %25 %24 %28 %10
+ %59 = OpLoad %6 %58
+-%60 = OpAccessChain %25 %15 %37 %31 %17
++%60 = OpAccessChain %25 %15 %10 %28 %17
+ OpStore %60 %59
+-%62 = OpAccessChain %25 %24 %31 %61
++%62 = OpAccessChain %25 %24 %28 %7
+ %63 = OpLoad %6 %62
+-%64 = OpAccessChain %25 %15 %37 %31 %31
++%64 = OpAccessChain %25 %15 %10 %28 %28
+ OpStore %64 %63
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, IndexSignednessNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpDecorate %8 ArrayStride 4
+ OpDecorate %9 ArrayStride 4
+ OpDecorate %11 ArrayStride 4
+ OpDecorate %12 ArrayStride 8
+ OpMemberDecorate %13 0 Offset 0
+ OpMemberDecorate %13 1 Offset 12
+ OpMemberDecorate %13 2 Offset 24
+ OpDecorate %13 BufferBlock
+ OpDecorate %15 DescriptorSet 0
+ OpDecorate %15 Binding 1
+ OpDecorate %18 ArrayStride 16
+ OpDecorate %19 ArrayStride 48
+ OpDecorate %21 ArrayStride 16
+ OpMemberDecorate %22 0 Offset 0
+ OpMemberDecorate %22 1 Offset 96
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpConstant %6 3
+ %8 = OpTypeArray %6 %7
+ %9 = OpTypeArray %6 %7
+ %10 = OpConstant %6 2
+ %11 = OpTypeArray %6 %10
+ %12 = OpTypeArray %11 %10
+ %13 = OpTypeStruct %8 %9 %12
+ %14 = OpTypePointer Uniform %13
+ %15 = OpVariable %14 Uniform
+ %16 = OpTypeInt 32 1
+ %17 = OpConstant %16 0
+ %18 = OpTypeArray %6 %7
+ %19 = OpTypeArray %18 %10
+ %20 = OpConstant %6 4
+ %21 = OpTypeArray %6 %20
+ %22 = OpTypeStruct %19 %21
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %6
+ %28 = OpConstant %6 1
+ %31 = OpConstant %16 1
+ %34 = OpConstant %6 0
+ %37 = OpConstant %16 2
+ %61 = OpConstant %16 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %26 = OpAccessChain %25 %24 %17 %17 %17
+ %27 = OpLoad %6 %26
+ %29 = OpIAdd %6 %27 %28
+ %30 = OpAccessChain %25 %15 %17 %17
+ OpStore %30 %29
+ %32 = OpAccessChain %25 %24 %17 %31 %17
+ %33 = OpLoad %6 %32
+ %35 = OpIAdd %6 %33 %34
+ %36 = OpAccessChain %25 %15 %17 %31
+ OpStore %36 %35
+ %38 = OpAccessChain %25 %24 %17 %31 %31
+ %39 = OpLoad %6 %38
+ %40 = OpIAdd %6 %39 %10
+ %41 = OpAccessChain %25 %15 %17 %37
+ OpStore %41 %40
+ %42 = OpAccessChain %25 %24 %17 %17 %37
+ %43 = OpLoad %6 %42
+ %44 = OpAccessChain %25 %15 %31 %17
+ OpStore %44 %43
+ %45 = OpAccessChain %25 %24 %17 %17 %31
+ %46 = OpLoad %6 %45
+ %47 = OpIMul %6 %46 %7
+ %48 = OpAccessChain %25 %15 %31 %31
+ OpStore %48 %47
+ %49 = OpAccessChain %25 %24 %17 %31 %37
+ %50 = OpLoad %6 %49
+ %51 = OpAccessChain %25 %15 %31 %37
+ OpStore %51 %50
+ %52 = OpAccessChain %25 %24 %31 %17
+ %53 = OpLoad %6 %52
+ %54 = OpAccessChain %25 %15 %37 %17 %17
+ OpStore %54 %53
+ %55 = OpAccessChain %25 %24 %31 %31
+ %56 = OpLoad %6 %55
+ %57 = OpAccessChain %25 %15 %37 %17 %31
+ OpStore %57 %56
+ %58 = OpAccessChain %25 %24 %31 %37
+ %59 = OpLoad %6 %58
+ %60 = OpAccessChain %25 %15 %37 %31 %17
+ OpStore %60 %59
+ %62 = OpAccessChain %25 %24 %31 %61
+ %63 = OpLoad %6 %62
+ %64 = OpAccessChain %25 %15 %37 %31 %31
+ OpStore %64 %63
+ OpReturn
+ OpFunctionEnd
+
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpDecorate %8 ArrayStride 4
+ OpDecorate %9 ArrayStride 4
+ OpDecorate %11 ArrayStride 4
+ OpDecorate %12 ArrayStride 8
+ OpMemberDecorate %13 0 Offset 0
+ OpMemberDecorate %13 1 Offset 12
+ OpMemberDecorate %13 2 Offset 24
+ OpDecorate %13 BufferBlock
+ OpDecorate %15 DescriptorSet 0
+ OpDecorate %15 Binding 1
+ OpDecorate %18 ArrayStride 16
+ OpDecorate %19 ArrayStride 48
+ OpDecorate %21 ArrayStride 16
+ OpMemberDecorate %22 0 Offset 0
+ OpMemberDecorate %22 1 Offset 96
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %16 = OpTypeInt 32 1
+ %7 = OpConstant %16 3
+ %8 = OpTypeArray %6 %7
+ %9 = OpTypeArray %6 %7
+ %10 = OpConstant %16 2
+ %11 = OpTypeArray %6 %10
+ %12 = OpTypeArray %11 %10
+ %13 = OpTypeStruct %8 %9 %12
+ %14 = OpTypePointer Uniform %13
+ %15 = OpVariable %14 Uniform
+ %18 = OpTypeArray %6 %7
+ %19 = OpTypeArray %18 %10
+ %20 = OpConstant %16 4
+ %21 = OpTypeArray %6 %20
+ %22 = OpTypeStruct %19 %21
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %6
+ %17 = OpConstant %16 0
+ %28 = OpConstant %16 1
+ %31 = OpConstant %6 1
+ %34 = OpConstant %6 0
+ %37 = OpConstant %6 2
+ %61 = OpConstant %6 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %26 = OpAccessChain %25 %24 %17 %17 %17
+ %27 = OpLoad %6 %26
+ %29 = OpIAdd %6 %27 %28
+ %30 = OpAccessChain %25 %15 %17 %17
+ OpStore %30 %29
+ %32 = OpAccessChain %25 %24 %17 %31 %17
+ %33 = OpLoad %6 %32
+ %35 = OpIAdd %6 %33 %34
+ %36 = OpAccessChain %25 %15 %17 %31
+ OpStore %36 %35
+ %38 = OpAccessChain %25 %24 %17 %31 %31
+ %39 = OpLoad %6 %38
+ %40 = OpIAdd %6 %39 %37
+ %41 = OpAccessChain %25 %15 %17 %10
+ OpStore %41 %40
+ %42 = OpAccessChain %25 %24 %17 %17 %10
+ %43 = OpLoad %6 %42
+ %44 = OpAccessChain %25 %15 %31 %17
+ OpStore %44 %43
+ %45 = OpAccessChain %25 %24 %17 %17 %31
+ %46 = OpLoad %6 %45
+ %47 = OpIMul %6 %46 %7
+ %48 = OpAccessChain %25 %15 %31 %31
+ OpStore %48 %47
+ %49 = OpAccessChain %25 %24 %17 %31 %10
+ %50 = OpLoad %6 %49
+ %51 = OpAccessChain %25 %15 %31 %10
+ OpStore %51 %50
+ %52 = OpAccessChain %25 %24 %31 %17
+ %53 = OpLoad %6 %52
+ %54 = OpAccessChain %25 %15 %37 %17 %17
+ OpStore %54 %53
+ %55 = OpAccessChain %25 %24 %31 %31
+ %56 = OpLoad %6 %55
+ %57 = OpAccessChain %25 %15 %37 %17 %31
+ OpStore %57 %56
+ %58 = OpAccessChain %25 %24 %31 %37
+ %59 = OpLoad %6 %58
+ %60 = OpAccessChain %25 %15 %37 %31 %17
+ OpStore %60 %59
+ %62 = OpAccessChain %25 %24 %31 %61
+ %63 = OpLoad %6 %62
+ %64 = OpAccessChain %25 %15 %37 %31 %31
+ OpStore %64 %63
+ OpReturn
+ OpFunctionEnd
+
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 65
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpDecorate %8 ArrayStride 4
+ OpDecorate %9 ArrayStride 4
+ OpDecorate %11 ArrayStride 4
+ OpDecorate %12 ArrayStride 8
+ OpMemberDecorate %13 0 Offset 0
+ OpMemberDecorate %13 1 Offset 12
+ OpMemberDecorate %13 2 Offset 24
+ OpDecorate %13 BufferBlock
+ OpDecorate %15 DescriptorSet 0
+ OpDecorate %15 Binding 1
+ OpDecorate %18 ArrayStride 16
+ OpDecorate %19 ArrayStride 48
+ OpDecorate %21 ArrayStride 16
+ OpMemberDecorate %22 0 Offset 0
+ OpMemberDecorate %22 1 Offset 96
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpConstant %6 3
+-%8 = OpTypeArray %6 %7
++%8 = OpTypeArray %6 %61
+-%9 = OpTypeArray %6 %7
++%9 = OpTypeArray %6 %61
+ %10 = OpConstant %6 2
+-%11 = OpTypeArray %6 %10
++%11 = OpTypeArray %6 %37
+-%12 = OpTypeArray %11 %10
++%12 = OpTypeArray %11 %37
+ %13 = OpTypeStruct %8 %9 %12
+ %14 = OpTypePointer Uniform %13
+ %15 = OpVariable %14 Uniform
+ %16 = OpTypeInt 32 1
+ %17 = OpConstant %16 0
+-%18 = OpTypeArray %6 %7
++%18 = OpTypeArray %6 %61
+-%19 = OpTypeArray %18 %10
++%19 = OpTypeArray %18 %37
+-%20 = OpConstant %6 4
++%20 = OpConstant %16 4
+ %21 = OpTypeArray %6 %20
+ %22 = OpTypeStruct %19 %21
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %6
+ %28 = OpConstant %6 1
+ %31 = OpConstant %16 1
+ %34 = OpConstant %6 0
+ %37 = OpConstant %16 2
+ %61 = OpConstant %16 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %26 = OpAccessChain %25 %24 %17 %17 %17
+ %27 = OpLoad %6 %26
+-%29 = OpIAdd %6 %27 %28
++%29 = OpIAdd %6 %27 %31
+ %30 = OpAccessChain %25 %15 %17 %17
+ OpStore %30 %29
+-%32 = OpAccessChain %25 %24 %17 %31 %17
++%32 = OpAccessChain %25 %24 %17 %28 %17
+ %33 = OpLoad %6 %32
+ %35 = OpIAdd %6 %33 %34
+-%36 = OpAccessChain %25 %15 %17 %31
++%36 = OpAccessChain %25 %15 %17 %28
+ OpStore %36 %35
+-%38 = OpAccessChain %25 %24 %17 %31 %31
++%38 = OpAccessChain %25 %24 %17 %28 %28
+ %39 = OpLoad %6 %38
+ %40 = OpIAdd %6 %39 %10
+ %41 = OpAccessChain %25 %15 %17 %37
+ OpStore %41 %40
+ %42 = OpAccessChain %25 %24 %17 %17 %37
+ %43 = OpLoad %6 %42
+-%44 = OpAccessChain %25 %15 %31 %17
++%44 = OpAccessChain %25 %15 %28 %17
+ OpStore %44 %43
+-%45 = OpAccessChain %25 %24 %17 %17 %31
++%45 = OpAccessChain %25 %24 %17 %17 %28
+ %46 = OpLoad %6 %45
+-%47 = OpIMul %6 %46 %7
++%47 = OpIMul %6 %46 %61
+-%48 = OpAccessChain %25 %15 %31 %31
++%48 = OpAccessChain %25 %15 %28 %28
+ OpStore %48 %47
+-%49 = OpAccessChain %25 %24 %17 %31 %37
++%49 = OpAccessChain %25 %24 %17 %28 %37
+ %50 = OpLoad %6 %49
+-%51 = OpAccessChain %25 %15 %31 %37
++%51 = OpAccessChain %25 %15 %28 %37
+ OpStore %51 %50
+-%52 = OpAccessChain %25 %24 %31 %17
++%52 = OpAccessChain %25 %24 %28 %17
+ %53 = OpLoad %6 %52
+-%54 = OpAccessChain %25 %15 %37 %17 %17
++%54 = OpAccessChain %25 %15 %10 %17 %17
+ OpStore %54 %53
+-%55 = OpAccessChain %25 %24 %31 %31
++%55 = OpAccessChain %25 %24 %28 %28
+ %56 = OpLoad %6 %55
+-%57 = OpAccessChain %25 %15 %37 %17 %31
++%57 = OpAccessChain %25 %15 %10 %17 %28
+ OpStore %57 %56
+-%58 = OpAccessChain %25 %24 %31 %37
++%58 = OpAccessChain %25 %24 %28 %10
+ %59 = OpLoad %6 %58
+-%60 = OpAccessChain %25 %15 %37 %31 %17
++%60 = OpAccessChain %25 %15 %10 %28 %17
+ OpStore %60 %59
+-%62 = OpAccessChain %25 %24 %31 %61
++%62 = OpAccessChain %25 %24 %28 %7
+ %63 = OpLoad %6 %62
+-%64 = OpAccessChain %25 %15 %37 %31 %31
++%64 = OpAccessChain %25 %15 %10 %28 %28
+ OpStore %64 %63
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/index_signedness_dst.spvasm b/test/diff/diff_files/index_signedness_dst.spvasm
new file mode 100644
index 00000000..08c1939b
--- /dev/null
+++ b/test/diff/diff_files/index_signedness_dst.spvasm
@@ -0,0 +1,110 @@
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %13 "BufferOut"
+ OpMemberName %13 0 "o1"
+ OpMemberName %13 1 "o2"
+ OpMemberName %13 2 "o3"
+ OpName %15 ""
+ OpName %22 "BufferIn"
+ OpMemberName %22 0 "i1"
+ OpMemberName %22 1 "i2"
+ OpName %24 ""
+ OpDecorate %8 ArrayStride 4
+ OpDecorate %9 ArrayStride 4
+ OpDecorate %11 ArrayStride 4
+ OpDecorate %12 ArrayStride 8
+ OpMemberDecorate %13 0 Offset 0
+ OpMemberDecorate %13 1 Offset 12
+ OpMemberDecorate %13 2 Offset 24
+ OpDecorate %13 BufferBlock
+ OpDecorate %15 DescriptorSet 0
+ OpDecorate %15 Binding 1
+ OpDecorate %18 ArrayStride 16
+ OpDecorate %19 ArrayStride 48
+ OpDecorate %21 ArrayStride 16
+ OpMemberDecorate %22 0 Offset 0
+ OpMemberDecorate %22 1 Offset 96
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %16 = OpTypeInt 32 1
+ %7 = OpConstant %16 3
+ %8 = OpTypeArray %6 %7
+ %9 = OpTypeArray %6 %7
+ %10 = OpConstant %16 2
+ %11 = OpTypeArray %6 %10
+ %12 = OpTypeArray %11 %10
+ %13 = OpTypeStruct %8 %9 %12
+ %14 = OpTypePointer Uniform %13
+ %15 = OpVariable %14 Uniform
+ %18 = OpTypeArray %6 %7
+ %19 = OpTypeArray %18 %10
+ %20 = OpConstant %16 4
+ %21 = OpTypeArray %6 %20
+ %22 = OpTypeStruct %19 %21
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %6
+ %17 = OpConstant %16 0
+ %28 = OpConstant %16 1
+ %31 = OpConstant %6 1
+ %34 = OpConstant %6 0
+ %37 = OpConstant %6 2
+ %61 = OpConstant %6 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %26 = OpAccessChain %25 %24 %17 %17 %17
+ %27 = OpLoad %6 %26
+ %29 = OpIAdd %6 %27 %28
+ %30 = OpAccessChain %25 %15 %17 %17
+ OpStore %30 %29
+ %32 = OpAccessChain %25 %24 %17 %31 %17
+ %33 = OpLoad %6 %32
+ %35 = OpIAdd %6 %33 %34
+ %36 = OpAccessChain %25 %15 %17 %31
+ OpStore %36 %35
+ %38 = OpAccessChain %25 %24 %17 %31 %31
+ %39 = OpLoad %6 %38
+ %40 = OpIAdd %6 %39 %37
+ %41 = OpAccessChain %25 %15 %17 %10
+ OpStore %41 %40
+ %42 = OpAccessChain %25 %24 %17 %17 %10
+ %43 = OpLoad %6 %42
+ %44 = OpAccessChain %25 %15 %31 %17
+ OpStore %44 %43
+ %45 = OpAccessChain %25 %24 %17 %17 %31
+ %46 = OpLoad %6 %45
+ %47 = OpIMul %6 %46 %7
+ %48 = OpAccessChain %25 %15 %31 %31
+ OpStore %48 %47
+ %49 = OpAccessChain %25 %24 %17 %31 %10
+ %50 = OpLoad %6 %49
+ %51 = OpAccessChain %25 %15 %31 %10
+ OpStore %51 %50
+ %52 = OpAccessChain %25 %24 %31 %17
+ %53 = OpLoad %6 %52
+ %54 = OpAccessChain %25 %15 %37 %17 %17
+ OpStore %54 %53
+ %55 = OpAccessChain %25 %24 %31 %31
+ %56 = OpLoad %6 %55
+ %57 = OpAccessChain %25 %15 %37 %17 %31
+ OpStore %57 %56
+ %58 = OpAccessChain %25 %24 %31 %37
+ %59 = OpLoad %6 %58
+ %60 = OpAccessChain %25 %15 %37 %31 %17
+ OpStore %60 %59
+ %62 = OpAccessChain %25 %24 %31 %61
+ %63 = OpLoad %6 %62
+ %64 = OpAccessChain %25 %15 %37 %31 %31
+ OpStore %64 %63
+ OpReturn
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/index_signedness_src.spvasm b/test/diff/diff_files/index_signedness_src.spvasm
new file mode 100644
index 00000000..06dfd18b
--- /dev/null
+++ b/test/diff/diff_files/index_signedness_src.spvasm
@@ -0,0 +1,111 @@
+;; Test where signedness of indices are different between src and dst.
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %13 "BufferOut"
+ OpMemberName %13 0 "o1"
+ OpMemberName %13 1 "o2"
+ OpMemberName %13 2 "o3"
+ OpName %15 ""
+ OpName %22 "BufferIn"
+ OpMemberName %22 0 "i1"
+ OpMemberName %22 1 "i2"
+ OpName %24 ""
+ OpDecorate %8 ArrayStride 4
+ OpDecorate %9 ArrayStride 4
+ OpDecorate %11 ArrayStride 4
+ OpDecorate %12 ArrayStride 8
+ OpMemberDecorate %13 0 Offset 0
+ OpMemberDecorate %13 1 Offset 12
+ OpMemberDecorate %13 2 Offset 24
+ OpDecorate %13 BufferBlock
+ OpDecorate %15 DescriptorSet 0
+ OpDecorate %15 Binding 1
+ OpDecorate %18 ArrayStride 16
+ OpDecorate %19 ArrayStride 48
+ OpDecorate %21 ArrayStride 16
+ OpMemberDecorate %22 0 Offset 0
+ OpMemberDecorate %22 1 Offset 96
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpConstant %6 3
+ %8 = OpTypeArray %6 %7
+ %9 = OpTypeArray %6 %7
+ %10 = OpConstant %6 2
+ %11 = OpTypeArray %6 %10
+ %12 = OpTypeArray %11 %10
+ %13 = OpTypeStruct %8 %9 %12
+ %14 = OpTypePointer Uniform %13
+ %15 = OpVariable %14 Uniform
+ %16 = OpTypeInt 32 1
+ %17 = OpConstant %16 0
+ %18 = OpTypeArray %6 %7
+ %19 = OpTypeArray %18 %10
+ %20 = OpConstant %6 4
+ %21 = OpTypeArray %6 %20
+ %22 = OpTypeStruct %19 %21
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %6
+ %28 = OpConstant %6 1
+ %31 = OpConstant %16 1
+ %34 = OpConstant %6 0
+ %37 = OpConstant %16 2
+ %61 = OpConstant %16 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %26 = OpAccessChain %25 %24 %17 %17 %17
+ %27 = OpLoad %6 %26
+ %29 = OpIAdd %6 %27 %28
+ %30 = OpAccessChain %25 %15 %17 %17
+ OpStore %30 %29
+ %32 = OpAccessChain %25 %24 %17 %31 %17
+ %33 = OpLoad %6 %32
+ %35 = OpIAdd %6 %33 %34
+ %36 = OpAccessChain %25 %15 %17 %31
+ OpStore %36 %35
+ %38 = OpAccessChain %25 %24 %17 %31 %31
+ %39 = OpLoad %6 %38
+ %40 = OpIAdd %6 %39 %10
+ %41 = OpAccessChain %25 %15 %17 %37
+ OpStore %41 %40
+ %42 = OpAccessChain %25 %24 %17 %17 %37
+ %43 = OpLoad %6 %42
+ %44 = OpAccessChain %25 %15 %31 %17
+ OpStore %44 %43
+ %45 = OpAccessChain %25 %24 %17 %17 %31
+ %46 = OpLoad %6 %45
+ %47 = OpIMul %6 %46 %7
+ %48 = OpAccessChain %25 %15 %31 %31
+ OpStore %48 %47
+ %49 = OpAccessChain %25 %24 %17 %31 %37
+ %50 = OpLoad %6 %49
+ %51 = OpAccessChain %25 %15 %31 %37
+ OpStore %51 %50
+ %52 = OpAccessChain %25 %24 %31 %17
+ %53 = OpLoad %6 %52
+ %54 = OpAccessChain %25 %15 %37 %17 %17
+ OpStore %54 %53
+ %55 = OpAccessChain %25 %24 %31 %31
+ %56 = OpLoad %6 %55
+ %57 = OpAccessChain %25 %15 %37 %17 %31
+ OpStore %57 %56
+ %58 = OpAccessChain %25 %24 %31 %37
+ %59 = OpLoad %6 %58
+ %60 = OpAccessChain %25 %15 %37 %31 %17
+ OpStore %60 %59
+ %62 = OpAccessChain %25 %24 %31 %61
+ %63 = OpLoad %6 %62
+ %64 = OpAccessChain %25 %15 %37 %31 %31
+ OpStore %64 %63
+ OpReturn
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/int_vs_uint_constants_autogen.cpp b/test/diff/diff_files/int_vs_uint_constants_autogen.cpp
new file mode 100644
index 00000000..187722e8
--- /dev/null
+++ b/test/diff/diff_files/int_vs_uint_constants_autogen.cpp
@@ -0,0 +1,396 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests that identical integer constants are matched, regardless of int or
+// uint. This helps compare output from different generators that default to
+// int or uint for constants such as those passed to OpAccessChain.
+constexpr char kSrc[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %22 "main" %4 %19
+OpSource GLSL 450
+OpName %4 "_ua_position"
+OpName %17 "gl_PerVertex"
+OpMemberName %17 0 "gl_Position"
+OpMemberName %17 1 "gl_PointSize"
+OpMemberName %17 2 "gl_ClipDistance"
+OpMemberName %17 3 "gl_CullDistance"
+OpName %19 ""
+OpName %22 "main"
+OpDecorate %4 Location 0
+OpMemberDecorate %17 1 RelaxedPrecision
+OpMemberDecorate %17 0 BuiltIn Position
+OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%8 = OpTypeVector %5 4
+%15 = OpConstant %5 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %5 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd)";
+constexpr char kDst[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 10
+; Bound: 28
+; Schema: 0
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %4 "main" %13 %17
+OpSource GLSL 450
+OpName %4 "main"
+OpName %11 "gl_PerVertex"
+OpMemberName %11 0 "gl_Position"
+OpMemberName %11 1 "gl_PointSize"
+OpMemberName %11 2 "gl_ClipDistance"
+OpMemberName %11 3 "gl_CullDistance"
+OpName %13 ""
+OpName %17 "_ua_position"
+OpMemberDecorate %11 0 BuiltIn Position
+OpMemberDecorate %11 1 BuiltIn PointSize
+OpMemberDecorate %11 2 BuiltIn ClipDistance
+OpMemberDecorate %11 3 BuiltIn CullDistance
+OpDecorate %11 Block
+OpDecorate %17 Location 0
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%7 = OpTypeVector %6 4
+%8 = OpTypeInt 32 0
+%9 = OpConstant %8 1
+%10 = OpTypeArray %6 %9
+%11 = OpTypeStruct %7 %6 %10 %10
+%12 = OpTypePointer Output %11
+%13 = OpVariable %12 Output
+%14 = OpTypeInt 32 1
+%15 = OpConstant %14 0
+%16 = OpTypePointer Input %7
+%17 = OpVariable %16 Input
+%19 = OpTypePointer Output %7
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%18 = OpLoad %7 %17
+%20 = OpAccessChain %19 %13 %15
+OpStore %20 %18
+OpReturn
+OpFunctionEnd
+)";
+
+TEST(DiffTest, IntVsUintConstants) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 27
++; Bound: 31
+ ; Schema: 0
+ OpCapability Shader
++%27 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+-OpEntryPoint Vertex %22 "main" %4 %19
++OpEntryPoint Vertex %22 "main" %19 %4
+ OpSource GLSL 450
+ OpName %4 "_ua_position"
+ OpName %17 "gl_PerVertex"
+ OpMemberName %17 0 "gl_Position"
+ OpMemberName %17 1 "gl_PointSize"
+ OpMemberName %17 2 "gl_ClipDistance"
+ OpMemberName %17 3 "gl_CullDistance"
+ OpName %19 ""
+ OpName %22 "main"
+ OpDecorate %4 Location 0
+-OpMemberDecorate %17 1 RelaxedPrecision
+ OpMemberDecorate %17 0 BuiltIn Position
+ OpMemberDecorate %17 1 BuiltIn PointSize
+ OpMemberDecorate %17 2 BuiltIn ClipDistance
+ OpMemberDecorate %17 3 BuiltIn CullDistance
+ OpDecorate %17 Block
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %5 = OpTypeInt 32 0
+-%8 = OpTypeVector %5 4
+-%15 = OpConstant %5 8
+-%16 = OpTypeArray %1 %15
+-%17 = OpTypeStruct %2 %1 %16 %16
++%17 = OpTypeStruct %2 %1 %29 %29
+ %20 = OpTypeVoid
++%28 = OpConstant %5 1
++%29 = OpTypeArray %1 %28
+-%25 = OpConstant %5 0
++%25 = OpConstant %30 0
+ %3 = OpTypePointer Input %2
+ %13 = OpTypePointer Output %2
++%30 = OpTypeInt 32 1
+ %18 = OpTypePointer Output %17
+ %21 = OpTypeFunction %20
+ %4 = OpVariable %3 Input
+ %19 = OpVariable %18 Output
+ %22 = OpFunction %20 None %21
+ %23 = OpLabel
+ %24 = OpLoad %2 %4
+ %26 = OpAccessChain %13 %19 %25
+ OpStore %26 %24
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, IntVsUintConstantsNoDebug) {
+ constexpr char kSrcNoDebug[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %22 "main" %4 %19
+OpSource GLSL 450
+OpDecorate %4 Location 0
+OpMemberDecorate %17 1 RelaxedPrecision
+OpMemberDecorate %17 0 BuiltIn Position
+OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%8 = OpTypeVector %5 4
+%15 = OpConstant %5 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %5 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd
+)";
+ constexpr char kDstNoDebug[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 10
+; Bound: 28
+; Schema: 0
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %4 "main" %13 %17
+OpSource GLSL 450
+OpMemberDecorate %11 0 BuiltIn Position
+OpMemberDecorate %11 1 BuiltIn PointSize
+OpMemberDecorate %11 2 BuiltIn ClipDistance
+OpMemberDecorate %11 3 BuiltIn CullDistance
+OpDecorate %11 Block
+OpDecorate %17 Location 0
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%7 = OpTypeVector %6 4
+%8 = OpTypeInt 32 0
+%9 = OpConstant %8 1
+%10 = OpTypeArray %6 %9
+%11 = OpTypeStruct %7 %6 %10 %10
+%12 = OpTypePointer Output %11
+%13 = OpVariable %12 Output
+%14 = OpTypeInt 32 1
+%15 = OpConstant %14 0
+%16 = OpTypePointer Input %7
+%17 = OpVariable %16 Input
+%19 = OpTypePointer Output %7
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%18 = OpLoad %7 %17
+%20 = OpAccessChain %19 %13 %15
+OpStore %20 %18
+OpReturn
+OpFunctionEnd
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 27
++; Bound: 31
+ ; Schema: 0
+ OpCapability Shader
++%27 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+-OpEntryPoint Vertex %22 "main" %4 %19
++OpEntryPoint Vertex %22 "main" %19 %4
+ OpSource GLSL 450
+ OpDecorate %4 Location 0
+-OpMemberDecorate %17 1 RelaxedPrecision
+ OpMemberDecorate %17 0 BuiltIn Position
+ OpMemberDecorate %17 1 BuiltIn PointSize
+ OpMemberDecorate %17 2 BuiltIn ClipDistance
+ OpMemberDecorate %17 3 BuiltIn CullDistance
+ OpDecorate %17 Block
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %5 = OpTypeInt 32 0
+-%8 = OpTypeVector %5 4
+-%15 = OpConstant %5 8
+-%16 = OpTypeArray %1 %15
+-%17 = OpTypeStruct %2 %1 %16 %16
++%17 = OpTypeStruct %2 %1 %29 %29
+ %20 = OpTypeVoid
++%28 = OpConstant %5 1
++%29 = OpTypeArray %1 %28
+-%25 = OpConstant %5 0
++%25 = OpConstant %30 0
+ %3 = OpTypePointer Input %2
+ %13 = OpTypePointer Output %2
++%30 = OpTypeInt 32 1
+ %18 = OpTypePointer Output %17
+ %21 = OpTypeFunction %20
+ %4 = OpVariable %3 Input
+ %19 = OpVariable %18 Output
+ %22 = OpFunction %20 None %21
+ %23 = OpLabel
+ %24 = OpLoad %2 %4
+ %26 = OpAccessChain %13 %19 %25
+ OpStore %26 %24
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+TEST(DiffTest, IntVsUintConstantsDumpIds) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 27
++; Bound: 31
+ ; Schema: 0
+ OpCapability Shader
++%27 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+-OpEntryPoint Vertex %22 "main" %4 %19
++OpEntryPoint Vertex %22 "main" %19 %4
+ OpSource GLSL 450
+ OpName %4 "_ua_position"
+ OpName %17 "gl_PerVertex"
+ OpMemberName %17 0 "gl_Position"
+ OpMemberName %17 1 "gl_PointSize"
+ OpMemberName %17 2 "gl_ClipDistance"
+ OpMemberName %17 3 "gl_CullDistance"
+ OpName %19 ""
+ OpName %22 "main"
+ OpDecorate %4 Location 0
+-OpMemberDecorate %17 1 RelaxedPrecision
+ OpMemberDecorate %17 0 BuiltIn Position
+ OpMemberDecorate %17 1 BuiltIn PointSize
+ OpMemberDecorate %17 2 BuiltIn ClipDistance
+ OpMemberDecorate %17 3 BuiltIn CullDistance
+ OpDecorate %17 Block
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %5 = OpTypeInt 32 0
+-%8 = OpTypeVector %5 4
+-%15 = OpConstant %5 8
+-%16 = OpTypeArray %1 %15
+-%17 = OpTypeStruct %2 %1 %16 %16
++%17 = OpTypeStruct %2 %1 %29 %29
+ %20 = OpTypeVoid
++%28 = OpConstant %5 1
++%29 = OpTypeArray %1 %28
+-%25 = OpConstant %5 0
++%25 = OpConstant %30 0
+ %3 = OpTypePointer Input %2
+ %13 = OpTypePointer Output %2
++%30 = OpTypeInt 32 1
+ %18 = OpTypePointer Output %17
+ %21 = OpTypeFunction %20
+ %4 = OpVariable %3 Input
+ %19 = OpVariable %18 Output
+ %22 = OpFunction %20 None %21
+ %23 = OpLabel
+ %24 = OpLoad %2 %4
+ %26 = OpAccessChain %13 %19 %25
+ OpStore %26 %24
+ OpReturn
+ OpFunctionEnd
+ Src -> Dst
+ 1 -> 6 [TypeFloat]
+ 2 -> 7 [TypeVector]
+ 3 -> 16 [TypePointer]
+ 4 -> 17 [Variable]
+ 5 -> 8 [TypeInt]
+ 8 -> 23 [TypeVector]
+ 13 -> 19 [TypePointer]
+ 15 -> 29 [Constant]
+ 16 -> 30 [TypeArray]
+ 17 -> 11 [TypeStruct]
+ 18 -> 12 [TypePointer]
+ 19 -> 13 [Variable]
+ 20 -> 2 [TypeVoid]
+ 21 -> 3 [TypeFunction]
+ 22 -> 4 [Function]
+ 23 -> 5 [Label]
+ 24 -> 18 [Load]
+ 25 -> 15 [Constant]
+ 26 -> 20 [AccessChain]
+)";
+ Options options;
+ options.dump_id_map = true;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/int_vs_uint_constants_dst.spvasm b/test/diff/diff_files/int_vs_uint_constants_dst.spvasm
new file mode 100644
index 00000000..da2618b2
--- /dev/null
+++ b/test/diff/diff_files/int_vs_uint_constants_dst.spvasm
@@ -0,0 +1,46 @@
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 10
+; Bound: 28
+; Schema: 0
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %4 "main" %13 %17
+OpSource GLSL 450
+OpName %4 "main"
+OpName %11 "gl_PerVertex"
+OpMemberName %11 0 "gl_Position"
+OpMemberName %11 1 "gl_PointSize"
+OpMemberName %11 2 "gl_ClipDistance"
+OpMemberName %11 3 "gl_CullDistance"
+OpName %13 ""
+OpName %17 "_ua_position"
+OpMemberDecorate %11 0 BuiltIn Position
+OpMemberDecorate %11 1 BuiltIn PointSize
+OpMemberDecorate %11 2 BuiltIn ClipDistance
+OpMemberDecorate %11 3 BuiltIn CullDistance
+OpDecorate %11 Block
+OpDecorate %17 Location 0
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%7 = OpTypeVector %6 4
+%8 = OpTypeInt 32 0
+%9 = OpConstant %8 1
+%10 = OpTypeArray %6 %9
+%11 = OpTypeStruct %7 %6 %10 %10
+%12 = OpTypePointer Output %11
+%13 = OpVariable %12 Output
+%14 = OpTypeInt 32 1
+%15 = OpConstant %14 0
+%16 = OpTypePointer Input %7
+%17 = OpVariable %16 Input
+%19 = OpTypePointer Output %7
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%18 = OpLoad %7 %17
+%20 = OpAccessChain %19 %13 %15
+OpStore %20 %18
+OpReturn
+OpFunctionEnd
diff --git a/test/diff/diff_files/int_vs_uint_constants_src.spvasm b/test/diff/diff_files/int_vs_uint_constants_src.spvasm
new file mode 100644
index 00000000..214b49bd
--- /dev/null
+++ b/test/diff/diff_files/int_vs_uint_constants_src.spvasm
@@ -0,0 +1,49 @@
+;; Tests that identical integer constants are matched, regardless of int or
+;; uint. This helps compare output from different generators that default to
+;; int or uint for constants such as those passed to OpAccessChain.
+; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %22 "main" %4 %19
+OpSource GLSL 450
+OpName %4 "_ua_position"
+OpName %17 "gl_PerVertex"
+OpMemberName %17 0 "gl_Position"
+OpMemberName %17 1 "gl_PointSize"
+OpMemberName %17 2 "gl_ClipDistance"
+OpMemberName %17 3 "gl_CullDistance"
+OpName %19 ""
+OpName %22 "main"
+OpDecorate %4 Location 0
+OpMemberDecorate %17 1 RelaxedPrecision
+OpMemberDecorate %17 0 BuiltIn Position
+OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%8 = OpTypeVector %5 4
+%15 = OpConstant %5 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %5 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd
diff --git a/test/diff/diff_files/large_functions_large_diffs_autogen.cpp b/test/diff/diff_files/large_functions_large_diffs_autogen.cpp
new file mode 100644
index 00000000..12cb6219
--- /dev/null
+++ b/test/diff/diff_files/large_functions_large_diffs_autogen.cpp
@@ -0,0 +1,1534 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Test where src and dst have a few large functions with large differences.
+constexpr char kSrc[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main" %15
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "f1("
+ OpName %8 "f2("
+ OpName %12 "x"
+ OpName %15 "gl_LocalInvocationID"
+ OpName %20 "y"
+ OpName %27 "image"
+ OpName %44 "sum"
+ OpName %46 "i"
+ OpName %56 "j"
+ OpName %80 "BufferOut"
+ OpMemberName %80 0 "o_uv4"
+ OpMemberName %80 1 "o_v3"
+ OpMemberName %80 2 "o_i"
+ OpName %82 ""
+ OpName %88 "BufferIn"
+ OpMemberName %88 0 "i_u"
+ OpMemberName %88 1 "i_v4"
+ OpMemberName %88 2 "i_f"
+ OpName %90 ""
+ OpName %101 "i"
+ OpName %128 "image2"
+ OpDecorate %15 BuiltIn LocalInvocationId
+ OpDecorate %27 DescriptorSet 0
+ OpDecorate %27 Binding 2
+ OpMemberDecorate %80 0 Offset 0
+ OpMemberDecorate %80 1 Offset 16
+ OpMemberDecorate %80 2 Offset 28
+ OpDecorate %80 BufferBlock
+ OpDecorate %82 DescriptorSet 0
+ OpDecorate %82 Binding 1
+ OpMemberDecorate %88 0 Offset 0
+ OpMemberDecorate %88 1 RowMajor
+ OpMemberDecorate %88 1 Offset 16
+ OpMemberDecorate %88 1 MatrixStride 16
+ OpMemberDecorate %88 2 Offset 80
+ OpDecorate %88 Block
+ OpDecorate %90 DescriptorSet 0
+ OpDecorate %90 Binding 0
+ OpDecorate %128 DescriptorSet 0
+ OpDecorate %128 Binding 3
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpTypePointer Function %10
+ %13 = OpTypeVector %10 3
+ %14 = OpTypePointer Input %13
+ %15 = OpVariable %14 Input
+ %16 = OpConstant %10 0
+ %17 = OpTypePointer Input %10
+ %21 = OpConstant %10 1
+ %24 = OpTypeInt 32 1
+ %25 = OpTypeImage %24 2D 0 0 0 2 R32i
+ %26 = OpTypePointer UniformConstant %25
+ %27 = OpVariable %26 UniformConstant
+ %29 = OpTypeVector %10 2
+ %32 = OpTypeVector %24 2
+ %38 = OpTypeVector %24 4
+ %40 = OpConstant %10 2
+ %41 = OpConstant %10 3400
+ %42 = OpConstant %10 264
+ %43 = OpTypePointer Function %24
+ %45 = OpConstant %24 0
+ %53 = OpConstant %24 2
+ %54 = OpTypeBool
+ %73 = OpConstant %24 1
+ %77 = OpTypeVector %10 4
+ %78 = OpTypeFloat 32
+ %79 = OpTypeVector %78 3
+ %80 = OpTypeStruct %77 %79 %24
+ %81 = OpTypePointer Uniform %80
+ %82 = OpVariable %81 Uniform
+ %84 = OpTypePointer Uniform %24
+ %86 = OpTypeVector %78 4
+ %87 = OpTypeMatrix %86 4
+ %88 = OpTypeStruct %10 %87 %78
+ %89 = OpTypePointer Uniform %88
+ %90 = OpVariable %89 Uniform
+ %91 = OpTypePointer Uniform %87
+ %94 = OpTypePointer Uniform %77
+ %108 = OpConstant %24 3
+ %110 = OpTypePointer Uniform %79
+ %113 = OpTypePointer Uniform %78
+ %128 = OpVariable %26 UniformConstant
+ %130 = OpConstantComposite %32 %45 %45
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %136 = OpFunctionCall %2 %6
+ %137 = OpFunctionCall %2 %8
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %12 = OpVariable %11 Function
+ %20 = OpVariable %11 Function
+ %44 = OpVariable %43 Function
+ %46 = OpVariable %43 Function
+ %56 = OpVariable %43 Function
+ %18 = OpAccessChain %17 %15 %16
+ %19 = OpLoad %10 %18
+ OpStore %12 %19
+ %22 = OpAccessChain %17 %15 %21
+ %23 = OpLoad %10 %22
+ OpStore %20 %23
+ %28 = OpLoad %25 %27
+ %30 = OpLoad %13 %15
+ %31 = OpVectorShuffle %29 %30 %30 0 1
+ %33 = OpBitcast %32 %31
+ %34 = OpLoad %10 %12
+ %35 = OpLoad %10 %20
+ %36 = OpIAdd %10 %34 %35
+ %37 = OpBitcast %24 %36
+ %39 = OpCompositeConstruct %38 %37 %37 %37 %37
+ OpImageWrite %28 %33 %39
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ OpStore %44 %45
+ OpStore %46 %45
+ OpBranch %47
+ %47 = OpLabel
+ OpLoopMerge %49 %50 None
+ OpBranch %51
+ %51 = OpLabel
+ %52 = OpLoad %24 %46
+ %55 = OpSLessThan %54 %52 %53
+ OpBranchConditional %55 %48 %49
+ %48 = OpLabel
+ OpStore %56 %45
+ OpBranch %57
+ %57 = OpLabel
+ OpLoopMerge %59 %60 None
+ OpBranch %61
+ %61 = OpLabel
+ %62 = OpLoad %24 %56
+ %63 = OpSLessThan %54 %62 %53
+ OpBranchConditional %63 %58 %59
+ %58 = OpLabel
+ %64 = OpLoad %25 %27
+ %65 = OpLoad %24 %46
+ %66 = OpLoad %24 %56
+ %67 = OpCompositeConstruct %32 %65 %66
+ %68 = OpImageRead %38 %64 %67
+ %69 = OpCompositeExtract %24 %68 0
+ %70 = OpLoad %24 %44
+ %71 = OpIMul %24 %70 %69
+ OpStore %44 %71
+ OpBranch %60
+ %60 = OpLabel
+ %72 = OpLoad %24 %56
+ %74 = OpIAdd %24 %72 %73
+ OpStore %56 %74
+ OpBranch %57
+ %59 = OpLabel
+ OpBranch %50
+ %50 = OpLabel
+ %75 = OpLoad %24 %46
+ %76 = OpIAdd %24 %75 %73
+ OpStore %46 %76
+ OpBranch %47
+ %49 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %83 = OpLoad %24 %44
+ %85 = OpAccessChain %84 %82 %53
+ OpStore %85 %83
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %101 = OpVariable %43 Function
+ %92 = OpAccessChain %91 %90 %73
+ %93 = OpLoad %87 %92
+ %95 = OpAccessChain %94 %82 %45
+ %96 = OpLoad %77 %95
+ %97 = OpConvertUToF %86 %96
+ %98 = OpMatrixTimesVector %86 %93 %97
+ %99 = OpConvertFToU %77 %98
+ %100 = OpAccessChain %94 %82 %45
+ OpStore %100 %99
+ OpStore %101 %45
+ OpBranch %102
+ %102 = OpLabel
+ OpLoopMerge %104 %105 None
+ OpBranch %106
+ %106 = OpLabel
+ %107 = OpLoad %24 %101
+ %109 = OpSLessThan %54 %107 %108
+ OpBranchConditional %109 %103 %104
+ %103 = OpLabel
+ %111 = OpAccessChain %110 %82 %73
+ %112 = OpLoad %79 %111
+ %114 = OpAccessChain %113 %90 %53
+ %115 = OpLoad %78 %114
+ %116 = OpVectorTimesScalar %79 %112 %115
+ %117 = OpConvertFToU %13 %116
+ %118 = OpCompositeExtract %10 %117 0
+ %119 = OpCompositeExtract %10 %117 1
+ %120 = OpCompositeExtract %10 %117 2
+ %121 = OpCompositeConstruct %77 %118 %119 %120 %16
+ %122 = OpAccessChain %94 %82 %45
+ %123 = OpLoad %77 %122
+ %124 = OpIAdd %77 %123 %121
+ %125 = OpAccessChain %94 %82 %45
+ OpStore %125 %124
+ OpBranch %105
+ %105 = OpLabel
+ %126 = OpLoad %24 %101
+ %127 = OpIAdd %24 %126 %73
+ OpStore %101 %127
+ OpBranch %102
+ %104 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %129 = OpLoad %25 %128
+ %131 = OpImageRead %38 %129 %130
+ %132 = OpCompositeExtract %24 %131 0
+ %133 = OpConvertSToF %78 %132
+ %134 = OpCompositeConstruct %79 %133 %133 %133
+ %135 = OpAccessChain %110 %82 %73
+ OpStore %135 %134
+ OpReturn
+ OpFunctionEnd
+)";
+constexpr char kDst[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main" %15 %110
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "f1("
+ OpName %8 "f2("
+ OpName %12 "x"
+ OpName %15 "gl_GlobalInvocationID"
+ OpName %20 "z"
+ OpName %26 "i"
+ OpName %40 "BufferOut"
+ OpMemberName %40 0 "o_uv4"
+ OpMemberName %40 1 "o_v3"
+ OpMemberName %40 2 "o_i"
+ OpName %42 ""
+ OpName %63 "image2"
+ OpName %79 "image"
+ OpName %89 "i"
+ OpName %110 "gl_LocalInvocationID"
+ OpName %127 "BufferIn"
+ OpMemberName %127 0 "i_u"
+ OpMemberName %127 1 "i_v4"
+ OpMemberName %127 2 "i_f"
+ OpName %129 ""
+ OpDecorate %15 BuiltIn GlobalInvocationId
+ OpMemberDecorate %40 0 Offset 0
+ OpMemberDecorate %40 1 Offset 16
+ OpMemberDecorate %40 2 Offset 28
+ OpDecorate %40 BufferBlock
+ OpDecorate %42 DescriptorSet 0
+ OpDecorate %42 Binding 1
+ OpDecorate %63 DescriptorSet 0
+ OpDecorate %63 Binding 3
+ OpDecorate %79 DescriptorSet 0
+ OpDecorate %79 Binding 2
+ OpDecorate %110 BuiltIn LocalInvocationId
+ OpMemberDecorate %127 0 Offset 0
+ OpMemberDecorate %127 1 RowMajor
+ OpMemberDecorate %127 1 Offset 16
+ OpMemberDecorate %127 1 MatrixStride 16
+ OpMemberDecorate %127 2 Offset 80
+ OpDecorate %127 Block
+ OpDecorate %129 DescriptorSet 0
+ OpDecorate %129 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpTypePointer Function %10
+ %13 = OpTypeVector %10 3
+ %14 = OpTypePointer Input %13
+ %15 = OpVariable %14 Input
+ %16 = OpConstant %10 0
+ %17 = OpTypePointer Input %10
+ %21 = OpConstant %10 1
+ %24 = OpTypeInt 32 1
+ %25 = OpTypePointer Function %24
+ %27 = OpConstant %24 0
+ %34 = OpConstant %24 2
+ %35 = OpTypeBool
+ %37 = OpTypeVector %10 4
+ %38 = OpTypeFloat 32
+ %39 = OpTypeVector %38 3
+ %40 = OpTypeStruct %37 %39 %24
+ %41 = OpTypePointer Uniform %40
+ %42 = OpVariable %41 Uniform
+ %46 = OpTypeVector %10 2
+ %48 = OpTypePointer Uniform %37
+ %53 = OpTypePointer Uniform %10
+ %59 = OpConstant %24 1
+ %61 = OpTypeImage %24 2D 0 0 0 2 R32i
+ %62 = OpTypePointer UniformConstant %61
+ %63 = OpVariable %62 UniformConstant
+ %69 = OpTypeVector %24 2
+ %71 = OpTypeVector %24 4
+ %74 = OpTypePointer Uniform %24
+ %76 = OpConstant %10 2
+ %77 = OpConstant %10 3400
+ %78 = OpConstant %10 264
+ %79 = OpVariable %62 UniformConstant
+ %96 = OpConstant %24 3
+ %103 = OpConstantComposite %69 %27 %27
+ %107 = OpTypePointer Uniform %38
+ %110 = OpVariable %14 Input
+ %113 = OpTypeVector %38 2
+ %125 = OpTypeVector %38 4
+ %126 = OpTypeMatrix %125 4
+ %127 = OpTypeStruct %10 %126 %38
+ %128 = OpTypePointer Uniform %127
+ %129 = OpVariable %128 Uniform
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %123 = OpFunctionCall %2 %8
+ %124 = OpFunctionCall %2 %6
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %12 = OpVariable %11 Function
+ %20 = OpVariable %11 Function
+ %26 = OpVariable %25 Function
+ %18 = OpAccessChain %17 %15 %16
+ %19 = OpLoad %10 %18
+ OpStore %12 %19
+ %22 = OpAccessChain %17 %15 %21
+ %23 = OpLoad %10 %22
+ OpStore %20 %23
+ OpStore %26 %27
+ OpBranch %28
+ %28 = OpLabel
+ OpLoopMerge %30 %31 None
+ OpBranch %32
+ %32 = OpLabel
+ %33 = OpLoad %24 %26
+ %36 = OpSLessThan %35 %33 %34
+ OpBranchConditional %36 %29 %30
+ %29 = OpLabel
+ %43 = OpLoad %10 %12
+ %44 = OpLoad %10 %20
+ %45 = OpIAdd %10 %43 %44
+ %47 = OpCompositeConstruct %46 %45 %45
+ %49 = OpAccessChain %48 %42 %27
+ %50 = OpLoad %37 %49
+ %51 = OpVectorShuffle %46 %50 %50 0 1
+ %52 = OpIAdd %46 %51 %47
+ %54 = OpAccessChain %53 %42 %27 %16
+ %55 = OpCompositeExtract %10 %52 0
+ OpStore %54 %55
+ %56 = OpAccessChain %53 %42 %27 %21
+ %57 = OpCompositeExtract %10 %52 1
+ OpStore %56 %57
+ OpBranch %31
+ %31 = OpLabel
+ %58 = OpLoad %24 %26
+ %60 = OpIAdd %24 %58 %59
+ OpStore %26 %60
+ OpBranch %28
+ %30 = OpLabel
+ %64 = OpLoad %61 %63
+ %65 = OpLoad %10 %12
+ %66 = OpBitcast %24 %65
+ %67 = OpLoad %10 %20
+ %68 = OpBitcast %24 %67
+ %70 = OpCompositeConstruct %69 %66 %68
+ %72 = OpImageRead %71 %64 %70
+ %73 = OpCompositeExtract %24 %72 1
+ %75 = OpAccessChain %74 %42 %34
+ OpStore %75 %73
+ OpMemoryBarrier %76 %77
+ OpControlBarrier %76 %76 %78
+ %80 = OpLoad %61 %79
+ %81 = OpLoad %10 %20
+ %82 = OpBitcast %24 %81
+ %83 = OpLoad %10 %12
+ %84 = OpBitcast %24 %83
+ %85 = OpCompositeConstruct %69 %82 %84
+ %86 = OpAccessChain %74 %42 %34
+ %87 = OpLoad %24 %86
+ %88 = OpCompositeConstruct %71 %87 %27 %27 %27
+ OpImageWrite %80 %85 %88
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %89 = OpVariable %25 Function
+ OpStore %89 %27
+ OpBranch %90
+ %90 = OpLabel
+ OpLoopMerge %92 %93 None
+ OpBranch %94
+ %94 = OpLabel
+ %95 = OpLoad %24 %89
+ %97 = OpSLessThan %35 %95 %96
+ OpBranchConditional %97 %91 %92
+ %91 = OpLabel
+ %98 = OpLoad %24 %89
+ %99 = OpIEqual %35 %98 %27
+ OpSelectionMerge %101 None
+ OpBranchConditional %99 %100 %109
+ %100 = OpLabel
+ %102 = OpLoad %61 %63
+ %104 = OpImageRead %71 %102 %103
+ %105 = OpCompositeExtract %24 %104 0
+ %106 = OpConvertSToF %38 %105
+ %108 = OpAccessChain %107 %42 %59 %16
+ OpStore %108 %106
+ OpBranch %101
+ %109 = OpLabel
+ %111 = OpLoad %13 %110
+ %112 = OpConvertUToF %39 %111
+ %114 = OpCompositeExtract %38 %112 0
+ %115 = OpCompositeExtract %38 %112 1
+ %116 = OpCompositeConstruct %113 %114 %115
+ %117 = OpAccessChain %107 %42 %59 %21
+ %118 = OpCompositeExtract %38 %116 0
+ OpStore %117 %118
+ %119 = OpAccessChain %107 %42 %59 %76
+ %120 = OpCompositeExtract %38 %116 1
+ OpStore %119 %120
+ OpBranch %101
+ %101 = OpLabel
+ OpBranch %93
+ %93 = OpLabel
+ %121 = OpLoad %24 %89
+ %122 = OpIAdd %24 %121 %59
+ OpStore %89 %122
+ OpBranch %90
+ %92 = OpLabel
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+TEST(DiffTest, LargeFunctionsLargeDiffs) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 138
++; Bound: 190
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+-OpEntryPoint GLCompute %4 "main" %15
++OpEntryPoint GLCompute %4 "main" %138 %15
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "f1("
+ OpName %8 "f2("
+ OpName %12 "x"
++OpName %138 "gl_GlobalInvocationID"
+ OpName %15 "gl_LocalInvocationID"
+-OpName %20 "y"
++OpName %20 "z"
+ OpName %27 "image"
+-OpName %44 "sum"
++OpName %44 "i"
+-OpName %46 "i"
+-OpName %56 "j"
+ OpName %80 "BufferOut"
+ OpMemberName %80 0 "o_uv4"
+ OpMemberName %80 1 "o_v3"
+ OpMemberName %80 2 "o_i"
+ OpName %82 ""
+ OpName %88 "BufferIn"
+ OpMemberName %88 0 "i_u"
+ OpMemberName %88 1 "i_v4"
+ OpMemberName %88 2 "i_f"
+ OpName %90 ""
+ OpName %101 "i"
+ OpName %128 "image2"
++OpDecorate %138 BuiltIn GlobalInvocationId
+ OpDecorate %15 BuiltIn LocalInvocationId
+ OpDecorate %27 DescriptorSet 0
+ OpDecorate %27 Binding 2
+ OpMemberDecorate %80 0 Offset 0
+ OpMemberDecorate %80 1 Offset 16
+ OpMemberDecorate %80 2 Offset 28
+ OpDecorate %80 BufferBlock
+ OpDecorate %82 DescriptorSet 0
+ OpDecorate %82 Binding 1
+ OpMemberDecorate %88 0 Offset 0
+ OpMemberDecorate %88 1 RowMajor
+ OpMemberDecorate %88 1 Offset 16
+ OpMemberDecorate %88 1 MatrixStride 16
+ OpMemberDecorate %88 2 Offset 80
+ OpDecorate %88 Block
+ OpDecorate %90 DescriptorSet 0
+ OpDecorate %90 Binding 0
+ OpDecorate %128 DescriptorSet 0
+ OpDecorate %128 Binding 3
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpTypePointer Function %10
+ %13 = OpTypeVector %10 3
+ %14 = OpTypePointer Input %13
++%138 = OpVariable %14 Input
+ %15 = OpVariable %14 Input
+ %16 = OpConstant %10 0
+ %17 = OpTypePointer Input %10
+ %21 = OpConstant %10 1
+ %24 = OpTypeInt 32 1
+ %25 = OpTypeImage %24 2D 0 0 0 2 R32i
+ %26 = OpTypePointer UniformConstant %25
+ %27 = OpVariable %26 UniformConstant
+ %29 = OpTypeVector %10 2
+ %32 = OpTypeVector %24 2
+ %38 = OpTypeVector %24 4
+ %40 = OpConstant %10 2
+ %41 = OpConstant %10 3400
+ %42 = OpConstant %10 264
+ %43 = OpTypePointer Function %24
+ %45 = OpConstant %24 0
++%149 = OpTypePointer Uniform %10
+ %53 = OpConstant %24 2
+ %54 = OpTypeBool
+ %73 = OpConstant %24 1
+ %77 = OpTypeVector %10 4
+ %78 = OpTypeFloat 32
+ %79 = OpTypeVector %78 3
+ %80 = OpTypeStruct %77 %79 %24
+ %81 = OpTypePointer Uniform %80
+ %82 = OpVariable %81 Uniform
+ %84 = OpTypePointer Uniform %24
+ %86 = OpTypeVector %78 4
+ %87 = OpTypeMatrix %86 4
+ %88 = OpTypeStruct %10 %87 %78
+ %89 = OpTypePointer Uniform %88
+ %90 = OpVariable %89 Uniform
+-%91 = OpTypePointer Uniform %87
++%179 = OpTypeVector %78 2
+ %94 = OpTypePointer Uniform %77
+ %108 = OpConstant %24 3
+-%110 = OpTypePointer Uniform %79
+ %113 = OpTypePointer Uniform %78
+ %128 = OpVariable %26 UniformConstant
+ %130 = OpConstantComposite %32 %45 %45
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+-%136 = OpFunctionCall %2 %6
+ %137 = OpFunctionCall %2 %8
++%189 = OpFunctionCall %2 %6
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %12 = OpVariable %11 Function
+ %20 = OpVariable %11 Function
+ %44 = OpVariable %43 Function
+-%46 = OpVariable %43 Function
+-%56 = OpVariable %43 Function
+-%18 = OpAccessChain %17 %15 %16
++%139 = OpAccessChain %17 %138 %16
+-%19 = OpLoad %10 %18
++%19 = OpLoad %10 %139
+ OpStore %12 %19
+-%22 = OpAccessChain %17 %15 %21
++%140 = OpAccessChain %17 %138 %21
+-%23 = OpLoad %10 %22
++%23 = OpLoad %10 %140
+ OpStore %20 %23
+-%28 = OpLoad %25 %27
+-%30 = OpLoad %13 %15
+-%31 = OpVectorShuffle %29 %30 %30 0 1
+-%33 = OpBitcast %32 %31
+-%34 = OpLoad %10 %12
+-%35 = OpLoad %10 %20
+-%36 = OpIAdd %10 %34 %35
+-%37 = OpBitcast %24 %36
+-%39 = OpCompositeConstruct %38 %37 %37 %37 %37
+-OpImageWrite %28 %33 %39
+-OpMemoryBarrier %40 %41
+-OpControlBarrier %40 %40 %42
+ OpStore %44 %45
+-OpStore %46 %45
+ OpBranch %47
+ %47 = OpLabel
+-OpLoopMerge %49 %50 None
++OpLoopMerge %49 %59 None
+ OpBranch %51
+ %51 = OpLabel
+-%52 = OpLoad %24 %46
++%52 = OpLoad %24 %44
+ %55 = OpSLessThan %54 %52 %53
+ OpBranchConditional %55 %48 %49
+ %48 = OpLabel
+-OpStore %56 %45
+-OpBranch %57
+-%57 = OpLabel
+-OpLoopMerge %59 %60 None
+-OpBranch %61
+-%61 = OpLabel
+-%62 = OpLoad %24 %56
+-%63 = OpSLessThan %54 %62 %53
+-OpBranchConditional %63 %58 %59
+-%58 = OpLabel
+-%64 = OpLoad %25 %27
+-%65 = OpLoad %24 %46
+-%66 = OpLoad %24 %56
+-%67 = OpCompositeConstruct %32 %65 %66
+-%68 = OpImageRead %38 %64 %67
+-%69 = OpCompositeExtract %24 %68 0
+-%70 = OpLoad %24 %44
+-%71 = OpIMul %24 %70 %69
++%141 = OpLoad %10 %12
++%142 = OpLoad %10 %20
++%143 = OpIAdd %10 %141 %142
++%144 = OpCompositeConstruct %29 %143 %143
++%145 = OpAccessChain %94 %82 %45
++%146 = OpLoad %77 %145
++%147 = OpVectorShuffle %29 %146 %146 0 1
++%148 = OpIAdd %29 %147 %144
++%150 = OpAccessChain %149 %82 %45 %16
++%151 = OpCompositeExtract %10 %148 0
+-OpStore %44 %71
++OpStore %150 %151
+-OpBranch %60
+-%60 = OpLabel
+-%72 = OpLoad %24 %56
+-%74 = OpIAdd %24 %72 %73
++%152 = OpAccessChain %149 %82 %45 %21
++%153 = OpCompositeExtract %10 %148 1
+-OpStore %56 %74
++OpStore %152 %153
+-OpBranch %57
++OpBranch %59
+ %59 = OpLabel
+-OpBranch %50
+-%50 = OpLabel
+-%75 = OpLoad %24 %46
++%75 = OpLoad %24 %44
+ %76 = OpIAdd %24 %75 %73
+-OpStore %46 %76
++OpStore %44 %76
+ OpBranch %47
+ %49 = OpLabel
++%154 = OpLoad %25 %128
++%155 = OpLoad %10 %12
++%156 = OpBitcast %24 %155
++%157 = OpLoad %10 %20
++%158 = OpBitcast %24 %157
++%159 = OpCompositeConstruct %32 %156 %158
++%160 = OpImageRead %38 %154 %159
++%161 = OpCompositeExtract %24 %160 1
++%162 = OpAccessChain %84 %82 %53
++OpStore %162 %161
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+-%83 = OpLoad %24 %44
++%163 = OpLoad %25 %27
++%164 = OpLoad %10 %20
++%165 = OpBitcast %24 %164
++%166 = OpLoad %10 %12
++%167 = OpBitcast %24 %166
++%168 = OpCompositeConstruct %32 %165 %167
+ %85 = OpAccessChain %84 %82 %53
+-OpStore %85 %83
++%169 = OpLoad %24 %85
++%170 = OpCompositeConstruct %38 %169 %45 %45 %45
++OpImageWrite %163 %168 %170
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %101 = OpVariable %43 Function
+-%92 = OpAccessChain %91 %90 %73
+-%93 = OpLoad %87 %92
+-%95 = OpAccessChain %94 %82 %45
+-%96 = OpLoad %77 %95
+-%97 = OpConvertUToF %86 %96
+-%98 = OpMatrixTimesVector %86 %93 %97
+-%99 = OpConvertFToU %77 %98
+-%100 = OpAccessChain %94 %82 %45
+-OpStore %100 %99
++OpStore %101 %45
+-OpStore %101 %45
+ OpBranch %102
+ %102 = OpLabel
+-OpLoopMerge %104 %105 None
++OpLoopMerge %171 %172 None
+ OpBranch %106
+ %106 = OpLabel
+ %107 = OpLoad %24 %101
+ %109 = OpSLessThan %54 %107 %108
+-OpBranchConditional %109 %103 %104
++OpBranchConditional %109 %103 %171
+ %103 = OpLabel
+-%111 = OpAccessChain %110 %82 %73
+-%112 = OpLoad %79 %111
+-%114 = OpAccessChain %113 %90 %53
+-%115 = OpLoad %78 %114
+-%116 = OpVectorTimesScalar %79 %112 %115
+-%117 = OpConvertFToU %13 %116
+-%118 = OpCompositeExtract %10 %117 0
+-%119 = OpCompositeExtract %10 %117 1
+-%120 = OpCompositeExtract %10 %117 2
+-%121 = OpCompositeConstruct %77 %118 %119 %120 %16
+-%122 = OpAccessChain %94 %82 %45
+-%123 = OpLoad %77 %122
+-%124 = OpIAdd %77 %123 %121
+-%125 = OpAccessChain %94 %82 %45
+-OpStore %125 %124
+-OpBranch %105
+-%105 = OpLabel
+ %126 = OpLoad %24 %101
+-%127 = OpIAdd %24 %126 %73
++%173 = OpIEqual %54 %126 %45
++OpSelectionMerge %174 None
++OpBranchConditional %173 %104 %176
++%176 = OpLabel
++%177 = OpLoad %13 %15
++%178 = OpConvertUToF %79 %177
++%180 = OpCompositeExtract %78 %178 0
++%181 = OpCompositeExtract %78 %178 1
++%182 = OpCompositeConstruct %179 %180 %181
++%183 = OpAccessChain %113 %82 %73 %21
++%184 = OpCompositeExtract %78 %182 0
+-OpStore %101 %127
++OpStore %183 %184
++%185 = OpAccessChain %113 %82 %73 %40
++%186 = OpCompositeExtract %78 %182 1
++OpStore %185 %186
+-OpBranch %102
++OpBranch %174
+ %104 = OpLabel
+-OpMemoryBarrier %40 %41
+-OpControlBarrier %40 %40 %42
+ %129 = OpLoad %25 %128
+ %131 = OpImageRead %38 %129 %130
+ %132 = OpCompositeExtract %24 %131 0
+ %133 = OpConvertSToF %78 %132
+-%134 = OpCompositeConstruct %79 %133 %133 %133
+-%135 = OpAccessChain %110 %82 %73
++%175 = OpAccessChain %113 %82 %73 %16
+-OpStore %135 %134
++OpStore %175 %133
++OpBranch %174
++%174 = OpLabel
++OpBranch %172
++%172 = OpLabel
++%187 = OpLoad %24 %101
++%188 = OpIAdd %24 %187 %73
++OpStore %101 %188
++OpBranch %102
++%171 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, LargeFunctionsLargeDiffsNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main" %15
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpDecorate %15 BuiltIn LocalInvocationId
+ OpDecorate %27 DescriptorSet 0
+ OpDecorate %27 Binding 2
+ OpMemberDecorate %80 0 Offset 0
+ OpMemberDecorate %80 1 Offset 16
+ OpMemberDecorate %80 2 Offset 28
+ OpDecorate %80 BufferBlock
+ OpDecorate %82 DescriptorSet 0
+ OpDecorate %82 Binding 1
+ OpMemberDecorate %88 0 Offset 0
+ OpMemberDecorate %88 1 RowMajor
+ OpMemberDecorate %88 1 Offset 16
+ OpMemberDecorate %88 1 MatrixStride 16
+ OpMemberDecorate %88 2 Offset 80
+ OpDecorate %88 Block
+ OpDecorate %90 DescriptorSet 0
+ OpDecorate %90 Binding 0
+ OpDecorate %128 DescriptorSet 0
+ OpDecorate %128 Binding 3
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpTypePointer Function %10
+ %13 = OpTypeVector %10 3
+ %14 = OpTypePointer Input %13
+ %15 = OpVariable %14 Input
+ %16 = OpConstant %10 0
+ %17 = OpTypePointer Input %10
+ %21 = OpConstant %10 1
+ %24 = OpTypeInt 32 1
+ %25 = OpTypeImage %24 2D 0 0 0 2 R32i
+ %26 = OpTypePointer UniformConstant %25
+ %27 = OpVariable %26 UniformConstant
+ %29 = OpTypeVector %10 2
+ %32 = OpTypeVector %24 2
+ %38 = OpTypeVector %24 4
+ %40 = OpConstant %10 2
+ %41 = OpConstant %10 3400
+ %42 = OpConstant %10 264
+ %43 = OpTypePointer Function %24
+ %45 = OpConstant %24 0
+ %53 = OpConstant %24 2
+ %54 = OpTypeBool
+ %73 = OpConstant %24 1
+ %77 = OpTypeVector %10 4
+ %78 = OpTypeFloat 32
+ %79 = OpTypeVector %78 3
+ %80 = OpTypeStruct %77 %79 %24
+ %81 = OpTypePointer Uniform %80
+ %82 = OpVariable %81 Uniform
+ %84 = OpTypePointer Uniform %24
+ %86 = OpTypeVector %78 4
+ %87 = OpTypeMatrix %86 4
+ %88 = OpTypeStruct %10 %87 %78
+ %89 = OpTypePointer Uniform %88
+ %90 = OpVariable %89 Uniform
+ %91 = OpTypePointer Uniform %87
+ %94 = OpTypePointer Uniform %77
+ %108 = OpConstant %24 3
+ %110 = OpTypePointer Uniform %79
+ %113 = OpTypePointer Uniform %78
+ %128 = OpVariable %26 UniformConstant
+ %130 = OpConstantComposite %32 %45 %45
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %136 = OpFunctionCall %2 %6
+ %137 = OpFunctionCall %2 %8
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %12 = OpVariable %11 Function
+ %20 = OpVariable %11 Function
+ %44 = OpVariable %43 Function
+ %46 = OpVariable %43 Function
+ %56 = OpVariable %43 Function
+ %18 = OpAccessChain %17 %15 %16
+ %19 = OpLoad %10 %18
+ OpStore %12 %19
+ %22 = OpAccessChain %17 %15 %21
+ %23 = OpLoad %10 %22
+ OpStore %20 %23
+ %28 = OpLoad %25 %27
+ %30 = OpLoad %13 %15
+ %31 = OpVectorShuffle %29 %30 %30 0 1
+ %33 = OpBitcast %32 %31
+ %34 = OpLoad %10 %12
+ %35 = OpLoad %10 %20
+ %36 = OpIAdd %10 %34 %35
+ %37 = OpBitcast %24 %36
+ %39 = OpCompositeConstruct %38 %37 %37 %37 %37
+ OpImageWrite %28 %33 %39
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ OpStore %44 %45
+ OpStore %46 %45
+ OpBranch %47
+ %47 = OpLabel
+ OpLoopMerge %49 %50 None
+ OpBranch %51
+ %51 = OpLabel
+ %52 = OpLoad %24 %46
+ %55 = OpSLessThan %54 %52 %53
+ OpBranchConditional %55 %48 %49
+ %48 = OpLabel
+ OpStore %56 %45
+ OpBranch %57
+ %57 = OpLabel
+ OpLoopMerge %59 %60 None
+ OpBranch %61
+ %61 = OpLabel
+ %62 = OpLoad %24 %56
+ %63 = OpSLessThan %54 %62 %53
+ OpBranchConditional %63 %58 %59
+ %58 = OpLabel
+ %64 = OpLoad %25 %27
+ %65 = OpLoad %24 %46
+ %66 = OpLoad %24 %56
+ %67 = OpCompositeConstruct %32 %65 %66
+ %68 = OpImageRead %38 %64 %67
+ %69 = OpCompositeExtract %24 %68 0
+ %70 = OpLoad %24 %44
+ %71 = OpIMul %24 %70 %69
+ OpStore %44 %71
+ OpBranch %60
+ %60 = OpLabel
+ %72 = OpLoad %24 %56
+ %74 = OpIAdd %24 %72 %73
+ OpStore %56 %74
+ OpBranch %57
+ %59 = OpLabel
+ OpBranch %50
+ %50 = OpLabel
+ %75 = OpLoad %24 %46
+ %76 = OpIAdd %24 %75 %73
+ OpStore %46 %76
+ OpBranch %47
+ %49 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %83 = OpLoad %24 %44
+ %85 = OpAccessChain %84 %82 %53
+ OpStore %85 %83
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %101 = OpVariable %43 Function
+ %92 = OpAccessChain %91 %90 %73
+ %93 = OpLoad %87 %92
+ %95 = OpAccessChain %94 %82 %45
+ %96 = OpLoad %77 %95
+ %97 = OpConvertUToF %86 %96
+ %98 = OpMatrixTimesVector %86 %93 %97
+ %99 = OpConvertFToU %77 %98
+ %100 = OpAccessChain %94 %82 %45
+ OpStore %100 %99
+ OpStore %101 %45
+ OpBranch %102
+ %102 = OpLabel
+ OpLoopMerge %104 %105 None
+ OpBranch %106
+ %106 = OpLabel
+ %107 = OpLoad %24 %101
+ %109 = OpSLessThan %54 %107 %108
+ OpBranchConditional %109 %103 %104
+ %103 = OpLabel
+ %111 = OpAccessChain %110 %82 %73
+ %112 = OpLoad %79 %111
+ %114 = OpAccessChain %113 %90 %53
+ %115 = OpLoad %78 %114
+ %116 = OpVectorTimesScalar %79 %112 %115
+ %117 = OpConvertFToU %13 %116
+ %118 = OpCompositeExtract %10 %117 0
+ %119 = OpCompositeExtract %10 %117 1
+ %120 = OpCompositeExtract %10 %117 2
+ %121 = OpCompositeConstruct %77 %118 %119 %120 %16
+ %122 = OpAccessChain %94 %82 %45
+ %123 = OpLoad %77 %122
+ %124 = OpIAdd %77 %123 %121
+ %125 = OpAccessChain %94 %82 %45
+ OpStore %125 %124
+ OpBranch %105
+ %105 = OpLabel
+ %126 = OpLoad %24 %101
+ %127 = OpIAdd %24 %126 %73
+ OpStore %101 %127
+ OpBranch %102
+ %104 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %129 = OpLoad %25 %128
+ %131 = OpImageRead %38 %129 %130
+ %132 = OpCompositeExtract %24 %131 0
+ %133 = OpConvertSToF %78 %132
+ %134 = OpCompositeConstruct %79 %133 %133 %133
+ %135 = OpAccessChain %110 %82 %73
+ OpStore %135 %134
+ OpReturn
+ OpFunctionEnd
+
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main" %15 %110
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpDecorate %15 BuiltIn GlobalInvocationId
+ OpMemberDecorate %40 0 Offset 0
+ OpMemberDecorate %40 1 Offset 16
+ OpMemberDecorate %40 2 Offset 28
+ OpDecorate %40 BufferBlock
+ OpDecorate %42 DescriptorSet 0
+ OpDecorate %42 Binding 1
+ OpDecorate %63 DescriptorSet 0
+ OpDecorate %63 Binding 3
+ OpDecorate %79 DescriptorSet 0
+ OpDecorate %79 Binding 2
+ OpDecorate %110 BuiltIn LocalInvocationId
+ OpMemberDecorate %127 0 Offset 0
+ OpMemberDecorate %127 1 RowMajor
+ OpMemberDecorate %127 1 Offset 16
+ OpMemberDecorate %127 1 MatrixStride 16
+ OpMemberDecorate %127 2 Offset 80
+ OpDecorate %127 Block
+ OpDecorate %129 DescriptorSet 0
+ OpDecorate %129 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpTypePointer Function %10
+ %13 = OpTypeVector %10 3
+ %14 = OpTypePointer Input %13
+ %15 = OpVariable %14 Input
+ %16 = OpConstant %10 0
+ %17 = OpTypePointer Input %10
+ %21 = OpConstant %10 1
+ %24 = OpTypeInt 32 1
+ %25 = OpTypePointer Function %24
+ %27 = OpConstant %24 0
+ %34 = OpConstant %24 2
+ %35 = OpTypeBool
+ %37 = OpTypeVector %10 4
+ %38 = OpTypeFloat 32
+ %39 = OpTypeVector %38 3
+ %40 = OpTypeStruct %37 %39 %24
+ %41 = OpTypePointer Uniform %40
+ %42 = OpVariable %41 Uniform
+ %46 = OpTypeVector %10 2
+ %48 = OpTypePointer Uniform %37
+ %53 = OpTypePointer Uniform %10
+ %59 = OpConstant %24 1
+ %61 = OpTypeImage %24 2D 0 0 0 2 R32i
+ %62 = OpTypePointer UniformConstant %61
+ %63 = OpVariable %62 UniformConstant
+ %69 = OpTypeVector %24 2
+ %71 = OpTypeVector %24 4
+ %74 = OpTypePointer Uniform %24
+ %76 = OpConstant %10 2
+ %77 = OpConstant %10 3400
+ %78 = OpConstant %10 264
+ %79 = OpVariable %62 UniformConstant
+ %96 = OpConstant %24 3
+ %103 = OpConstantComposite %69 %27 %27
+ %107 = OpTypePointer Uniform %38
+ %110 = OpVariable %14 Input
+ %113 = OpTypeVector %38 2
+ %125 = OpTypeVector %38 4
+ %126 = OpTypeMatrix %125 4
+ %127 = OpTypeStruct %10 %126 %38
+ %128 = OpTypePointer Uniform %127
+ %129 = OpVariable %128 Uniform
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %123 = OpFunctionCall %2 %8
+ %124 = OpFunctionCall %2 %6
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %12 = OpVariable %11 Function
+ %20 = OpVariable %11 Function
+ %26 = OpVariable %25 Function
+ %18 = OpAccessChain %17 %15 %16
+ %19 = OpLoad %10 %18
+ OpStore %12 %19
+ %22 = OpAccessChain %17 %15 %21
+ %23 = OpLoad %10 %22
+ OpStore %20 %23
+ OpStore %26 %27
+ OpBranch %28
+ %28 = OpLabel
+ OpLoopMerge %30 %31 None
+ OpBranch %32
+ %32 = OpLabel
+ %33 = OpLoad %24 %26
+ %36 = OpSLessThan %35 %33 %34
+ OpBranchConditional %36 %29 %30
+ %29 = OpLabel
+ %43 = OpLoad %10 %12
+ %44 = OpLoad %10 %20
+ %45 = OpIAdd %10 %43 %44
+ %47 = OpCompositeConstruct %46 %45 %45
+ %49 = OpAccessChain %48 %42 %27
+ %50 = OpLoad %37 %49
+ %51 = OpVectorShuffle %46 %50 %50 0 1
+ %52 = OpIAdd %46 %51 %47
+ %54 = OpAccessChain %53 %42 %27 %16
+ %55 = OpCompositeExtract %10 %52 0
+ OpStore %54 %55
+ %56 = OpAccessChain %53 %42 %27 %21
+ %57 = OpCompositeExtract %10 %52 1
+ OpStore %56 %57
+ OpBranch %31
+ %31 = OpLabel
+ %58 = OpLoad %24 %26
+ %60 = OpIAdd %24 %58 %59
+ OpStore %26 %60
+ OpBranch %28
+ %30 = OpLabel
+ %64 = OpLoad %61 %63
+ %65 = OpLoad %10 %12
+ %66 = OpBitcast %24 %65
+ %67 = OpLoad %10 %20
+ %68 = OpBitcast %24 %67
+ %70 = OpCompositeConstruct %69 %66 %68
+ %72 = OpImageRead %71 %64 %70
+ %73 = OpCompositeExtract %24 %72 1
+ %75 = OpAccessChain %74 %42 %34
+ OpStore %75 %73
+ OpMemoryBarrier %76 %77
+ OpControlBarrier %76 %76 %78
+ %80 = OpLoad %61 %79
+ %81 = OpLoad %10 %20
+ %82 = OpBitcast %24 %81
+ %83 = OpLoad %10 %12
+ %84 = OpBitcast %24 %83
+ %85 = OpCompositeConstruct %69 %82 %84
+ %86 = OpAccessChain %74 %42 %34
+ %87 = OpLoad %24 %86
+ %88 = OpCompositeConstruct %71 %87 %27 %27 %27
+ OpImageWrite %80 %85 %88
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %89 = OpVariable %25 Function
+ OpStore %89 %27
+ OpBranch %90
+ %90 = OpLabel
+ OpLoopMerge %92 %93 None
+ OpBranch %94
+ %94 = OpLabel
+ %95 = OpLoad %24 %89
+ %97 = OpSLessThan %35 %95 %96
+ OpBranchConditional %97 %91 %92
+ %91 = OpLabel
+ %98 = OpLoad %24 %89
+ %99 = OpIEqual %35 %98 %27
+ OpSelectionMerge %101 None
+ OpBranchConditional %99 %100 %109
+ %100 = OpLabel
+ %102 = OpLoad %61 %63
+ %104 = OpImageRead %71 %102 %103
+ %105 = OpCompositeExtract %24 %104 0
+ %106 = OpConvertSToF %38 %105
+ %108 = OpAccessChain %107 %42 %59 %16
+ OpStore %108 %106
+ OpBranch %101
+ %109 = OpLabel
+ %111 = OpLoad %13 %110
+ %112 = OpConvertUToF %39 %111
+ %114 = OpCompositeExtract %38 %112 0
+ %115 = OpCompositeExtract %38 %112 1
+ %116 = OpCompositeConstruct %113 %114 %115
+ %117 = OpAccessChain %107 %42 %59 %21
+ %118 = OpCompositeExtract %38 %116 0
+ OpStore %117 %118
+ %119 = OpAccessChain %107 %42 %59 %76
+ %120 = OpCompositeExtract %38 %116 1
+ OpStore %119 %120
+ OpBranch %101
+ %101 = OpLabel
+ OpBranch %93
+ %93 = OpLabel
+ %121 = OpLoad %24 %89
+ %122 = OpIAdd %24 %121 %59
+ OpStore %89 %122
+ OpBranch %90
+ %92 = OpLabel
+ OpReturn
+ OpFunctionEnd
+
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 138
++; Bound: 220
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+-OpEntryPoint GLCompute %4 "main" %15
++OpEntryPoint GLCompute %4 "main" %143 %15
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
++OpDecorate %143 BuiltIn GlobalInvocationId
+ OpDecorate %15 BuiltIn LocalInvocationId
+ OpDecorate %27 DescriptorSet 0
+ OpDecorate %27 Binding 2
+ OpMemberDecorate %80 0 Offset 0
+ OpMemberDecorate %80 1 Offset 16
+ OpMemberDecorate %80 2 Offset 28
+ OpDecorate %80 BufferBlock
+ OpDecorate %82 DescriptorSet 0
+ OpDecorate %82 Binding 1
+ OpMemberDecorate %88 0 Offset 0
+ OpMemberDecorate %88 1 RowMajor
+ OpMemberDecorate %88 1 Offset 16
+ OpMemberDecorate %88 1 MatrixStride 16
+ OpMemberDecorate %88 2 Offset 80
+ OpDecorate %88 Block
+ OpDecorate %90 DescriptorSet 0
+ OpDecorate %90 Binding 0
+ OpDecorate %128 DescriptorSet 0
+ OpDecorate %128 Binding 3
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpTypePointer Function %10
+ %13 = OpTypeVector %10 3
+ %14 = OpTypePointer Input %13
++%143 = OpVariable %14 Input
+ %15 = OpVariable %14 Input
+ %16 = OpConstant %10 0
+ %17 = OpTypePointer Input %10
+ %21 = OpConstant %10 1
+ %24 = OpTypeInt 32 1
+ %25 = OpTypeImage %24 2D 0 0 0 2 R32i
+ %26 = OpTypePointer UniformConstant %25
+ %27 = OpVariable %26 UniformConstant
+ %29 = OpTypeVector %10 2
+ %32 = OpTypeVector %24 2
+ %38 = OpTypeVector %24 4
+ %40 = OpConstant %10 2
+ %41 = OpConstant %10 3400
+ %42 = OpConstant %10 264
+ %43 = OpTypePointer Function %24
+ %45 = OpConstant %24 0
++%165 = OpTypePointer Uniform %10
+ %53 = OpConstant %24 2
+ %54 = OpTypeBool
+ %73 = OpConstant %24 1
+ %77 = OpTypeVector %10 4
+ %78 = OpTypeFloat 32
+ %79 = OpTypeVector %78 3
+ %80 = OpTypeStruct %77 %79 %24
+ %81 = OpTypePointer Uniform %80
+ %82 = OpVariable %81 Uniform
+ %84 = OpTypePointer Uniform %24
+ %86 = OpTypeVector %78 4
+ %87 = OpTypeMatrix %86 4
+ %88 = OpTypeStruct %10 %87 %78
+ %89 = OpTypePointer Uniform %88
+ %90 = OpVariable %89 Uniform
+-%91 = OpTypePointer Uniform %87
++%210 = OpTypeVector %78 2
+ %94 = OpTypePointer Uniform %77
+ %108 = OpConstant %24 3
+-%110 = OpTypePointer Uniform %79
+ %113 = OpTypePointer Uniform %78
+ %128 = OpVariable %26 UniformConstant
+ %130 = OpConstantComposite %32 %45 %45
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+-%136 = OpFunctionCall %2 %6
++%136 = OpFunctionCall %2 %140
+-%137 = OpFunctionCall %2 %8
++%137 = OpFunctionCall %2 %138
+ OpReturn
+ OpFunctionEnd
+-%6 = OpFunction %2 None %3
+-%7 = OpLabel
+-%12 = OpVariable %11 Function
+-%20 = OpVariable %11 Function
+-%44 = OpVariable %43 Function
+-%46 = OpVariable %43 Function
+-%56 = OpVariable %43 Function
+-%18 = OpAccessChain %17 %15 %16
+-%19 = OpLoad %10 %18
+-OpStore %12 %19
+-%22 = OpAccessChain %17 %15 %21
+-%23 = OpLoad %10 %22
+-OpStore %20 %23
+-%28 = OpLoad %25 %27
+-%30 = OpLoad %13 %15
+-%31 = OpVectorShuffle %29 %30 %30 0 1
+-%33 = OpBitcast %32 %31
+-%34 = OpLoad %10 %12
+-%35 = OpLoad %10 %20
+-%36 = OpIAdd %10 %34 %35
+-%37 = OpBitcast %24 %36
+-%39 = OpCompositeConstruct %38 %37 %37 %37 %37
+-OpImageWrite %28 %33 %39
+-OpMemoryBarrier %40 %41
+-OpControlBarrier %40 %40 %42
+-OpStore %44 %45
+-OpStore %46 %45
+-OpBranch %47
+-%47 = OpLabel
+-OpLoopMerge %49 %50 None
+-OpBranch %51
+-%51 = OpLabel
+-%52 = OpLoad %24 %46
+-%55 = OpSLessThan %54 %52 %53
+-OpBranchConditional %55 %48 %49
+-%48 = OpLabel
+-OpStore %56 %45
+-OpBranch %57
+-%57 = OpLabel
+-OpLoopMerge %59 %60 None
+-OpBranch %61
+-%61 = OpLabel
+-%62 = OpLoad %24 %56
+-%63 = OpSLessThan %54 %62 %53
+-OpBranchConditional %63 %58 %59
+-%58 = OpLabel
+-%64 = OpLoad %25 %27
+-%65 = OpLoad %24 %46
+-%66 = OpLoad %24 %56
+-%67 = OpCompositeConstruct %32 %65 %66
+-%68 = OpImageRead %38 %64 %67
+-%69 = OpCompositeExtract %24 %68 0
+-%70 = OpLoad %24 %44
+-%71 = OpIMul %24 %70 %69
+-OpStore %44 %71
+-OpBranch %60
+-%60 = OpLabel
+-%72 = OpLoad %24 %56
+-%74 = OpIAdd %24 %72 %73
+-OpStore %56 %74
+-OpBranch %57
+-%59 = OpLabel
+-OpBranch %50
+-%50 = OpLabel
+-%75 = OpLoad %24 %46
+-%76 = OpIAdd %24 %75 %73
+-OpStore %46 %76
+-OpBranch %47
+-%49 = OpLabel
+-OpMemoryBarrier %40 %41
+-OpControlBarrier %40 %40 %42
+-%83 = OpLoad %24 %44
+-%85 = OpAccessChain %84 %82 %53
+-OpStore %85 %83
+-OpReturn
+-OpFunctionEnd
+-%8 = OpFunction %2 None %3
+-%9 = OpLabel
+-%101 = OpVariable %43 Function
+-%92 = OpAccessChain %91 %90 %73
+-%93 = OpLoad %87 %92
+-%95 = OpAccessChain %94 %82 %45
+-%96 = OpLoad %77 %95
+-%97 = OpConvertUToF %86 %96
+-%98 = OpMatrixTimesVector %86 %93 %97
+-%99 = OpConvertFToU %77 %98
+-%100 = OpAccessChain %94 %82 %45
+-OpStore %100 %99
+-OpStore %101 %45
+-OpBranch %102
+-%102 = OpLabel
+-OpLoopMerge %104 %105 None
+-OpBranch %106
+-%106 = OpLabel
+-%107 = OpLoad %24 %101
+-%109 = OpSLessThan %54 %107 %108
+-OpBranchConditional %109 %103 %104
+-%103 = OpLabel
+-%111 = OpAccessChain %110 %82 %73
+-%112 = OpLoad %79 %111
+-%114 = OpAccessChain %113 %90 %53
+-%115 = OpLoad %78 %114
+-%116 = OpVectorTimesScalar %79 %112 %115
+-%117 = OpConvertFToU %13 %116
+-%118 = OpCompositeExtract %10 %117 0
+-%119 = OpCompositeExtract %10 %117 1
+-%120 = OpCompositeExtract %10 %117 2
+-%121 = OpCompositeConstruct %77 %118 %119 %120 %16
+-%122 = OpAccessChain %94 %82 %45
+-%123 = OpLoad %77 %122
+-%124 = OpIAdd %77 %123 %121
+-%125 = OpAccessChain %94 %82 %45
+-OpStore %125 %124
+-OpBranch %105
+-%105 = OpLabel
+-%126 = OpLoad %24 %101
+-%127 = OpIAdd %24 %126 %73
+-OpStore %101 %127
+-OpBranch %102
+-%104 = OpLabel
+-OpMemoryBarrier %40 %41
+-OpControlBarrier %40 %40 %42
+-%129 = OpLoad %25 %128
+-%131 = OpImageRead %38 %129 %130
+-%132 = OpCompositeExtract %24 %131 0
+-%133 = OpConvertSToF %78 %132
+-%134 = OpCompositeConstruct %79 %133 %133 %133
+-%135 = OpAccessChain %110 %82 %73
+-OpStore %135 %134
+-OpReturn
+-OpFunctionEnd
++%138 = OpFunction %2 None %3
++%139 = OpLabel
++%142 = OpVariable %11 Function
++%146 = OpVariable %11 Function
++%149 = OpVariable %43 Function
++%144 = OpAccessChain %17 %143 %16
++%145 = OpLoad %10 %144
++OpStore %142 %145
++%147 = OpAccessChain %17 %143 %21
++%148 = OpLoad %10 %147
++OpStore %146 %148
++OpStore %149 %45
++OpBranch %150
++%150 = OpLabel
++OpLoopMerge %152 %153 None
++OpBranch %154
++%154 = OpLabel
++%155 = OpLoad %24 %149
++%156 = OpSLessThan %54 %155 %53
++OpBranchConditional %156 %151 %152
++%151 = OpLabel
++%157 = OpLoad %10 %142
++%158 = OpLoad %10 %146
++%159 = OpIAdd %10 %157 %158
++%160 = OpCompositeConstruct %29 %159 %159
++%161 = OpAccessChain %94 %82 %45
++%162 = OpLoad %77 %161
++%163 = OpVectorShuffle %29 %162 %162 0 1
++%164 = OpIAdd %29 %163 %160
++%166 = OpAccessChain %165 %82 %45 %16
++%167 = OpCompositeExtract %10 %164 0
++OpStore %166 %167
++%168 = OpAccessChain %165 %82 %45 %21
++%169 = OpCompositeExtract %10 %164 1
++OpStore %168 %169
++OpBranch %153
++%153 = OpLabel
++%170 = OpLoad %24 %149
++%171 = OpIAdd %24 %170 %73
++OpStore %149 %171
++OpBranch %150
++%152 = OpLabel
++%172 = OpLoad %25 %128
++%173 = OpLoad %10 %142
++%174 = OpBitcast %24 %173
++%175 = OpLoad %10 %146
++%176 = OpBitcast %24 %175
++%177 = OpCompositeConstruct %32 %174 %176
++%178 = OpImageRead %38 %172 %177
++%179 = OpCompositeExtract %24 %178 1
++%180 = OpAccessChain %84 %82 %53
++OpStore %180 %179
++OpMemoryBarrier %40 %41
++OpControlBarrier %40 %40 %42
++%181 = OpLoad %25 %27
++%182 = OpLoad %10 %146
++%183 = OpBitcast %24 %182
++%184 = OpLoad %10 %142
++%185 = OpBitcast %24 %184
++%186 = OpCompositeConstruct %32 %183 %185
++%187 = OpAccessChain %84 %82 %53
++%188 = OpLoad %24 %187
++%189 = OpCompositeConstruct %38 %188 %45 %45 %45
++OpImageWrite %181 %186 %189
++OpReturn
++OpFunctionEnd
++%140 = OpFunction %2 None %3
++%141 = OpLabel
++%190 = OpVariable %43 Function
++OpStore %190 %45
++OpBranch %191
++%191 = OpLabel
++OpLoopMerge %193 %194 None
++OpBranch %195
++%195 = OpLabel
++%196 = OpLoad %24 %190
++%197 = OpSLessThan %54 %196 %108
++OpBranchConditional %197 %192 %193
++%192 = OpLabel
++%198 = OpLoad %24 %190
++%199 = OpIEqual %54 %198 %45
++OpSelectionMerge %201 None
++OpBranchConditional %199 %200 %207
++%207 = OpLabel
++%208 = OpLoad %13 %15
++%209 = OpConvertUToF %79 %208
++%211 = OpCompositeExtract %78 %209 0
++%212 = OpCompositeExtract %78 %209 1
++%213 = OpCompositeConstruct %210 %211 %212
++%214 = OpAccessChain %113 %82 %73 %21
++%215 = OpCompositeExtract %78 %213 0
++OpStore %214 %215
++%216 = OpAccessChain %113 %82 %73 %40
++%217 = OpCompositeExtract %78 %213 1
++OpStore %216 %217
++OpBranch %201
++%200 = OpLabel
++%202 = OpLoad %25 %128
++%203 = OpImageRead %38 %202 %130
++%204 = OpCompositeExtract %24 %203 0
++%205 = OpConvertSToF %78 %204
++%206 = OpAccessChain %113 %82 %73 %16
++OpStore %206 %205
++OpBranch %201
++%201 = OpLabel
++OpBranch %194
++%194 = OpLabel
++%218 = OpLoad %24 %190
++%219 = OpIAdd %24 %218 %73
++OpStore %190 %219
++OpBranch %191
++%193 = OpLabel
++OpReturn
++OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/large_functions_large_diffs_dst.spvasm b/test/diff/diff_files/large_functions_large_diffs_dst.spvasm
new file mode 100644
index 00000000..be7c1d5f
--- /dev/null
+++ b/test/diff/diff_files/large_functions_large_diffs_dst.spvasm
@@ -0,0 +1,213 @@
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main" %15 %110
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "f1("
+ OpName %8 "f2("
+ OpName %12 "x"
+ OpName %15 "gl_GlobalInvocationID"
+ OpName %20 "z"
+ OpName %26 "i"
+ OpName %40 "BufferOut"
+ OpMemberName %40 0 "o_uv4"
+ OpMemberName %40 1 "o_v3"
+ OpMemberName %40 2 "o_i"
+ OpName %42 ""
+ OpName %63 "image2"
+ OpName %79 "image"
+ OpName %89 "i"
+ OpName %110 "gl_LocalInvocationID"
+ OpName %127 "BufferIn"
+ OpMemberName %127 0 "i_u"
+ OpMemberName %127 1 "i_v4"
+ OpMemberName %127 2 "i_f"
+ OpName %129 ""
+ OpDecorate %15 BuiltIn GlobalInvocationId
+ OpMemberDecorate %40 0 Offset 0
+ OpMemberDecorate %40 1 Offset 16
+ OpMemberDecorate %40 2 Offset 28
+ OpDecorate %40 BufferBlock
+ OpDecorate %42 DescriptorSet 0
+ OpDecorate %42 Binding 1
+ OpDecorate %63 DescriptorSet 0
+ OpDecorate %63 Binding 3
+ OpDecorate %79 DescriptorSet 0
+ OpDecorate %79 Binding 2
+ OpDecorate %110 BuiltIn LocalInvocationId
+ OpMemberDecorate %127 0 Offset 0
+ OpMemberDecorate %127 1 RowMajor
+ OpMemberDecorate %127 1 Offset 16
+ OpMemberDecorate %127 1 MatrixStride 16
+ OpMemberDecorate %127 2 Offset 80
+ OpDecorate %127 Block
+ OpDecorate %129 DescriptorSet 0
+ OpDecorate %129 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpTypePointer Function %10
+ %13 = OpTypeVector %10 3
+ %14 = OpTypePointer Input %13
+ %15 = OpVariable %14 Input
+ %16 = OpConstant %10 0
+ %17 = OpTypePointer Input %10
+ %21 = OpConstant %10 1
+ %24 = OpTypeInt 32 1
+ %25 = OpTypePointer Function %24
+ %27 = OpConstant %24 0
+ %34 = OpConstant %24 2
+ %35 = OpTypeBool
+ %37 = OpTypeVector %10 4
+ %38 = OpTypeFloat 32
+ %39 = OpTypeVector %38 3
+ %40 = OpTypeStruct %37 %39 %24
+ %41 = OpTypePointer Uniform %40
+ %42 = OpVariable %41 Uniform
+ %46 = OpTypeVector %10 2
+ %48 = OpTypePointer Uniform %37
+ %53 = OpTypePointer Uniform %10
+ %59 = OpConstant %24 1
+ %61 = OpTypeImage %24 2D 0 0 0 2 R32i
+ %62 = OpTypePointer UniformConstant %61
+ %63 = OpVariable %62 UniformConstant
+ %69 = OpTypeVector %24 2
+ %71 = OpTypeVector %24 4
+ %74 = OpTypePointer Uniform %24
+ %76 = OpConstant %10 2
+ %77 = OpConstant %10 3400
+ %78 = OpConstant %10 264
+ %79 = OpVariable %62 UniformConstant
+ %96 = OpConstant %24 3
+ %103 = OpConstantComposite %69 %27 %27
+ %107 = OpTypePointer Uniform %38
+ %110 = OpVariable %14 Input
+ %113 = OpTypeVector %38 2
+ %125 = OpTypeVector %38 4
+ %126 = OpTypeMatrix %125 4
+ %127 = OpTypeStruct %10 %126 %38
+ %128 = OpTypePointer Uniform %127
+ %129 = OpVariable %128 Uniform
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %123 = OpFunctionCall %2 %8
+ %124 = OpFunctionCall %2 %6
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %12 = OpVariable %11 Function
+ %20 = OpVariable %11 Function
+ %26 = OpVariable %25 Function
+ %18 = OpAccessChain %17 %15 %16
+ %19 = OpLoad %10 %18
+ OpStore %12 %19
+ %22 = OpAccessChain %17 %15 %21
+ %23 = OpLoad %10 %22
+ OpStore %20 %23
+ OpStore %26 %27
+ OpBranch %28
+ %28 = OpLabel
+ OpLoopMerge %30 %31 None
+ OpBranch %32
+ %32 = OpLabel
+ %33 = OpLoad %24 %26
+ %36 = OpSLessThan %35 %33 %34
+ OpBranchConditional %36 %29 %30
+ %29 = OpLabel
+ %43 = OpLoad %10 %12
+ %44 = OpLoad %10 %20
+ %45 = OpIAdd %10 %43 %44
+ %47 = OpCompositeConstruct %46 %45 %45
+ %49 = OpAccessChain %48 %42 %27
+ %50 = OpLoad %37 %49
+ %51 = OpVectorShuffle %46 %50 %50 0 1
+ %52 = OpIAdd %46 %51 %47
+ %54 = OpAccessChain %53 %42 %27 %16
+ %55 = OpCompositeExtract %10 %52 0
+ OpStore %54 %55
+ %56 = OpAccessChain %53 %42 %27 %21
+ %57 = OpCompositeExtract %10 %52 1
+ OpStore %56 %57
+ OpBranch %31
+ %31 = OpLabel
+ %58 = OpLoad %24 %26
+ %60 = OpIAdd %24 %58 %59
+ OpStore %26 %60
+ OpBranch %28
+ %30 = OpLabel
+ %64 = OpLoad %61 %63
+ %65 = OpLoad %10 %12
+ %66 = OpBitcast %24 %65
+ %67 = OpLoad %10 %20
+ %68 = OpBitcast %24 %67
+ %70 = OpCompositeConstruct %69 %66 %68
+ %72 = OpImageRead %71 %64 %70
+ %73 = OpCompositeExtract %24 %72 1
+ %75 = OpAccessChain %74 %42 %34
+ OpStore %75 %73
+ OpMemoryBarrier %76 %77
+ OpControlBarrier %76 %76 %78
+ %80 = OpLoad %61 %79
+ %81 = OpLoad %10 %20
+ %82 = OpBitcast %24 %81
+ %83 = OpLoad %10 %12
+ %84 = OpBitcast %24 %83
+ %85 = OpCompositeConstruct %69 %82 %84
+ %86 = OpAccessChain %74 %42 %34
+ %87 = OpLoad %24 %86
+ %88 = OpCompositeConstruct %71 %87 %27 %27 %27
+ OpImageWrite %80 %85 %88
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %89 = OpVariable %25 Function
+ OpStore %89 %27
+ OpBranch %90
+ %90 = OpLabel
+ OpLoopMerge %92 %93 None
+ OpBranch %94
+ %94 = OpLabel
+ %95 = OpLoad %24 %89
+ %97 = OpSLessThan %35 %95 %96
+ OpBranchConditional %97 %91 %92
+ %91 = OpLabel
+ %98 = OpLoad %24 %89
+ %99 = OpIEqual %35 %98 %27
+ OpSelectionMerge %101 None
+ OpBranchConditional %99 %100 %109
+ %100 = OpLabel
+ %102 = OpLoad %61 %63
+ %104 = OpImageRead %71 %102 %103
+ %105 = OpCompositeExtract %24 %104 0
+ %106 = OpConvertSToF %38 %105
+ %108 = OpAccessChain %107 %42 %59 %16
+ OpStore %108 %106
+ OpBranch %101
+ %109 = OpLabel
+ %111 = OpLoad %13 %110
+ %112 = OpConvertUToF %39 %111
+ %114 = OpCompositeExtract %38 %112 0
+ %115 = OpCompositeExtract %38 %112 1
+ %116 = OpCompositeConstruct %113 %114 %115
+ %117 = OpAccessChain %107 %42 %59 %21
+ %118 = OpCompositeExtract %38 %116 0
+ OpStore %117 %118
+ %119 = OpAccessChain %107 %42 %59 %76
+ %120 = OpCompositeExtract %38 %116 1
+ OpStore %119 %120
+ OpBranch %101
+ %101 = OpLabel
+ OpBranch %93
+ %93 = OpLabel
+ %121 = OpLoad %24 %89
+ %122 = OpIAdd %24 %121 %59
+ OpStore %89 %122
+ OpBranch %90
+ %92 = OpLabel
+ OpReturn
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/large_functions_large_diffs_src.spvasm b/test/diff/diff_files/large_functions_large_diffs_src.spvasm
new file mode 100644
index 00000000..8f0aa614
--- /dev/null
+++ b/test/diff/diff_files/large_functions_large_diffs_src.spvasm
@@ -0,0 +1,230 @@
+;; Test where src and dst have a few large functions with large differences.
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main" %15
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "f1("
+ OpName %8 "f2("
+ OpName %12 "x"
+ OpName %15 "gl_LocalInvocationID"
+ OpName %20 "y"
+ OpName %27 "image"
+ OpName %44 "sum"
+ OpName %46 "i"
+ OpName %56 "j"
+ OpName %80 "BufferOut"
+ OpMemberName %80 0 "o_uv4"
+ OpMemberName %80 1 "o_v3"
+ OpMemberName %80 2 "o_i"
+ OpName %82 ""
+ OpName %88 "BufferIn"
+ OpMemberName %88 0 "i_u"
+ OpMemberName %88 1 "i_v4"
+ OpMemberName %88 2 "i_f"
+ OpName %90 ""
+ OpName %101 "i"
+ OpName %128 "image2"
+ OpDecorate %15 BuiltIn LocalInvocationId
+ OpDecorate %27 DescriptorSet 0
+ OpDecorate %27 Binding 2
+ OpMemberDecorate %80 0 Offset 0
+ OpMemberDecorate %80 1 Offset 16
+ OpMemberDecorate %80 2 Offset 28
+ OpDecorate %80 BufferBlock
+ OpDecorate %82 DescriptorSet 0
+ OpDecorate %82 Binding 1
+ OpMemberDecorate %88 0 Offset 0
+ OpMemberDecorate %88 1 RowMajor
+ OpMemberDecorate %88 1 Offset 16
+ OpMemberDecorate %88 1 MatrixStride 16
+ OpMemberDecorate %88 2 Offset 80
+ OpDecorate %88 Block
+ OpDecorate %90 DescriptorSet 0
+ OpDecorate %90 Binding 0
+ OpDecorate %128 DescriptorSet 0
+ OpDecorate %128 Binding 3
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpTypePointer Function %10
+ %13 = OpTypeVector %10 3
+ %14 = OpTypePointer Input %13
+ %15 = OpVariable %14 Input
+ %16 = OpConstant %10 0
+ %17 = OpTypePointer Input %10
+ %21 = OpConstant %10 1
+ %24 = OpTypeInt 32 1
+ %25 = OpTypeImage %24 2D 0 0 0 2 R32i
+ %26 = OpTypePointer UniformConstant %25
+ %27 = OpVariable %26 UniformConstant
+ %29 = OpTypeVector %10 2
+ %32 = OpTypeVector %24 2
+ %38 = OpTypeVector %24 4
+ %40 = OpConstant %10 2
+ %41 = OpConstant %10 3400
+ %42 = OpConstant %10 264
+ %43 = OpTypePointer Function %24
+ %45 = OpConstant %24 0
+ %53 = OpConstant %24 2
+ %54 = OpTypeBool
+ %73 = OpConstant %24 1
+ %77 = OpTypeVector %10 4
+ %78 = OpTypeFloat 32
+ %79 = OpTypeVector %78 3
+ %80 = OpTypeStruct %77 %79 %24
+ %81 = OpTypePointer Uniform %80
+ %82 = OpVariable %81 Uniform
+ %84 = OpTypePointer Uniform %24
+ %86 = OpTypeVector %78 4
+ %87 = OpTypeMatrix %86 4
+ %88 = OpTypeStruct %10 %87 %78
+ %89 = OpTypePointer Uniform %88
+ %90 = OpVariable %89 Uniform
+ %91 = OpTypePointer Uniform %87
+ %94 = OpTypePointer Uniform %77
+ %108 = OpConstant %24 3
+ %110 = OpTypePointer Uniform %79
+ %113 = OpTypePointer Uniform %78
+ %128 = OpVariable %26 UniformConstant
+ %130 = OpConstantComposite %32 %45 %45
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %136 = OpFunctionCall %2 %6
+ %137 = OpFunctionCall %2 %8
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %12 = OpVariable %11 Function
+ %20 = OpVariable %11 Function
+ %44 = OpVariable %43 Function
+ %46 = OpVariable %43 Function
+ %56 = OpVariable %43 Function
+ %18 = OpAccessChain %17 %15 %16
+ %19 = OpLoad %10 %18
+ OpStore %12 %19
+ %22 = OpAccessChain %17 %15 %21
+ %23 = OpLoad %10 %22
+ OpStore %20 %23
+ %28 = OpLoad %25 %27
+ %30 = OpLoad %13 %15
+ %31 = OpVectorShuffle %29 %30 %30 0 1
+ %33 = OpBitcast %32 %31
+ %34 = OpLoad %10 %12
+ %35 = OpLoad %10 %20
+ %36 = OpIAdd %10 %34 %35
+ %37 = OpBitcast %24 %36
+ %39 = OpCompositeConstruct %38 %37 %37 %37 %37
+ OpImageWrite %28 %33 %39
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ OpStore %44 %45
+ OpStore %46 %45
+ OpBranch %47
+ %47 = OpLabel
+ OpLoopMerge %49 %50 None
+ OpBranch %51
+ %51 = OpLabel
+ %52 = OpLoad %24 %46
+ %55 = OpSLessThan %54 %52 %53
+ OpBranchConditional %55 %48 %49
+ %48 = OpLabel
+ OpStore %56 %45
+ OpBranch %57
+ %57 = OpLabel
+ OpLoopMerge %59 %60 None
+ OpBranch %61
+ %61 = OpLabel
+ %62 = OpLoad %24 %56
+ %63 = OpSLessThan %54 %62 %53
+ OpBranchConditional %63 %58 %59
+ %58 = OpLabel
+ %64 = OpLoad %25 %27
+ %65 = OpLoad %24 %46
+ %66 = OpLoad %24 %56
+ %67 = OpCompositeConstruct %32 %65 %66
+ %68 = OpImageRead %38 %64 %67
+ %69 = OpCompositeExtract %24 %68 0
+ %70 = OpLoad %24 %44
+ %71 = OpIMul %24 %70 %69
+ OpStore %44 %71
+ OpBranch %60
+ %60 = OpLabel
+ %72 = OpLoad %24 %56
+ %74 = OpIAdd %24 %72 %73
+ OpStore %56 %74
+ OpBranch %57
+ %59 = OpLabel
+ OpBranch %50
+ %50 = OpLabel
+ %75 = OpLoad %24 %46
+ %76 = OpIAdd %24 %75 %73
+ OpStore %46 %76
+ OpBranch %47
+ %49 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %83 = OpLoad %24 %44
+ %85 = OpAccessChain %84 %82 %53
+ OpStore %85 %83
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %101 = OpVariable %43 Function
+ %92 = OpAccessChain %91 %90 %73
+ %93 = OpLoad %87 %92
+ %95 = OpAccessChain %94 %82 %45
+ %96 = OpLoad %77 %95
+ %97 = OpConvertUToF %86 %96
+ %98 = OpMatrixTimesVector %86 %93 %97
+ %99 = OpConvertFToU %77 %98
+ %100 = OpAccessChain %94 %82 %45
+ OpStore %100 %99
+ OpStore %101 %45
+ OpBranch %102
+ %102 = OpLabel
+ OpLoopMerge %104 %105 None
+ OpBranch %106
+ %106 = OpLabel
+ %107 = OpLoad %24 %101
+ %109 = OpSLessThan %54 %107 %108
+ OpBranchConditional %109 %103 %104
+ %103 = OpLabel
+ %111 = OpAccessChain %110 %82 %73
+ %112 = OpLoad %79 %111
+ %114 = OpAccessChain %113 %90 %53
+ %115 = OpLoad %78 %114
+ %116 = OpVectorTimesScalar %79 %112 %115
+ %117 = OpConvertFToU %13 %116
+ %118 = OpCompositeExtract %10 %117 0
+ %119 = OpCompositeExtract %10 %117 1
+ %120 = OpCompositeExtract %10 %117 2
+ %121 = OpCompositeConstruct %77 %118 %119 %120 %16
+ %122 = OpAccessChain %94 %82 %45
+ %123 = OpLoad %77 %122
+ %124 = OpIAdd %77 %123 %121
+ %125 = OpAccessChain %94 %82 %45
+ OpStore %125 %124
+ OpBranch %105
+ %105 = OpLabel
+ %126 = OpLoad %24 %101
+ %127 = OpIAdd %24 %126 %73
+ OpStore %101 %127
+ OpBranch %102
+ %104 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %129 = OpLoad %25 %128
+ %131 = OpImageRead %38 %129 %130
+ %132 = OpCompositeExtract %24 %131 0
+ %133 = OpConvertSToF %78 %132
+ %134 = OpCompositeConstruct %79 %133 %133 %133
+ %135 = OpAccessChain %110 %82 %73
+ OpStore %135 %134
+ OpReturn
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/large_functions_small_diffs_autogen.cpp b/test/diff/diff_files/large_functions_small_diffs_autogen.cpp
new file mode 100644
index 00000000..02838d9a
--- /dev/null
+++ b/test/diff/diff_files/large_functions_small_diffs_autogen.cpp
@@ -0,0 +1,1364 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Test where src and dst have a few large functions with small differences.
+constexpr char kSrc[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main" %15
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "f1("
+ OpName %8 "f2("
+ OpName %12 "x"
+ OpName %15 "gl_LocalInvocationID"
+ OpName %20 "y"
+ OpName %27 "image"
+ OpName %44 "sum"
+ OpName %46 "i"
+ OpName %56 "j"
+ OpName %80 "BufferOut"
+ OpMemberName %80 0 "o_uv4"
+ OpMemberName %80 1 "o_v3"
+ OpMemberName %80 2 "o_i"
+ OpName %82 ""
+ OpName %88 "BufferIn"
+ OpMemberName %88 0 "i_u"
+ OpMemberName %88 1 "i_v4"
+ OpMemberName %88 2 "i_f"
+ OpName %90 ""
+ OpName %101 "i"
+ OpDecorate %15 BuiltIn LocalInvocationId
+ OpDecorate %27 DescriptorSet 0
+ OpDecorate %27 Binding 2
+ OpMemberDecorate %80 0 Offset 0
+ OpMemberDecorate %80 1 Offset 16
+ OpMemberDecorate %80 2 Offset 28
+ OpDecorate %80 BufferBlock
+ OpDecorate %82 DescriptorSet 0
+ OpDecorate %82 Binding 1
+ OpMemberDecorate %88 0 Offset 0
+ OpMemberDecorate %88 1 RowMajor
+ OpMemberDecorate %88 1 Offset 16
+ OpMemberDecorate %88 1 MatrixStride 16
+ OpMemberDecorate %88 2 Offset 80
+ OpDecorate %88 Block
+ OpDecorate %90 DescriptorSet 0
+ OpDecorate %90 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpTypePointer Function %10
+ %13 = OpTypeVector %10 3
+ %14 = OpTypePointer Input %13
+ %15 = OpVariable %14 Input
+ %16 = OpConstant %10 0
+ %17 = OpTypePointer Input %10
+ %21 = OpConstant %10 1
+ %24 = OpTypeInt 32 1
+ %25 = OpTypeImage %24 2D 0 0 0 2 R32i
+ %26 = OpTypePointer UniformConstant %25
+ %27 = OpVariable %26 UniformConstant
+ %29 = OpTypeVector %10 2
+ %32 = OpTypeVector %24 2
+ %38 = OpTypeVector %24 4
+ %40 = OpConstant %10 2
+ %41 = OpConstant %10 3400
+ %42 = OpConstant %10 264
+ %43 = OpTypePointer Function %24
+ %45 = OpConstant %24 0
+ %53 = OpConstant %24 2
+ %54 = OpTypeBool
+ %73 = OpConstant %24 1
+ %77 = OpTypeVector %10 4
+ %78 = OpTypeFloat 32
+ %79 = OpTypeVector %78 3
+ %80 = OpTypeStruct %77 %79 %24
+ %81 = OpTypePointer Uniform %80
+ %82 = OpVariable %81 Uniform
+ %84 = OpTypePointer Uniform %24
+ %86 = OpTypeVector %78 4
+ %87 = OpTypeMatrix %86 4
+ %88 = OpTypeStruct %10 %87 %78
+ %89 = OpTypePointer Uniform %88
+ %90 = OpVariable %89 Uniform
+ %91 = OpTypePointer Uniform %87
+ %94 = OpTypePointer Uniform %77
+ %108 = OpConstant %24 3
+ %110 = OpTypePointer Uniform %79
+ %113 = OpTypePointer Uniform %78
+ %129 = OpConstantComposite %32 %45 %45
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %135 = OpFunctionCall %2 %6
+ %136 = OpFunctionCall %2 %8
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %12 = OpVariable %11 Function
+ %20 = OpVariable %11 Function
+ %44 = OpVariable %43 Function
+ %46 = OpVariable %43 Function
+ %56 = OpVariable %43 Function
+ %18 = OpAccessChain %17 %15 %16
+ %19 = OpLoad %10 %18
+ OpStore %12 %19
+ %22 = OpAccessChain %17 %15 %21
+ %23 = OpLoad %10 %22
+ OpStore %20 %23
+ %28 = OpLoad %25 %27
+ %30 = OpLoad %13 %15
+ %31 = OpVectorShuffle %29 %30 %30 0 1
+ %33 = OpBitcast %32 %31
+ %34 = OpLoad %10 %12
+ %35 = OpLoad %10 %20
+ %36 = OpIAdd %10 %34 %35
+ %37 = OpBitcast %24 %36
+ %39 = OpCompositeConstruct %38 %37 %37 %37 %37
+ OpImageWrite %28 %33 %39
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ OpStore %44 %45
+ OpStore %46 %45
+ OpBranch %47
+ %47 = OpLabel
+ OpLoopMerge %49 %50 None
+ OpBranch %51
+ %51 = OpLabel
+ %52 = OpLoad %24 %46
+ %55 = OpSLessThan %54 %52 %53
+ OpBranchConditional %55 %48 %49
+ %48 = OpLabel
+ OpStore %56 %45
+ OpBranch %57
+ %57 = OpLabel
+ OpLoopMerge %59 %60 None
+ OpBranch %61
+ %61 = OpLabel
+ %62 = OpLoad %24 %56
+ %63 = OpSLessThan %54 %62 %53
+ OpBranchConditional %63 %58 %59
+ %58 = OpLabel
+ %64 = OpLoad %25 %27
+ %65 = OpLoad %24 %46
+ %66 = OpLoad %24 %56
+ %67 = OpCompositeConstruct %32 %65 %66
+ %68 = OpImageRead %38 %64 %67
+ %69 = OpCompositeExtract %24 %68 0
+ %70 = OpLoad %24 %44
+ %71 = OpIAdd %24 %70 %69
+ OpStore %44 %71
+ OpBranch %60
+ %60 = OpLabel
+ %72 = OpLoad %24 %56
+ %74 = OpIAdd %24 %72 %73
+ OpStore %56 %74
+ OpBranch %57
+ %59 = OpLabel
+ OpBranch %50
+ %50 = OpLabel
+ %75 = OpLoad %24 %46
+ %76 = OpIAdd %24 %75 %73
+ OpStore %46 %76
+ OpBranch %47
+ %49 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %83 = OpLoad %24 %44
+ %85 = OpAccessChain %84 %82 %53
+ OpStore %85 %83
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %101 = OpVariable %43 Function
+ %92 = OpAccessChain %91 %90 %73
+ %93 = OpLoad %87 %92
+ %95 = OpAccessChain %94 %82 %45
+ %96 = OpLoad %77 %95
+ %97 = OpConvertUToF %86 %96
+ %98 = OpMatrixTimesVector %86 %93 %97
+ %99 = OpConvertFToU %77 %98
+ %100 = OpAccessChain %94 %82 %45
+ OpStore %100 %99
+ OpStore %101 %45
+ OpBranch %102
+ %102 = OpLabel
+ OpLoopMerge %104 %105 None
+ OpBranch %106
+ %106 = OpLabel
+ %107 = OpLoad %24 %101
+ %109 = OpSLessThan %54 %107 %108
+ OpBranchConditional %109 %103 %104
+ %103 = OpLabel
+ %111 = OpAccessChain %110 %82 %73
+ %112 = OpLoad %79 %111
+ %114 = OpAccessChain %113 %90 %53
+ %115 = OpLoad %78 %114
+ %116 = OpVectorTimesScalar %79 %112 %115
+ %117 = OpConvertFToU %13 %116
+ %118 = OpCompositeExtract %10 %117 0
+ %119 = OpCompositeExtract %10 %117 1
+ %120 = OpCompositeExtract %10 %117 2
+ %121 = OpCompositeConstruct %77 %118 %119 %120 %16
+ %122 = OpAccessChain %94 %82 %45
+ %123 = OpLoad %77 %122
+ %124 = OpIAdd %77 %123 %121
+ %125 = OpAccessChain %94 %82 %45
+ OpStore %125 %124
+ OpBranch %105
+ %105 = OpLabel
+ %126 = OpLoad %24 %101
+ %127 = OpIAdd %24 %126 %73
+ OpStore %101 %127
+ OpBranch %102
+ %104 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %128 = OpLoad %25 %27
+ %130 = OpImageRead %38 %128 %129
+ %131 = OpCompositeExtract %24 %130 0
+ %132 = OpConvertSToF %78 %131
+ %133 = OpCompositeConstruct %79 %132 %132 %132
+ %134 = OpAccessChain %110 %82 %73
+ OpStore %134 %133
+ OpReturn
+ OpFunctionEnd
+)";
+constexpr char kDst[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main" %15
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "f1("
+ OpName %8 "f2("
+ OpName %12 "x"
+ OpName %15 "gl_LocalInvocationID"
+ OpName %20 "y"
+ OpName %27 "image"
+ OpName %44 "sum"
+ OpName %46 "i"
+ OpName %56 "j"
+ OpName %80 "BufferOut"
+ OpMemberName %80 0 "o_uv4"
+ OpMemberName %80 1 "o_v3"
+ OpMemberName %80 2 "o_i"
+ OpName %82 ""
+ OpName %88 "BufferIn"
+ OpMemberName %88 0 "i_u"
+ OpMemberName %88 1 "i_v4"
+ OpMemberName %88 2 "i_f"
+ OpName %90 ""
+ OpName %101 "i"
+ OpName %128 "image2"
+ OpDecorate %15 BuiltIn LocalInvocationId
+ OpDecorate %27 DescriptorSet 0
+ OpDecorate %27 Binding 2
+ OpMemberDecorate %80 0 Offset 0
+ OpMemberDecorate %80 1 Offset 16
+ OpMemberDecorate %80 2 Offset 28
+ OpDecorate %80 BufferBlock
+ OpDecorate %82 DescriptorSet 0
+ OpDecorate %82 Binding 1
+ OpMemberDecorate %88 0 Offset 0
+ OpMemberDecorate %88 1 RowMajor
+ OpMemberDecorate %88 1 Offset 16
+ OpMemberDecorate %88 1 MatrixStride 16
+ OpMemberDecorate %88 2 Offset 80
+ OpDecorate %88 Block
+ OpDecorate %90 DescriptorSet 0
+ OpDecorate %90 Binding 0
+ OpDecorate %128 DescriptorSet 0
+ OpDecorate %128 Binding 3
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpTypePointer Function %10
+ %13 = OpTypeVector %10 3
+ %14 = OpTypePointer Input %13
+ %15 = OpVariable %14 Input
+ %16 = OpConstant %10 0
+ %17 = OpTypePointer Input %10
+ %21 = OpConstant %10 1
+ %24 = OpTypeInt 32 1
+ %25 = OpTypeImage %24 2D 0 0 0 2 R32i
+ %26 = OpTypePointer UniformConstant %25
+ %27 = OpVariable %26 UniformConstant
+ %29 = OpTypeVector %10 2
+ %32 = OpTypeVector %24 2
+ %38 = OpTypeVector %24 4
+ %40 = OpConstant %10 2
+ %41 = OpConstant %10 3400
+ %42 = OpConstant %10 264
+ %43 = OpTypePointer Function %24
+ %45 = OpConstant %24 0
+ %53 = OpConstant %24 2
+ %54 = OpTypeBool
+ %73 = OpConstant %24 1
+ %77 = OpTypeVector %10 4
+ %78 = OpTypeFloat 32
+ %79 = OpTypeVector %78 3
+ %80 = OpTypeStruct %77 %79 %24
+ %81 = OpTypePointer Uniform %80
+ %82 = OpVariable %81 Uniform
+ %84 = OpTypePointer Uniform %24
+ %86 = OpTypeVector %78 4
+ %87 = OpTypeMatrix %86 4
+ %88 = OpTypeStruct %10 %87 %78
+ %89 = OpTypePointer Uniform %88
+ %90 = OpVariable %89 Uniform
+ %91 = OpTypePointer Uniform %87
+ %94 = OpTypePointer Uniform %77
+ %108 = OpConstant %24 3
+ %110 = OpTypePointer Uniform %79
+ %113 = OpTypePointer Uniform %78
+ %128 = OpVariable %26 UniformConstant
+ %130 = OpConstantComposite %32 %45 %45
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %136 = OpFunctionCall %2 %6
+ %137 = OpFunctionCall %2 %8
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %12 = OpVariable %11 Function
+ %20 = OpVariable %11 Function
+ %44 = OpVariable %43 Function
+ %46 = OpVariable %43 Function
+ %56 = OpVariable %43 Function
+ %18 = OpAccessChain %17 %15 %16
+ %19 = OpLoad %10 %18
+ OpStore %12 %19
+ %22 = OpAccessChain %17 %15 %21
+ %23 = OpLoad %10 %22
+ OpStore %20 %23
+ %28 = OpLoad %25 %27
+ %30 = OpLoad %13 %15
+ %31 = OpVectorShuffle %29 %30 %30 0 1
+ %33 = OpBitcast %32 %31
+ %34 = OpLoad %10 %12
+ %35 = OpLoad %10 %20
+ %36 = OpIAdd %10 %34 %35
+ %37 = OpBitcast %24 %36
+ %39 = OpCompositeConstruct %38 %37 %37 %37 %37
+ OpImageWrite %28 %33 %39
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ OpStore %44 %45
+ OpStore %46 %45
+ OpBranch %47
+ %47 = OpLabel
+ OpLoopMerge %49 %50 None
+ OpBranch %51
+ %51 = OpLabel
+ %52 = OpLoad %24 %46
+ %55 = OpSLessThan %54 %52 %53
+ OpBranchConditional %55 %48 %49
+ %48 = OpLabel
+ OpStore %56 %45
+ OpBranch %57
+ %57 = OpLabel
+ OpLoopMerge %59 %60 None
+ OpBranch %61
+ %61 = OpLabel
+ %62 = OpLoad %24 %56
+ %63 = OpSLessThan %54 %62 %53
+ OpBranchConditional %63 %58 %59
+ %58 = OpLabel
+ %64 = OpLoad %25 %27
+ %65 = OpLoad %24 %46
+ %66 = OpLoad %24 %56
+ %67 = OpCompositeConstruct %32 %65 %66
+ %68 = OpImageRead %38 %64 %67
+ %69 = OpCompositeExtract %24 %68 0
+ %70 = OpLoad %24 %44
+ %71 = OpIMul %24 %70 %69
+ OpStore %44 %71
+ OpBranch %60
+ %60 = OpLabel
+ %72 = OpLoad %24 %56
+ %74 = OpIAdd %24 %72 %73
+ OpStore %56 %74
+ OpBranch %57
+ %59 = OpLabel
+ OpBranch %50
+ %50 = OpLabel
+ %75 = OpLoad %24 %46
+ %76 = OpIAdd %24 %75 %73
+ OpStore %46 %76
+ OpBranch %47
+ %49 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %83 = OpLoad %24 %44
+ %85 = OpAccessChain %84 %82 %53
+ OpStore %85 %83
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %101 = OpVariable %43 Function
+ %92 = OpAccessChain %91 %90 %73
+ %93 = OpLoad %87 %92
+ %95 = OpAccessChain %94 %82 %45
+ %96 = OpLoad %77 %95
+ %97 = OpConvertUToF %86 %96
+ %98 = OpMatrixTimesVector %86 %93 %97
+ %99 = OpConvertFToU %77 %98
+ %100 = OpAccessChain %94 %82 %45
+ OpStore %100 %99
+ OpStore %101 %45
+ OpBranch %102
+ %102 = OpLabel
+ OpLoopMerge %104 %105 None
+ OpBranch %106
+ %106 = OpLabel
+ %107 = OpLoad %24 %101
+ %109 = OpSLessThan %54 %107 %108
+ OpBranchConditional %109 %103 %104
+ %103 = OpLabel
+ %111 = OpAccessChain %110 %82 %73
+ %112 = OpLoad %79 %111
+ %114 = OpAccessChain %113 %90 %53
+ %115 = OpLoad %78 %114
+ %116 = OpVectorTimesScalar %79 %112 %115
+ %117 = OpConvertFToU %13 %116
+ %118 = OpCompositeExtract %10 %117 0
+ %119 = OpCompositeExtract %10 %117 1
+ %120 = OpCompositeExtract %10 %117 2
+ %121 = OpCompositeConstruct %77 %118 %119 %120 %16
+ %122 = OpAccessChain %94 %82 %45
+ %123 = OpLoad %77 %122
+ %124 = OpIAdd %77 %123 %121
+ %125 = OpAccessChain %94 %82 %45
+ OpStore %125 %124
+ OpBranch %105
+ %105 = OpLabel
+ %126 = OpLoad %24 %101
+ %127 = OpIAdd %24 %126 %73
+ OpStore %101 %127
+ OpBranch %102
+ %104 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %129 = OpLoad %25 %128
+ %131 = OpImageRead %38 %129 %130
+ %132 = OpCompositeExtract %24 %131 0
+ %133 = OpConvertSToF %78 %132
+ %134 = OpCompositeConstruct %79 %133 %133 %133
+ %135 = OpAccessChain %110 %82 %73
+ OpStore %135 %134
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+TEST(DiffTest, LargeFunctionsSmallDiffs) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 137
++; Bound: 140
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main" %15
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "f1("
+ OpName %8 "f2("
+ OpName %12 "x"
+ OpName %15 "gl_LocalInvocationID"
+ OpName %20 "y"
+ OpName %27 "image"
+ OpName %44 "sum"
+ OpName %46 "i"
+ OpName %56 "j"
+ OpName %80 "BufferOut"
+ OpMemberName %80 0 "o_uv4"
+ OpMemberName %80 1 "o_v3"
+ OpMemberName %80 2 "o_i"
+ OpName %82 ""
+ OpName %88 "BufferIn"
+ OpMemberName %88 0 "i_u"
+ OpMemberName %88 1 "i_v4"
+ OpMemberName %88 2 "i_f"
+ OpName %90 ""
+ OpName %101 "i"
++OpName %138 "image2"
+ OpDecorate %15 BuiltIn LocalInvocationId
+ OpDecorate %27 DescriptorSet 0
+ OpDecorate %27 Binding 2
+ OpMemberDecorate %80 0 Offset 0
+ OpMemberDecorate %80 1 Offset 16
+ OpMemberDecorate %80 2 Offset 28
+ OpDecorate %80 BufferBlock
+ OpDecorate %82 DescriptorSet 0
+ OpDecorate %82 Binding 1
+ OpMemberDecorate %88 0 Offset 0
+ OpMemberDecorate %88 1 RowMajor
+ OpMemberDecorate %88 1 Offset 16
+ OpMemberDecorate %88 1 MatrixStride 16
+ OpMemberDecorate %88 2 Offset 80
+ OpDecorate %88 Block
+ OpDecorate %90 DescriptorSet 0
+ OpDecorate %90 Binding 0
++OpDecorate %138 DescriptorSet 0
++OpDecorate %138 Binding 3
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpTypePointer Function %10
+ %13 = OpTypeVector %10 3
+ %14 = OpTypePointer Input %13
+ %15 = OpVariable %14 Input
+ %16 = OpConstant %10 0
+ %17 = OpTypePointer Input %10
+ %21 = OpConstant %10 1
+ %24 = OpTypeInt 32 1
+ %25 = OpTypeImage %24 2D 0 0 0 2 R32i
+ %26 = OpTypePointer UniformConstant %25
+ %27 = OpVariable %26 UniformConstant
+ %29 = OpTypeVector %10 2
+ %32 = OpTypeVector %24 2
+ %38 = OpTypeVector %24 4
+ %40 = OpConstant %10 2
+ %41 = OpConstant %10 3400
+ %42 = OpConstant %10 264
+ %43 = OpTypePointer Function %24
+ %45 = OpConstant %24 0
+ %53 = OpConstant %24 2
+ %54 = OpTypeBool
+ %73 = OpConstant %24 1
+ %77 = OpTypeVector %10 4
+ %78 = OpTypeFloat 32
+ %79 = OpTypeVector %78 3
+ %80 = OpTypeStruct %77 %79 %24
+ %81 = OpTypePointer Uniform %80
+ %82 = OpVariable %81 Uniform
+ %84 = OpTypePointer Uniform %24
+ %86 = OpTypeVector %78 4
+ %87 = OpTypeMatrix %86 4
+ %88 = OpTypeStruct %10 %87 %78
+ %89 = OpTypePointer Uniform %88
+ %90 = OpVariable %89 Uniform
+ %91 = OpTypePointer Uniform %87
+ %94 = OpTypePointer Uniform %77
+ %108 = OpConstant %24 3
+ %110 = OpTypePointer Uniform %79
+ %113 = OpTypePointer Uniform %78
++%138 = OpVariable %26 UniformConstant
+ %129 = OpConstantComposite %32 %45 %45
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %135 = OpFunctionCall %2 %6
+ %136 = OpFunctionCall %2 %8
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %12 = OpVariable %11 Function
+ %20 = OpVariable %11 Function
+ %44 = OpVariable %43 Function
+ %46 = OpVariable %43 Function
+ %56 = OpVariable %43 Function
+ %18 = OpAccessChain %17 %15 %16
+ %19 = OpLoad %10 %18
+ OpStore %12 %19
+ %22 = OpAccessChain %17 %15 %21
+ %23 = OpLoad %10 %22
+ OpStore %20 %23
+ %28 = OpLoad %25 %27
+ %30 = OpLoad %13 %15
+ %31 = OpVectorShuffle %29 %30 %30 0 1
+ %33 = OpBitcast %32 %31
+ %34 = OpLoad %10 %12
+ %35 = OpLoad %10 %20
+ %36 = OpIAdd %10 %34 %35
+ %37 = OpBitcast %24 %36
+ %39 = OpCompositeConstruct %38 %37 %37 %37 %37
+ OpImageWrite %28 %33 %39
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ OpStore %44 %45
+ OpStore %46 %45
+ OpBranch %47
+ %47 = OpLabel
+ OpLoopMerge %49 %50 None
+ OpBranch %51
+ %51 = OpLabel
+ %52 = OpLoad %24 %46
+ %55 = OpSLessThan %54 %52 %53
+ OpBranchConditional %55 %48 %49
+ %48 = OpLabel
+ OpStore %56 %45
+ OpBranch %57
+ %57 = OpLabel
+ OpLoopMerge %59 %60 None
+ OpBranch %61
+ %61 = OpLabel
+ %62 = OpLoad %24 %56
+ %63 = OpSLessThan %54 %62 %53
+ OpBranchConditional %63 %58 %59
+ %58 = OpLabel
+ %64 = OpLoad %25 %27
+ %65 = OpLoad %24 %46
+ %66 = OpLoad %24 %56
+ %67 = OpCompositeConstruct %32 %65 %66
+ %68 = OpImageRead %38 %64 %67
+ %69 = OpCompositeExtract %24 %68 0
+ %70 = OpLoad %24 %44
+-%71 = OpIAdd %24 %70 %69
++%137 = OpIMul %24 %70 %69
+-OpStore %44 %71
++OpStore %44 %137
+ OpBranch %60
+ %60 = OpLabel
+ %72 = OpLoad %24 %56
+ %74 = OpIAdd %24 %72 %73
+ OpStore %56 %74
+ OpBranch %57
+ %59 = OpLabel
+ OpBranch %50
+ %50 = OpLabel
+ %75 = OpLoad %24 %46
+ %76 = OpIAdd %24 %75 %73
+ OpStore %46 %76
+ OpBranch %47
+ %49 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %83 = OpLoad %24 %44
+ %85 = OpAccessChain %84 %82 %53
+ OpStore %85 %83
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %101 = OpVariable %43 Function
+ %92 = OpAccessChain %91 %90 %73
+ %93 = OpLoad %87 %92
+ %95 = OpAccessChain %94 %82 %45
+ %96 = OpLoad %77 %95
+ %97 = OpConvertUToF %86 %96
+ %98 = OpMatrixTimesVector %86 %93 %97
+ %99 = OpConvertFToU %77 %98
+ %100 = OpAccessChain %94 %82 %45
+ OpStore %100 %99
+ OpStore %101 %45
+ OpBranch %102
+ %102 = OpLabel
+ OpLoopMerge %104 %105 None
+ OpBranch %106
+ %106 = OpLabel
+ %107 = OpLoad %24 %101
+ %109 = OpSLessThan %54 %107 %108
+ OpBranchConditional %109 %103 %104
+ %103 = OpLabel
+ %111 = OpAccessChain %110 %82 %73
+ %112 = OpLoad %79 %111
+ %114 = OpAccessChain %113 %90 %53
+ %115 = OpLoad %78 %114
+ %116 = OpVectorTimesScalar %79 %112 %115
+ %117 = OpConvertFToU %13 %116
+ %118 = OpCompositeExtract %10 %117 0
+ %119 = OpCompositeExtract %10 %117 1
+ %120 = OpCompositeExtract %10 %117 2
+ %121 = OpCompositeConstruct %77 %118 %119 %120 %16
+ %122 = OpAccessChain %94 %82 %45
+ %123 = OpLoad %77 %122
+ %124 = OpIAdd %77 %123 %121
+ %125 = OpAccessChain %94 %82 %45
+ OpStore %125 %124
+ OpBranch %105
+ %105 = OpLabel
+ %126 = OpLoad %24 %101
+ %127 = OpIAdd %24 %126 %73
+ OpStore %101 %127
+ OpBranch %102
+ %104 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+-%128 = OpLoad %25 %27
++%139 = OpLoad %25 %138
+-%130 = OpImageRead %38 %128 %129
++%130 = OpImageRead %38 %139 %129
+ %131 = OpCompositeExtract %24 %130 0
+ %132 = OpConvertSToF %78 %131
+ %133 = OpCompositeConstruct %79 %132 %132 %132
+ %134 = OpAccessChain %110 %82 %73
+ OpStore %134 %133
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, LargeFunctionsSmallDiffsNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main" %15
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpDecorate %15 BuiltIn LocalInvocationId
+ OpDecorate %27 DescriptorSet 0
+ OpDecorate %27 Binding 2
+ OpMemberDecorate %80 0 Offset 0
+ OpMemberDecorate %80 1 Offset 16
+ OpMemberDecorate %80 2 Offset 28
+ OpDecorate %80 BufferBlock
+ OpDecorate %82 DescriptorSet 0
+ OpDecorate %82 Binding 1
+ OpMemberDecorate %88 0 Offset 0
+ OpMemberDecorate %88 1 RowMajor
+ OpMemberDecorate %88 1 Offset 16
+ OpMemberDecorate %88 1 MatrixStride 16
+ OpMemberDecorate %88 2 Offset 80
+ OpDecorate %88 Block
+ OpDecorate %90 DescriptorSet 0
+ OpDecorate %90 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpTypePointer Function %10
+ %13 = OpTypeVector %10 3
+ %14 = OpTypePointer Input %13
+ %15 = OpVariable %14 Input
+ %16 = OpConstant %10 0
+ %17 = OpTypePointer Input %10
+ %21 = OpConstant %10 1
+ %24 = OpTypeInt 32 1
+ %25 = OpTypeImage %24 2D 0 0 0 2 R32i
+ %26 = OpTypePointer UniformConstant %25
+ %27 = OpVariable %26 UniformConstant
+ %29 = OpTypeVector %10 2
+ %32 = OpTypeVector %24 2
+ %38 = OpTypeVector %24 4
+ %40 = OpConstant %10 2
+ %41 = OpConstant %10 3400
+ %42 = OpConstant %10 264
+ %43 = OpTypePointer Function %24
+ %45 = OpConstant %24 0
+ %53 = OpConstant %24 2
+ %54 = OpTypeBool
+ %73 = OpConstant %24 1
+ %77 = OpTypeVector %10 4
+ %78 = OpTypeFloat 32
+ %79 = OpTypeVector %78 3
+ %80 = OpTypeStruct %77 %79 %24
+ %81 = OpTypePointer Uniform %80
+ %82 = OpVariable %81 Uniform
+ %84 = OpTypePointer Uniform %24
+ %86 = OpTypeVector %78 4
+ %87 = OpTypeMatrix %86 4
+ %88 = OpTypeStruct %10 %87 %78
+ %89 = OpTypePointer Uniform %88
+ %90 = OpVariable %89 Uniform
+ %91 = OpTypePointer Uniform %87
+ %94 = OpTypePointer Uniform %77
+ %108 = OpConstant %24 3
+ %110 = OpTypePointer Uniform %79
+ %113 = OpTypePointer Uniform %78
+ %129 = OpConstantComposite %32 %45 %45
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %135 = OpFunctionCall %2 %6
+ %136 = OpFunctionCall %2 %8
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %12 = OpVariable %11 Function
+ %20 = OpVariable %11 Function
+ %44 = OpVariable %43 Function
+ %46 = OpVariable %43 Function
+ %56 = OpVariable %43 Function
+ %18 = OpAccessChain %17 %15 %16
+ %19 = OpLoad %10 %18
+ OpStore %12 %19
+ %22 = OpAccessChain %17 %15 %21
+ %23 = OpLoad %10 %22
+ OpStore %20 %23
+ %28 = OpLoad %25 %27
+ %30 = OpLoad %13 %15
+ %31 = OpVectorShuffle %29 %30 %30 0 1
+ %33 = OpBitcast %32 %31
+ %34 = OpLoad %10 %12
+ %35 = OpLoad %10 %20
+ %36 = OpIAdd %10 %34 %35
+ %37 = OpBitcast %24 %36
+ %39 = OpCompositeConstruct %38 %37 %37 %37 %37
+ OpImageWrite %28 %33 %39
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ OpStore %44 %45
+ OpStore %46 %45
+ OpBranch %47
+ %47 = OpLabel
+ OpLoopMerge %49 %50 None
+ OpBranch %51
+ %51 = OpLabel
+ %52 = OpLoad %24 %46
+ %55 = OpSLessThan %54 %52 %53
+ OpBranchConditional %55 %48 %49
+ %48 = OpLabel
+ OpStore %56 %45
+ OpBranch %57
+ %57 = OpLabel
+ OpLoopMerge %59 %60 None
+ OpBranch %61
+ %61 = OpLabel
+ %62 = OpLoad %24 %56
+ %63 = OpSLessThan %54 %62 %53
+ OpBranchConditional %63 %58 %59
+ %58 = OpLabel
+ %64 = OpLoad %25 %27
+ %65 = OpLoad %24 %46
+ %66 = OpLoad %24 %56
+ %67 = OpCompositeConstruct %32 %65 %66
+ %68 = OpImageRead %38 %64 %67
+ %69 = OpCompositeExtract %24 %68 0
+ %70 = OpLoad %24 %44
+ %71 = OpIAdd %24 %70 %69
+ OpStore %44 %71
+ OpBranch %60
+ %60 = OpLabel
+ %72 = OpLoad %24 %56
+ %74 = OpIAdd %24 %72 %73
+ OpStore %56 %74
+ OpBranch %57
+ %59 = OpLabel
+ OpBranch %50
+ %50 = OpLabel
+ %75 = OpLoad %24 %46
+ %76 = OpIAdd %24 %75 %73
+ OpStore %46 %76
+ OpBranch %47
+ %49 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %83 = OpLoad %24 %44
+ %85 = OpAccessChain %84 %82 %53
+ OpStore %85 %83
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %101 = OpVariable %43 Function
+ %92 = OpAccessChain %91 %90 %73
+ %93 = OpLoad %87 %92
+ %95 = OpAccessChain %94 %82 %45
+ %96 = OpLoad %77 %95
+ %97 = OpConvertUToF %86 %96
+ %98 = OpMatrixTimesVector %86 %93 %97
+ %99 = OpConvertFToU %77 %98
+ %100 = OpAccessChain %94 %82 %45
+ OpStore %100 %99
+ OpStore %101 %45
+ OpBranch %102
+ %102 = OpLabel
+ OpLoopMerge %104 %105 None
+ OpBranch %106
+ %106 = OpLabel
+ %107 = OpLoad %24 %101
+ %109 = OpSLessThan %54 %107 %108
+ OpBranchConditional %109 %103 %104
+ %103 = OpLabel
+ %111 = OpAccessChain %110 %82 %73
+ %112 = OpLoad %79 %111
+ %114 = OpAccessChain %113 %90 %53
+ %115 = OpLoad %78 %114
+ %116 = OpVectorTimesScalar %79 %112 %115
+ %117 = OpConvertFToU %13 %116
+ %118 = OpCompositeExtract %10 %117 0
+ %119 = OpCompositeExtract %10 %117 1
+ %120 = OpCompositeExtract %10 %117 2
+ %121 = OpCompositeConstruct %77 %118 %119 %120 %16
+ %122 = OpAccessChain %94 %82 %45
+ %123 = OpLoad %77 %122
+ %124 = OpIAdd %77 %123 %121
+ %125 = OpAccessChain %94 %82 %45
+ OpStore %125 %124
+ OpBranch %105
+ %105 = OpLabel
+ %126 = OpLoad %24 %101
+ %127 = OpIAdd %24 %126 %73
+ OpStore %101 %127
+ OpBranch %102
+ %104 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %128 = OpLoad %25 %27
+ %130 = OpImageRead %38 %128 %129
+ %131 = OpCompositeExtract %24 %130 0
+ %132 = OpConvertSToF %78 %131
+ %133 = OpCompositeConstruct %79 %132 %132 %132
+ %134 = OpAccessChain %110 %82 %73
+ OpStore %134 %133
+ OpReturn
+ OpFunctionEnd
+
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main" %15
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpDecorate %15 BuiltIn LocalInvocationId
+ OpDecorate %27 DescriptorSet 0
+ OpDecorate %27 Binding 2
+ OpMemberDecorate %80 0 Offset 0
+ OpMemberDecorate %80 1 Offset 16
+ OpMemberDecorate %80 2 Offset 28
+ OpDecorate %80 BufferBlock
+ OpDecorate %82 DescriptorSet 0
+ OpDecorate %82 Binding 1
+ OpMemberDecorate %88 0 Offset 0
+ OpMemberDecorate %88 1 RowMajor
+ OpMemberDecorate %88 1 Offset 16
+ OpMemberDecorate %88 1 MatrixStride 16
+ OpMemberDecorate %88 2 Offset 80
+ OpDecorate %88 Block
+ OpDecorate %90 DescriptorSet 0
+ OpDecorate %90 Binding 0
+ OpDecorate %128 DescriptorSet 0
+ OpDecorate %128 Binding 3
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpTypePointer Function %10
+ %13 = OpTypeVector %10 3
+ %14 = OpTypePointer Input %13
+ %15 = OpVariable %14 Input
+ %16 = OpConstant %10 0
+ %17 = OpTypePointer Input %10
+ %21 = OpConstant %10 1
+ %24 = OpTypeInt 32 1
+ %25 = OpTypeImage %24 2D 0 0 0 2 R32i
+ %26 = OpTypePointer UniformConstant %25
+ %27 = OpVariable %26 UniformConstant
+ %29 = OpTypeVector %10 2
+ %32 = OpTypeVector %24 2
+ %38 = OpTypeVector %24 4
+ %40 = OpConstant %10 2
+ %41 = OpConstant %10 3400
+ %42 = OpConstant %10 264
+ %43 = OpTypePointer Function %24
+ %45 = OpConstant %24 0
+ %53 = OpConstant %24 2
+ %54 = OpTypeBool
+ %73 = OpConstant %24 1
+ %77 = OpTypeVector %10 4
+ %78 = OpTypeFloat 32
+ %79 = OpTypeVector %78 3
+ %80 = OpTypeStruct %77 %79 %24
+ %81 = OpTypePointer Uniform %80
+ %82 = OpVariable %81 Uniform
+ %84 = OpTypePointer Uniform %24
+ %86 = OpTypeVector %78 4
+ %87 = OpTypeMatrix %86 4
+ %88 = OpTypeStruct %10 %87 %78
+ %89 = OpTypePointer Uniform %88
+ %90 = OpVariable %89 Uniform
+ %91 = OpTypePointer Uniform %87
+ %94 = OpTypePointer Uniform %77
+ %108 = OpConstant %24 3
+ %110 = OpTypePointer Uniform %79
+ %113 = OpTypePointer Uniform %78
+ %128 = OpVariable %26 UniformConstant
+ %130 = OpConstantComposite %32 %45 %45
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %136 = OpFunctionCall %2 %6
+ %137 = OpFunctionCall %2 %8
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %12 = OpVariable %11 Function
+ %20 = OpVariable %11 Function
+ %44 = OpVariable %43 Function
+ %46 = OpVariable %43 Function
+ %56 = OpVariable %43 Function
+ %18 = OpAccessChain %17 %15 %16
+ %19 = OpLoad %10 %18
+ OpStore %12 %19
+ %22 = OpAccessChain %17 %15 %21
+ %23 = OpLoad %10 %22
+ OpStore %20 %23
+ %28 = OpLoad %25 %27
+ %30 = OpLoad %13 %15
+ %31 = OpVectorShuffle %29 %30 %30 0 1
+ %33 = OpBitcast %32 %31
+ %34 = OpLoad %10 %12
+ %35 = OpLoad %10 %20
+ %36 = OpIAdd %10 %34 %35
+ %37 = OpBitcast %24 %36
+ %39 = OpCompositeConstruct %38 %37 %37 %37 %37
+ OpImageWrite %28 %33 %39
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ OpStore %44 %45
+ OpStore %46 %45
+ OpBranch %47
+ %47 = OpLabel
+ OpLoopMerge %49 %50 None
+ OpBranch %51
+ %51 = OpLabel
+ %52 = OpLoad %24 %46
+ %55 = OpSLessThan %54 %52 %53
+ OpBranchConditional %55 %48 %49
+ %48 = OpLabel
+ OpStore %56 %45
+ OpBranch %57
+ %57 = OpLabel
+ OpLoopMerge %59 %60 None
+ OpBranch %61
+ %61 = OpLabel
+ %62 = OpLoad %24 %56
+ %63 = OpSLessThan %54 %62 %53
+ OpBranchConditional %63 %58 %59
+ %58 = OpLabel
+ %64 = OpLoad %25 %27
+ %65 = OpLoad %24 %46
+ %66 = OpLoad %24 %56
+ %67 = OpCompositeConstruct %32 %65 %66
+ %68 = OpImageRead %38 %64 %67
+ %69 = OpCompositeExtract %24 %68 0
+ %70 = OpLoad %24 %44
+ %71 = OpIMul %24 %70 %69
+ OpStore %44 %71
+ OpBranch %60
+ %60 = OpLabel
+ %72 = OpLoad %24 %56
+ %74 = OpIAdd %24 %72 %73
+ OpStore %56 %74
+ OpBranch %57
+ %59 = OpLabel
+ OpBranch %50
+ %50 = OpLabel
+ %75 = OpLoad %24 %46
+ %76 = OpIAdd %24 %75 %73
+ OpStore %46 %76
+ OpBranch %47
+ %49 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %83 = OpLoad %24 %44
+ %85 = OpAccessChain %84 %82 %53
+ OpStore %85 %83
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %101 = OpVariable %43 Function
+ %92 = OpAccessChain %91 %90 %73
+ %93 = OpLoad %87 %92
+ %95 = OpAccessChain %94 %82 %45
+ %96 = OpLoad %77 %95
+ %97 = OpConvertUToF %86 %96
+ %98 = OpMatrixTimesVector %86 %93 %97
+ %99 = OpConvertFToU %77 %98
+ %100 = OpAccessChain %94 %82 %45
+ OpStore %100 %99
+ OpStore %101 %45
+ OpBranch %102
+ %102 = OpLabel
+ OpLoopMerge %104 %105 None
+ OpBranch %106
+ %106 = OpLabel
+ %107 = OpLoad %24 %101
+ %109 = OpSLessThan %54 %107 %108
+ OpBranchConditional %109 %103 %104
+ %103 = OpLabel
+ %111 = OpAccessChain %110 %82 %73
+ %112 = OpLoad %79 %111
+ %114 = OpAccessChain %113 %90 %53
+ %115 = OpLoad %78 %114
+ %116 = OpVectorTimesScalar %79 %112 %115
+ %117 = OpConvertFToU %13 %116
+ %118 = OpCompositeExtract %10 %117 0
+ %119 = OpCompositeExtract %10 %117 1
+ %120 = OpCompositeExtract %10 %117 2
+ %121 = OpCompositeConstruct %77 %118 %119 %120 %16
+ %122 = OpAccessChain %94 %82 %45
+ %123 = OpLoad %77 %122
+ %124 = OpIAdd %77 %123 %121
+ %125 = OpAccessChain %94 %82 %45
+ OpStore %125 %124
+ OpBranch %105
+ %105 = OpLabel
+ %126 = OpLoad %24 %101
+ %127 = OpIAdd %24 %126 %73
+ OpStore %101 %127
+ OpBranch %102
+ %104 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %129 = OpLoad %25 %128
+ %131 = OpImageRead %38 %129 %130
+ %132 = OpCompositeExtract %24 %131 0
+ %133 = OpConvertSToF %78 %132
+ %134 = OpCompositeConstruct %79 %133 %133 %133
+ %135 = OpAccessChain %110 %82 %73
+ OpStore %135 %134
+ OpReturn
+ OpFunctionEnd
+
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 137
++; Bound: 140
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main" %15
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpDecorate %15 BuiltIn LocalInvocationId
+ OpDecorate %27 DescriptorSet 0
+ OpDecorate %27 Binding 2
+ OpMemberDecorate %80 0 Offset 0
+ OpMemberDecorate %80 1 Offset 16
+ OpMemberDecorate %80 2 Offset 28
+ OpDecorate %80 BufferBlock
+ OpDecorate %82 DescriptorSet 0
+ OpDecorate %82 Binding 1
+ OpMemberDecorate %88 0 Offset 0
+ OpMemberDecorate %88 1 RowMajor
+ OpMemberDecorate %88 1 Offset 16
+ OpMemberDecorate %88 1 MatrixStride 16
+ OpMemberDecorate %88 2 Offset 80
+ OpDecorate %88 Block
+ OpDecorate %90 DescriptorSet 0
+ OpDecorate %90 Binding 0
++OpDecorate %138 DescriptorSet 0
++OpDecorate %138 Binding 3
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpTypePointer Function %10
+ %13 = OpTypeVector %10 3
+ %14 = OpTypePointer Input %13
+ %15 = OpVariable %14 Input
+ %16 = OpConstant %10 0
+ %17 = OpTypePointer Input %10
+ %21 = OpConstant %10 1
+ %24 = OpTypeInt 32 1
+ %25 = OpTypeImage %24 2D 0 0 0 2 R32i
+ %26 = OpTypePointer UniformConstant %25
+ %27 = OpVariable %26 UniformConstant
+ %29 = OpTypeVector %10 2
+ %32 = OpTypeVector %24 2
+ %38 = OpTypeVector %24 4
+ %40 = OpConstant %10 2
+ %41 = OpConstant %10 3400
+ %42 = OpConstant %10 264
+ %43 = OpTypePointer Function %24
+ %45 = OpConstant %24 0
+ %53 = OpConstant %24 2
+ %54 = OpTypeBool
+ %73 = OpConstant %24 1
+ %77 = OpTypeVector %10 4
+ %78 = OpTypeFloat 32
+ %79 = OpTypeVector %78 3
+ %80 = OpTypeStruct %77 %79 %24
+ %81 = OpTypePointer Uniform %80
+ %82 = OpVariable %81 Uniform
+ %84 = OpTypePointer Uniform %24
+ %86 = OpTypeVector %78 4
+ %87 = OpTypeMatrix %86 4
+ %88 = OpTypeStruct %10 %87 %78
+ %89 = OpTypePointer Uniform %88
+ %90 = OpVariable %89 Uniform
+ %91 = OpTypePointer Uniform %87
+ %94 = OpTypePointer Uniform %77
+ %108 = OpConstant %24 3
+ %110 = OpTypePointer Uniform %79
+ %113 = OpTypePointer Uniform %78
++%138 = OpVariable %26 UniformConstant
+ %129 = OpConstantComposite %32 %45 %45
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %135 = OpFunctionCall %2 %6
+ %136 = OpFunctionCall %2 %8
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %12 = OpVariable %11 Function
+ %20 = OpVariable %11 Function
+ %44 = OpVariable %43 Function
+ %46 = OpVariable %43 Function
+ %56 = OpVariable %43 Function
+ %18 = OpAccessChain %17 %15 %16
+ %19 = OpLoad %10 %18
+ OpStore %12 %19
+ %22 = OpAccessChain %17 %15 %21
+ %23 = OpLoad %10 %22
+ OpStore %20 %23
+ %28 = OpLoad %25 %27
+ %30 = OpLoad %13 %15
+ %31 = OpVectorShuffle %29 %30 %30 0 1
+ %33 = OpBitcast %32 %31
+ %34 = OpLoad %10 %12
+ %35 = OpLoad %10 %20
+ %36 = OpIAdd %10 %34 %35
+ %37 = OpBitcast %24 %36
+ %39 = OpCompositeConstruct %38 %37 %37 %37 %37
+ OpImageWrite %28 %33 %39
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ OpStore %44 %45
+ OpStore %46 %45
+ OpBranch %47
+ %47 = OpLabel
+ OpLoopMerge %49 %50 None
+ OpBranch %51
+ %51 = OpLabel
+ %52 = OpLoad %24 %46
+ %55 = OpSLessThan %54 %52 %53
+ OpBranchConditional %55 %48 %49
+ %48 = OpLabel
+ OpStore %56 %45
+ OpBranch %57
+ %57 = OpLabel
+ OpLoopMerge %59 %60 None
+ OpBranch %61
+ %61 = OpLabel
+ %62 = OpLoad %24 %56
+ %63 = OpSLessThan %54 %62 %53
+ OpBranchConditional %63 %58 %59
+ %58 = OpLabel
+ %64 = OpLoad %25 %27
+ %65 = OpLoad %24 %46
+ %66 = OpLoad %24 %56
+ %67 = OpCompositeConstruct %32 %65 %66
+ %68 = OpImageRead %38 %64 %67
+ %69 = OpCompositeExtract %24 %68 0
+ %70 = OpLoad %24 %44
+-%71 = OpIAdd %24 %70 %69
++%137 = OpIMul %24 %70 %69
+-OpStore %44 %71
++OpStore %44 %137
+ OpBranch %60
+ %60 = OpLabel
+ %72 = OpLoad %24 %56
+ %74 = OpIAdd %24 %72 %73
+ OpStore %56 %74
+ OpBranch %57
+ %59 = OpLabel
+ OpBranch %50
+ %50 = OpLabel
+ %75 = OpLoad %24 %46
+ %76 = OpIAdd %24 %75 %73
+ OpStore %46 %76
+ OpBranch %47
+ %49 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %83 = OpLoad %24 %44
+ %85 = OpAccessChain %84 %82 %53
+ OpStore %85 %83
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %101 = OpVariable %43 Function
+ %92 = OpAccessChain %91 %90 %73
+ %93 = OpLoad %87 %92
+ %95 = OpAccessChain %94 %82 %45
+ %96 = OpLoad %77 %95
+ %97 = OpConvertUToF %86 %96
+ %98 = OpMatrixTimesVector %86 %93 %97
+ %99 = OpConvertFToU %77 %98
+ %100 = OpAccessChain %94 %82 %45
+ OpStore %100 %99
+ OpStore %101 %45
+ OpBranch %102
+ %102 = OpLabel
+ OpLoopMerge %104 %105 None
+ OpBranch %106
+ %106 = OpLabel
+ %107 = OpLoad %24 %101
+ %109 = OpSLessThan %54 %107 %108
+ OpBranchConditional %109 %103 %104
+ %103 = OpLabel
+ %111 = OpAccessChain %110 %82 %73
+ %112 = OpLoad %79 %111
+ %114 = OpAccessChain %113 %90 %53
+ %115 = OpLoad %78 %114
+ %116 = OpVectorTimesScalar %79 %112 %115
+ %117 = OpConvertFToU %13 %116
+ %118 = OpCompositeExtract %10 %117 0
+ %119 = OpCompositeExtract %10 %117 1
+ %120 = OpCompositeExtract %10 %117 2
+ %121 = OpCompositeConstruct %77 %118 %119 %120 %16
+ %122 = OpAccessChain %94 %82 %45
+ %123 = OpLoad %77 %122
+ %124 = OpIAdd %77 %123 %121
+ %125 = OpAccessChain %94 %82 %45
+ OpStore %125 %124
+ OpBranch %105
+ %105 = OpLabel
+ %126 = OpLoad %24 %101
+ %127 = OpIAdd %24 %126 %73
+ OpStore %101 %127
+ OpBranch %102
+ %104 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+-%128 = OpLoad %25 %27
++%139 = OpLoad %25 %138
+-%130 = OpImageRead %38 %128 %129
++%130 = OpImageRead %38 %139 %129
+ %131 = OpCompositeExtract %24 %130 0
+ %132 = OpConvertSToF %78 %131
+ %133 = OpCompositeConstruct %79 %132 %132 %132
+ %134 = OpAccessChain %110 %82 %73
+ OpStore %134 %133
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/large_functions_small_diffs_dst.spvasm b/test/diff/diff_files/large_functions_small_diffs_dst.spvasm
new file mode 100644
index 00000000..f788e0b6
--- /dev/null
+++ b/test/diff/diff_files/large_functions_small_diffs_dst.spvasm
@@ -0,0 +1,229 @@
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main" %15
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "f1("
+ OpName %8 "f2("
+ OpName %12 "x"
+ OpName %15 "gl_LocalInvocationID"
+ OpName %20 "y"
+ OpName %27 "image"
+ OpName %44 "sum"
+ OpName %46 "i"
+ OpName %56 "j"
+ OpName %80 "BufferOut"
+ OpMemberName %80 0 "o_uv4"
+ OpMemberName %80 1 "o_v3"
+ OpMemberName %80 2 "o_i"
+ OpName %82 ""
+ OpName %88 "BufferIn"
+ OpMemberName %88 0 "i_u"
+ OpMemberName %88 1 "i_v4"
+ OpMemberName %88 2 "i_f"
+ OpName %90 ""
+ OpName %101 "i"
+ OpName %128 "image2"
+ OpDecorate %15 BuiltIn LocalInvocationId
+ OpDecorate %27 DescriptorSet 0
+ OpDecorate %27 Binding 2
+ OpMemberDecorate %80 0 Offset 0
+ OpMemberDecorate %80 1 Offset 16
+ OpMemberDecorate %80 2 Offset 28
+ OpDecorate %80 BufferBlock
+ OpDecorate %82 DescriptorSet 0
+ OpDecorate %82 Binding 1
+ OpMemberDecorate %88 0 Offset 0
+ OpMemberDecorate %88 1 RowMajor
+ OpMemberDecorate %88 1 Offset 16
+ OpMemberDecorate %88 1 MatrixStride 16
+ OpMemberDecorate %88 2 Offset 80
+ OpDecorate %88 Block
+ OpDecorate %90 DescriptorSet 0
+ OpDecorate %90 Binding 0
+ OpDecorate %128 DescriptorSet 0
+ OpDecorate %128 Binding 3
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpTypePointer Function %10
+ %13 = OpTypeVector %10 3
+ %14 = OpTypePointer Input %13
+ %15 = OpVariable %14 Input
+ %16 = OpConstant %10 0
+ %17 = OpTypePointer Input %10
+ %21 = OpConstant %10 1
+ %24 = OpTypeInt 32 1
+ %25 = OpTypeImage %24 2D 0 0 0 2 R32i
+ %26 = OpTypePointer UniformConstant %25
+ %27 = OpVariable %26 UniformConstant
+ %29 = OpTypeVector %10 2
+ %32 = OpTypeVector %24 2
+ %38 = OpTypeVector %24 4
+ %40 = OpConstant %10 2
+ %41 = OpConstant %10 3400
+ %42 = OpConstant %10 264
+ %43 = OpTypePointer Function %24
+ %45 = OpConstant %24 0
+ %53 = OpConstant %24 2
+ %54 = OpTypeBool
+ %73 = OpConstant %24 1
+ %77 = OpTypeVector %10 4
+ %78 = OpTypeFloat 32
+ %79 = OpTypeVector %78 3
+ %80 = OpTypeStruct %77 %79 %24
+ %81 = OpTypePointer Uniform %80
+ %82 = OpVariable %81 Uniform
+ %84 = OpTypePointer Uniform %24
+ %86 = OpTypeVector %78 4
+ %87 = OpTypeMatrix %86 4
+ %88 = OpTypeStruct %10 %87 %78
+ %89 = OpTypePointer Uniform %88
+ %90 = OpVariable %89 Uniform
+ %91 = OpTypePointer Uniform %87
+ %94 = OpTypePointer Uniform %77
+ %108 = OpConstant %24 3
+ %110 = OpTypePointer Uniform %79
+ %113 = OpTypePointer Uniform %78
+ %128 = OpVariable %26 UniformConstant
+ %130 = OpConstantComposite %32 %45 %45
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %136 = OpFunctionCall %2 %6
+ %137 = OpFunctionCall %2 %8
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %12 = OpVariable %11 Function
+ %20 = OpVariable %11 Function
+ %44 = OpVariable %43 Function
+ %46 = OpVariable %43 Function
+ %56 = OpVariable %43 Function
+ %18 = OpAccessChain %17 %15 %16
+ %19 = OpLoad %10 %18
+ OpStore %12 %19
+ %22 = OpAccessChain %17 %15 %21
+ %23 = OpLoad %10 %22
+ OpStore %20 %23
+ %28 = OpLoad %25 %27
+ %30 = OpLoad %13 %15
+ %31 = OpVectorShuffle %29 %30 %30 0 1
+ %33 = OpBitcast %32 %31
+ %34 = OpLoad %10 %12
+ %35 = OpLoad %10 %20
+ %36 = OpIAdd %10 %34 %35
+ %37 = OpBitcast %24 %36
+ %39 = OpCompositeConstruct %38 %37 %37 %37 %37
+ OpImageWrite %28 %33 %39
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ OpStore %44 %45
+ OpStore %46 %45
+ OpBranch %47
+ %47 = OpLabel
+ OpLoopMerge %49 %50 None
+ OpBranch %51
+ %51 = OpLabel
+ %52 = OpLoad %24 %46
+ %55 = OpSLessThan %54 %52 %53
+ OpBranchConditional %55 %48 %49
+ %48 = OpLabel
+ OpStore %56 %45
+ OpBranch %57
+ %57 = OpLabel
+ OpLoopMerge %59 %60 None
+ OpBranch %61
+ %61 = OpLabel
+ %62 = OpLoad %24 %56
+ %63 = OpSLessThan %54 %62 %53
+ OpBranchConditional %63 %58 %59
+ %58 = OpLabel
+ %64 = OpLoad %25 %27
+ %65 = OpLoad %24 %46
+ %66 = OpLoad %24 %56
+ %67 = OpCompositeConstruct %32 %65 %66
+ %68 = OpImageRead %38 %64 %67
+ %69 = OpCompositeExtract %24 %68 0
+ %70 = OpLoad %24 %44
+ %71 = OpIMul %24 %70 %69
+ OpStore %44 %71
+ OpBranch %60
+ %60 = OpLabel
+ %72 = OpLoad %24 %56
+ %74 = OpIAdd %24 %72 %73
+ OpStore %56 %74
+ OpBranch %57
+ %59 = OpLabel
+ OpBranch %50
+ %50 = OpLabel
+ %75 = OpLoad %24 %46
+ %76 = OpIAdd %24 %75 %73
+ OpStore %46 %76
+ OpBranch %47
+ %49 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %83 = OpLoad %24 %44
+ %85 = OpAccessChain %84 %82 %53
+ OpStore %85 %83
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %101 = OpVariable %43 Function
+ %92 = OpAccessChain %91 %90 %73
+ %93 = OpLoad %87 %92
+ %95 = OpAccessChain %94 %82 %45
+ %96 = OpLoad %77 %95
+ %97 = OpConvertUToF %86 %96
+ %98 = OpMatrixTimesVector %86 %93 %97
+ %99 = OpConvertFToU %77 %98
+ %100 = OpAccessChain %94 %82 %45
+ OpStore %100 %99
+ OpStore %101 %45
+ OpBranch %102
+ %102 = OpLabel
+ OpLoopMerge %104 %105 None
+ OpBranch %106
+ %106 = OpLabel
+ %107 = OpLoad %24 %101
+ %109 = OpSLessThan %54 %107 %108
+ OpBranchConditional %109 %103 %104
+ %103 = OpLabel
+ %111 = OpAccessChain %110 %82 %73
+ %112 = OpLoad %79 %111
+ %114 = OpAccessChain %113 %90 %53
+ %115 = OpLoad %78 %114
+ %116 = OpVectorTimesScalar %79 %112 %115
+ %117 = OpConvertFToU %13 %116
+ %118 = OpCompositeExtract %10 %117 0
+ %119 = OpCompositeExtract %10 %117 1
+ %120 = OpCompositeExtract %10 %117 2
+ %121 = OpCompositeConstruct %77 %118 %119 %120 %16
+ %122 = OpAccessChain %94 %82 %45
+ %123 = OpLoad %77 %122
+ %124 = OpIAdd %77 %123 %121
+ %125 = OpAccessChain %94 %82 %45
+ OpStore %125 %124
+ OpBranch %105
+ %105 = OpLabel
+ %126 = OpLoad %24 %101
+ %127 = OpIAdd %24 %126 %73
+ OpStore %101 %127
+ OpBranch %102
+ %104 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %129 = OpLoad %25 %128
+ %131 = OpImageRead %38 %129 %130
+ %132 = OpCompositeExtract %24 %131 0
+ %133 = OpConvertSToF %78 %132
+ %134 = OpCompositeConstruct %79 %133 %133 %133
+ %135 = OpAccessChain %110 %82 %73
+ OpStore %135 %134
+ OpReturn
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/large_functions_small_diffs_src.spvasm b/test/diff/diff_files/large_functions_small_diffs_src.spvasm
new file mode 100644
index 00000000..78a92785
--- /dev/null
+++ b/test/diff/diff_files/large_functions_small_diffs_src.spvasm
@@ -0,0 +1,226 @@
+;; Test where src and dst have a few large functions with small differences.
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main" %15
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "f1("
+ OpName %8 "f2("
+ OpName %12 "x"
+ OpName %15 "gl_LocalInvocationID"
+ OpName %20 "y"
+ OpName %27 "image"
+ OpName %44 "sum"
+ OpName %46 "i"
+ OpName %56 "j"
+ OpName %80 "BufferOut"
+ OpMemberName %80 0 "o_uv4"
+ OpMemberName %80 1 "o_v3"
+ OpMemberName %80 2 "o_i"
+ OpName %82 ""
+ OpName %88 "BufferIn"
+ OpMemberName %88 0 "i_u"
+ OpMemberName %88 1 "i_v4"
+ OpMemberName %88 2 "i_f"
+ OpName %90 ""
+ OpName %101 "i"
+ OpDecorate %15 BuiltIn LocalInvocationId
+ OpDecorate %27 DescriptorSet 0
+ OpDecorate %27 Binding 2
+ OpMemberDecorate %80 0 Offset 0
+ OpMemberDecorate %80 1 Offset 16
+ OpMemberDecorate %80 2 Offset 28
+ OpDecorate %80 BufferBlock
+ OpDecorate %82 DescriptorSet 0
+ OpDecorate %82 Binding 1
+ OpMemberDecorate %88 0 Offset 0
+ OpMemberDecorate %88 1 RowMajor
+ OpMemberDecorate %88 1 Offset 16
+ OpMemberDecorate %88 1 MatrixStride 16
+ OpMemberDecorate %88 2 Offset 80
+ OpDecorate %88 Block
+ OpDecorate %90 DescriptorSet 0
+ OpDecorate %90 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpTypePointer Function %10
+ %13 = OpTypeVector %10 3
+ %14 = OpTypePointer Input %13
+ %15 = OpVariable %14 Input
+ %16 = OpConstant %10 0
+ %17 = OpTypePointer Input %10
+ %21 = OpConstant %10 1
+ %24 = OpTypeInt 32 1
+ %25 = OpTypeImage %24 2D 0 0 0 2 R32i
+ %26 = OpTypePointer UniformConstant %25
+ %27 = OpVariable %26 UniformConstant
+ %29 = OpTypeVector %10 2
+ %32 = OpTypeVector %24 2
+ %38 = OpTypeVector %24 4
+ %40 = OpConstant %10 2
+ %41 = OpConstant %10 3400
+ %42 = OpConstant %10 264
+ %43 = OpTypePointer Function %24
+ %45 = OpConstant %24 0
+ %53 = OpConstant %24 2
+ %54 = OpTypeBool
+ %73 = OpConstant %24 1
+ %77 = OpTypeVector %10 4
+ %78 = OpTypeFloat 32
+ %79 = OpTypeVector %78 3
+ %80 = OpTypeStruct %77 %79 %24
+ %81 = OpTypePointer Uniform %80
+ %82 = OpVariable %81 Uniform
+ %84 = OpTypePointer Uniform %24
+ %86 = OpTypeVector %78 4
+ %87 = OpTypeMatrix %86 4
+ %88 = OpTypeStruct %10 %87 %78
+ %89 = OpTypePointer Uniform %88
+ %90 = OpVariable %89 Uniform
+ %91 = OpTypePointer Uniform %87
+ %94 = OpTypePointer Uniform %77
+ %108 = OpConstant %24 3
+ %110 = OpTypePointer Uniform %79
+ %113 = OpTypePointer Uniform %78
+ %129 = OpConstantComposite %32 %45 %45
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %135 = OpFunctionCall %2 %6
+ %136 = OpFunctionCall %2 %8
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %12 = OpVariable %11 Function
+ %20 = OpVariable %11 Function
+ %44 = OpVariable %43 Function
+ %46 = OpVariable %43 Function
+ %56 = OpVariable %43 Function
+ %18 = OpAccessChain %17 %15 %16
+ %19 = OpLoad %10 %18
+ OpStore %12 %19
+ %22 = OpAccessChain %17 %15 %21
+ %23 = OpLoad %10 %22
+ OpStore %20 %23
+ %28 = OpLoad %25 %27
+ %30 = OpLoad %13 %15
+ %31 = OpVectorShuffle %29 %30 %30 0 1
+ %33 = OpBitcast %32 %31
+ %34 = OpLoad %10 %12
+ %35 = OpLoad %10 %20
+ %36 = OpIAdd %10 %34 %35
+ %37 = OpBitcast %24 %36
+ %39 = OpCompositeConstruct %38 %37 %37 %37 %37
+ OpImageWrite %28 %33 %39
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ OpStore %44 %45
+ OpStore %46 %45
+ OpBranch %47
+ %47 = OpLabel
+ OpLoopMerge %49 %50 None
+ OpBranch %51
+ %51 = OpLabel
+ %52 = OpLoad %24 %46
+ %55 = OpSLessThan %54 %52 %53
+ OpBranchConditional %55 %48 %49
+ %48 = OpLabel
+ OpStore %56 %45
+ OpBranch %57
+ %57 = OpLabel
+ OpLoopMerge %59 %60 None
+ OpBranch %61
+ %61 = OpLabel
+ %62 = OpLoad %24 %56
+ %63 = OpSLessThan %54 %62 %53
+ OpBranchConditional %63 %58 %59
+ %58 = OpLabel
+ %64 = OpLoad %25 %27
+ %65 = OpLoad %24 %46
+ %66 = OpLoad %24 %56
+ %67 = OpCompositeConstruct %32 %65 %66
+ %68 = OpImageRead %38 %64 %67
+ %69 = OpCompositeExtract %24 %68 0
+ %70 = OpLoad %24 %44
+ %71 = OpIAdd %24 %70 %69
+ OpStore %44 %71
+ OpBranch %60
+ %60 = OpLabel
+ %72 = OpLoad %24 %56
+ %74 = OpIAdd %24 %72 %73
+ OpStore %56 %74
+ OpBranch %57
+ %59 = OpLabel
+ OpBranch %50
+ %50 = OpLabel
+ %75 = OpLoad %24 %46
+ %76 = OpIAdd %24 %75 %73
+ OpStore %46 %76
+ OpBranch %47
+ %49 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %83 = OpLoad %24 %44
+ %85 = OpAccessChain %84 %82 %53
+ OpStore %85 %83
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %101 = OpVariable %43 Function
+ %92 = OpAccessChain %91 %90 %73
+ %93 = OpLoad %87 %92
+ %95 = OpAccessChain %94 %82 %45
+ %96 = OpLoad %77 %95
+ %97 = OpConvertUToF %86 %96
+ %98 = OpMatrixTimesVector %86 %93 %97
+ %99 = OpConvertFToU %77 %98
+ %100 = OpAccessChain %94 %82 %45
+ OpStore %100 %99
+ OpStore %101 %45
+ OpBranch %102
+ %102 = OpLabel
+ OpLoopMerge %104 %105 None
+ OpBranch %106
+ %106 = OpLabel
+ %107 = OpLoad %24 %101
+ %109 = OpSLessThan %54 %107 %108
+ OpBranchConditional %109 %103 %104
+ %103 = OpLabel
+ %111 = OpAccessChain %110 %82 %73
+ %112 = OpLoad %79 %111
+ %114 = OpAccessChain %113 %90 %53
+ %115 = OpLoad %78 %114
+ %116 = OpVectorTimesScalar %79 %112 %115
+ %117 = OpConvertFToU %13 %116
+ %118 = OpCompositeExtract %10 %117 0
+ %119 = OpCompositeExtract %10 %117 1
+ %120 = OpCompositeExtract %10 %117 2
+ %121 = OpCompositeConstruct %77 %118 %119 %120 %16
+ %122 = OpAccessChain %94 %82 %45
+ %123 = OpLoad %77 %122
+ %124 = OpIAdd %77 %123 %121
+ %125 = OpAccessChain %94 %82 %45
+ OpStore %125 %124
+ OpBranch %105
+ %105 = OpLabel
+ %126 = OpLoad %24 %101
+ %127 = OpIAdd %24 %126 %73
+ OpStore %101 %127
+ OpBranch %102
+ %104 = OpLabel
+ OpMemoryBarrier %40 %41
+ OpControlBarrier %40 %40 %42
+ %128 = OpLoad %25 %27
+ %130 = OpImageRead %38 %128 %129
+ %131 = OpCompositeExtract %24 %130 0
+ %132 = OpConvertSToF %78 %131
+ %133 = OpCompositeConstruct %79 %132 %132 %132
+ %134 = OpAccessChain %110 %82 %73
+ OpStore %134 %133
+ OpReturn
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/multiple_different_entry_points_autogen.cpp b/test/diff/diff_files/multiple_different_entry_points_autogen.cpp
new file mode 100644
index 00000000..29d4b1d8
--- /dev/null
+++ b/test/diff/diff_files/multiple_different_entry_points_autogen.cpp
@@ -0,0 +1,330 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Basic test for multiple entry points. The entry points have different
+// execution models and so can be trivially matched.
+constexpr char kSrc[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %mainv "mainv" %vo %a
+ OpEntryPoint Fragment %mainf "mainf" %color %vi
+ OpExecutionMode %mainf OriginUpperLeft
+ OpSource ESSL 310
+ OpName %mainv "mainv"
+ OpName %mainf "mainf"
+ OpName %a "a"
+ OpName %vo "v"
+ OpName %vi "v"
+ OpName %color "color"
+ OpDecorate %a Location 0
+ OpDecorate %vo Location 0
+ OpDecorate %vi Location 0
+ OpDecorate %color Location 0
+ OpDecorate %color RelaxedPrecision
+ OpDecorate %vi RelaxedPrecision
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+
+%_ptr_Input_float = OpTypePointer Input %float
+ %a = OpVariable %_ptr_Input_float Input
+%_ptr_Output_float = OpTypePointer Output %float
+ %vo = OpVariable %_ptr_Output_float Output
+ %vi = OpVariable %_ptr_Input_float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %color = OpVariable %_ptr_Output_v4float Output
+
+ %mainv = OpFunction %void None %3
+ %5 = OpLabel
+ %11 = OpLoad %float %a
+ OpStore %vo %11
+ OpReturn
+ OpFunctionEnd
+
+ %mainf = OpFunction %void None %3
+ %6 = OpLabel
+ %12 = OpLoad %float %vi
+ %13 = OpCompositeConstruct %v4float %12 %12 %12 %12
+ OpStore %color %13
+ OpReturn
+ OpFunctionEnd)";
+constexpr char kDst[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %frag "frag" %vi %color
+ OpEntryPoint Vertex %vert "vert" %a %vo
+ OpExecutionMode %frag OriginUpperLeft
+ OpSource ESSL 310
+ OpName %frag "frag"
+ OpName %vert "vert"
+ OpName %vo "v"
+ OpName %a "a"
+ OpName %color "color"
+ OpName %vi "v"
+ OpDecorate %vi Location 0
+ OpDecorate %color Location 0
+ OpDecorate %a Location 0
+ OpDecorate %vo Location 0
+ OpDecorate %color RelaxedPrecision
+ OpDecorate %vi RelaxedPrecision
+ OpDecorate %14 RelaxedPrecision
+ OpDecorate %17 RelaxedPrecision
+
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+
+%_ptr_Output_float = OpTypePointer Output %float
+ %vo = OpVariable %_ptr_Output_float Output
+%_ptr_Input_float = OpTypePointer Input %float
+ %a = OpVariable %_ptr_Input_float Input
+ %vi = OpVariable %_ptr_Input_float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %color = OpVariable %_ptr_Output_v4float Output
+
+ %frag = OpFunction %void None %3
+ %7 = OpLabel
+ %14 = OpLoad %float %vi
+ %17 = OpCompositeConstruct %v4float %14 %14 %14 %14
+ OpStore %color %17
+ OpReturn
+ OpFunctionEnd
+
+ %vert = OpFunction %void None %3
+ %8 = OpLabel
+ %13 = OpLoad %float %a
+ OpStore %vo %13
+ OpReturn
+ OpFunctionEnd
+)";
+
+TEST(DiffTest, MultipleDifferentEntryPoints) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 20
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+-OpEntryPoint Vertex %2 "mainv" %4 %7
++OpEntryPoint Vertex %2 "vert" %7 %4
+-OpEntryPoint Fragment %8 "mainf" %9 %10
++OpEntryPoint Fragment %8 "frag" %10 %9
+ OpExecutionMode %8 OriginUpperLeft
+ OpSource ESSL 310
+-OpName %2 "mainv"
++OpName %2 "vert"
+-OpName %8 "mainf"
++OpName %8 "frag"
+ OpName %7 "a"
+ OpName %4 "v"
+ OpName %10 "v"
+ OpName %9 "color"
+ OpDecorate %7 Location 0
+ OpDecorate %4 Location 0
+ OpDecorate %10 Location 0
+ OpDecorate %9 Location 0
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %10 RelaxedPrecision
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ %14 = OpTypeVoid
+ %3 = OpTypeFunction %14
+ %15 = OpTypeFloat 32
+ %16 = OpTypeVector %15 4
+ %17 = OpTypePointer Input %15
+ %7 = OpVariable %17 Input
+ %18 = OpTypePointer Output %15
+ %4 = OpVariable %18 Output
+ %10 = OpVariable %17 Input
+ %19 = OpTypePointer Output %16
+ %9 = OpVariable %19 Output
+ %2 = OpFunction %14 None %3
+ %5 = OpLabel
+ %11 = OpLoad %15 %7
+ OpStore %4 %11
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %14 None %3
+ %6 = OpLabel
+ %12 = OpLoad %15 %10
+ %13 = OpCompositeConstruct %16 %12 %12 %12 %12
+ OpStore %9 %13
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, MultipleDifferentEntryPointsNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %mainv "mainv" %vo %a
+ OpEntryPoint Fragment %mainf "mainf" %color %vi
+ OpExecutionMode %mainf OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %a Location 0
+ OpDecorate %vo Location 0
+ OpDecorate %vi Location 0
+ OpDecorate %color Location 0
+ OpDecorate %color RelaxedPrecision
+ OpDecorate %vi RelaxedPrecision
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+
+%_ptr_Input_float = OpTypePointer Input %float
+ %a = OpVariable %_ptr_Input_float Input
+%_ptr_Output_float = OpTypePointer Output %float
+ %vo = OpVariable %_ptr_Output_float Output
+ %vi = OpVariable %_ptr_Input_float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %color = OpVariable %_ptr_Output_v4float Output
+
+ %mainv = OpFunction %void None %3
+ %5 = OpLabel
+ %11 = OpLoad %float %a
+ OpStore %vo %11
+ OpReturn
+ OpFunctionEnd
+
+ %mainf = OpFunction %void None %3
+ %6 = OpLabel
+ %12 = OpLoad %float %vi
+ %13 = OpCompositeConstruct %v4float %12 %12 %12 %12
+ OpStore %color %13
+ OpReturn
+ OpFunctionEnd
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %frag "frag" %vi %color
+ OpEntryPoint Vertex %vert "vert" %a %vo
+ OpExecutionMode %frag OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %vi Location 0
+ OpDecorate %color Location 0
+ OpDecorate %a Location 0
+ OpDecorate %vo Location 0
+ OpDecorate %color RelaxedPrecision
+ OpDecorate %vi RelaxedPrecision
+ OpDecorate %14 RelaxedPrecision
+ OpDecorate %17 RelaxedPrecision
+
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+
+%_ptr_Output_float = OpTypePointer Output %float
+ %vo = OpVariable %_ptr_Output_float Output
+%_ptr_Input_float = OpTypePointer Input %float
+ %a = OpVariable %_ptr_Input_float Input
+ %vi = OpVariable %_ptr_Input_float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %color = OpVariable %_ptr_Output_v4float Output
+
+ %frag = OpFunction %void None %3
+ %7 = OpLabel
+ %14 = OpLoad %float %vi
+ %17 = OpCompositeConstruct %v4float %14 %14 %14 %14
+ OpStore %color %17
+ OpReturn
+ OpFunctionEnd
+
+ %vert = OpFunction %void None %3
+ %8 = OpLabel
+ %13 = OpLoad %float %a
+ OpStore %vo %13
+ OpReturn
+ OpFunctionEnd
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 20
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+-OpEntryPoint Vertex %2 "mainv" %4 %7
++OpEntryPoint Vertex %2 "vert" %7 %4
+-OpEntryPoint Fragment %8 "mainf" %9 %10
++OpEntryPoint Fragment %8 "frag" %10 %9
+ OpExecutionMode %8 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %7 Location 0
+ OpDecorate %4 Location 0
+ OpDecorate %10 Location 0
+ OpDecorate %9 Location 0
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %10 RelaxedPrecision
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ %14 = OpTypeVoid
+ %3 = OpTypeFunction %14
+ %15 = OpTypeFloat 32
+ %16 = OpTypeVector %15 4
+ %17 = OpTypePointer Input %15
+ %7 = OpVariable %17 Input
+ %18 = OpTypePointer Output %15
+ %4 = OpVariable %18 Output
+ %10 = OpVariable %17 Input
+ %19 = OpTypePointer Output %16
+ %9 = OpVariable %19 Output
+ %2 = OpFunction %14 None %3
+ %5 = OpLabel
+ %11 = OpLoad %15 %7
+ OpStore %4 %11
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %14 None %3
+ %6 = OpLabel
+ %12 = OpLoad %15 %10
+ %13 = OpCompositeConstruct %16 %12 %12 %12 %12
+ OpStore %9 %13
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/multiple_different_entry_points_dst.spvasm b/test/diff/diff_files/multiple_different_entry_points_dst.spvasm
new file mode 100644
index 00000000..72cfe283
--- /dev/null
+++ b/test/diff/diff_files/multiple_different_entry_points_dst.spvasm
@@ -0,0 +1,49 @@
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %frag "frag" %vi %color
+ OpEntryPoint Vertex %vert "vert" %a %vo
+ OpExecutionMode %frag OriginUpperLeft
+ OpSource ESSL 310
+ OpName %frag "frag"
+ OpName %vert "vert"
+ OpName %vo "v"
+ OpName %a "a"
+ OpName %color "color"
+ OpName %vi "v"
+ OpDecorate %vi Location 0
+ OpDecorate %color Location 0
+ OpDecorate %a Location 0
+ OpDecorate %vo Location 0
+ OpDecorate %color RelaxedPrecision
+ OpDecorate %vi RelaxedPrecision
+ OpDecorate %14 RelaxedPrecision
+ OpDecorate %17 RelaxedPrecision
+
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+
+%_ptr_Output_float = OpTypePointer Output %float
+ %vo = OpVariable %_ptr_Output_float Output
+%_ptr_Input_float = OpTypePointer Input %float
+ %a = OpVariable %_ptr_Input_float Input
+ %vi = OpVariable %_ptr_Input_float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %color = OpVariable %_ptr_Output_v4float Output
+
+ %frag = OpFunction %void None %3
+ %7 = OpLabel
+ %14 = OpLoad %float %vi
+ %17 = OpCompositeConstruct %v4float %14 %14 %14 %14
+ OpStore %color %17
+ OpReturn
+ OpFunctionEnd
+
+ %vert = OpFunction %void None %3
+ %8 = OpLabel
+ %13 = OpLoad %float %a
+ OpStore %vo %13
+ OpReturn
+ OpFunctionEnd
diff --git a/test/diff/diff_files/multiple_different_entry_points_src.spvasm b/test/diff/diff_files/multiple_different_entry_points_src.spvasm
new file mode 100644
index 00000000..2119aa73
--- /dev/null
+++ b/test/diff/diff_files/multiple_different_entry_points_src.spvasm
@@ -0,0 +1,51 @@
+;; Basic test for multiple entry points. The entry points have different
+;; execution models and so can be trivially matched.
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %mainv "mainv" %vo %a
+ OpEntryPoint Fragment %mainf "mainf" %color %vi
+ OpExecutionMode %mainf OriginUpperLeft
+ OpSource ESSL 310
+ OpName %mainv "mainv"
+ OpName %mainf "mainf"
+ OpName %a "a"
+ OpName %vo "v"
+ OpName %vi "v"
+ OpName %color "color"
+ OpDecorate %a Location 0
+ OpDecorate %vo Location 0
+ OpDecorate %vi Location 0
+ OpDecorate %color Location 0
+ OpDecorate %color RelaxedPrecision
+ OpDecorate %vi RelaxedPrecision
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+
+%_ptr_Input_float = OpTypePointer Input %float
+ %a = OpVariable %_ptr_Input_float Input
+%_ptr_Output_float = OpTypePointer Output %float
+ %vo = OpVariable %_ptr_Output_float Output
+ %vi = OpVariable %_ptr_Input_float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %color = OpVariable %_ptr_Output_v4float Output
+
+ %mainv = OpFunction %void None %3
+ %5 = OpLabel
+ %11 = OpLoad %float %a
+ OpStore %vo %11
+ OpReturn
+ OpFunctionEnd
+
+ %mainf = OpFunction %void None %3
+ %6 = OpLabel
+ %12 = OpLoad %float %vi
+ %13 = OpCompositeConstruct %v4float %12 %12 %12 %12
+ OpStore %color %13
+ OpReturn
+ OpFunctionEnd
diff --git a/test/diff/diff_files/multiple_same_entry_points_autogen.cpp b/test/diff/diff_files/multiple_same_entry_points_autogen.cpp
new file mode 100644
index 00000000..9d011661
--- /dev/null
+++ b/test/diff/diff_files/multiple_same_entry_points_autogen.cpp
@@ -0,0 +1,375 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Test for multiple entry points with the same execution model.
+constexpr char kSrc[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %4 "main1" %8 %10
+ OpEntryPoint Vertex %12 "main2" %13 %14 %15
+ OpSource ESSL 310
+ OpName %4 "main1"
+ OpName %12 "main2"
+ OpName %8 "v"
+ OpName %10 "a"
+ OpName %13 "v"
+ OpName %14 "a"
+ OpName %15 "b"
+ OpDecorate %8 Location 0
+ OpDecorate %10 Location 0
+ OpDecorate %13 Location 0
+ OpDecorate %14 Location 0
+ OpDecorate %15 Location 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+
+ %7 = OpTypePointer Output %6
+ %9 = OpTypePointer Input %6
+ %8 = OpVariable %7 Output
+ %10 = OpVariable %9 Input
+ %13 = OpVariable %7 Output
+ %14 = OpVariable %9 Input
+ %15 = OpVariable %9 Input
+
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpLoad %6 %10
+ OpStore %8 %11
+ OpReturn
+ OpFunctionEnd
+
+ %12 = OpFunction %2 None %3
+ %16 = OpLabel
+ %17 = OpLoad %6 %14
+ %18 = OpLoad %6 %15
+ %19 = OpFAdd %6 %17 %18
+ OpStore %13 %19
+ OpReturn
+ OpFunctionEnd)";
+constexpr char kDst[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %4 "main2" %13 %14 %15
+ OpEntryPoint Vertex %12 "main1" %8 %10
+ OpSource ESSL 310
+ OpName %12 "main1"
+ OpName %4 "main2"
+ OpName %8 "v"
+ OpName %10 "a"
+ OpName %13 "v"
+ OpName %14 "a"
+ OpName %15 "b"
+ OpDecorate %8 Location 0
+ OpDecorate %10 Location 0
+ OpDecorate %13 Location 0
+ OpDecorate %14 Location 0
+ OpDecorate %15 Location 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+
+ %7 = OpTypePointer Output %6
+ %9 = OpTypePointer Input %6
+ %8 = OpVariable %7 Output
+ %10 = OpVariable %9 Input
+ %13 = OpVariable %7 Output
+ %14 = OpVariable %9 Input
+ %15 = OpVariable %9 Input
+
+ %4 = OpFunction %2 None %3
+ %16 = OpLabel
+ %17 = OpLoad %6 %14
+ %18 = OpLoad %6 %15
+ %19 = OpFAdd %6 %17 %18
+ OpStore %13 %19
+ OpReturn
+ OpFunctionEnd
+
+ %12 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpLoad %6 %10
+ OpStore %8 %11
+ OpReturn
+ OpFunctionEnd
+)";
+
+TEST(DiffTest, MultipleSameEntryPoints) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 20
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
++OpEntryPoint Vertex %12 "main2" %13 %14 %15
+ OpEntryPoint Vertex %4 "main1" %8 %10
+-OpEntryPoint Vertex %12 "main2" %13 %14 %15
+ OpSource ESSL 310
+ OpName %4 "main1"
+ OpName %12 "main2"
+ OpName %8 "v"
+ OpName %10 "a"
+ OpName %13 "v"
+ OpName %14 "a"
+ OpName %15 "b"
+ OpDecorate %8 Location 0
+ OpDecorate %10 Location 0
+ OpDecorate %13 Location 0
+ OpDecorate %14 Location 0
+ OpDecorate %15 Location 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Output %6
+ %9 = OpTypePointer Input %6
+ %8 = OpVariable %7 Output
+ %10 = OpVariable %9 Input
+ %13 = OpVariable %7 Output
+ %14 = OpVariable %9 Input
+ %15 = OpVariable %9 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpLoad %6 %10
+ OpStore %8 %11
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %2 None %3
+ %16 = OpLabel
+ %17 = OpLoad %6 %14
+ %18 = OpLoad %6 %15
+ %19 = OpFAdd %6 %17 %18
+ OpStore %13 %19
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, MultipleSameEntryPointsNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %4 "main1" %8 %10
+ OpEntryPoint Vertex %12 "main2" %13 %14 %15
+ OpSource ESSL 310
+ OpDecorate %8 Location 0
+ OpDecorate %10 Location 0
+ OpDecorate %13 Location 0
+ OpDecorate %14 Location 0
+ OpDecorate %15 Location 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+
+ %7 = OpTypePointer Output %6
+ %9 = OpTypePointer Input %6
+ %8 = OpVariable %7 Output
+ %10 = OpVariable %9 Input
+ %13 = OpVariable %7 Output
+ %14 = OpVariable %9 Input
+ %15 = OpVariable %9 Input
+
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpLoad %6 %10
+ OpStore %8 %11
+ OpReturn
+ OpFunctionEnd
+
+ %12 = OpFunction %2 None %3
+ %16 = OpLabel
+ %17 = OpLoad %6 %14
+ %18 = OpLoad %6 %15
+ %19 = OpFAdd %6 %17 %18
+ OpStore %13 %19
+ OpReturn
+ OpFunctionEnd
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %4 "main2" %13 %14 %15
+ OpEntryPoint Vertex %12 "main1" %8 %10
+ OpSource ESSL 310
+ OpDecorate %8 Location 0
+ OpDecorate %10 Location 0
+ OpDecorate %13 Location 0
+ OpDecorate %14 Location 0
+ OpDecorate %15 Location 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+
+ %7 = OpTypePointer Output %6
+ %9 = OpTypePointer Input %6
+ %8 = OpVariable %7 Output
+ %10 = OpVariable %9 Input
+ %13 = OpVariable %7 Output
+ %14 = OpVariable %9 Input
+ %15 = OpVariable %9 Input
+
+ %4 = OpFunction %2 None %3
+ %16 = OpLabel
+ %17 = OpLoad %6 %14
+ %18 = OpLoad %6 %15
+ %19 = OpFAdd %6 %17 %18
+ OpStore %13 %19
+ OpReturn
+ OpFunctionEnd
+
+ %12 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpLoad %6 %10
+ OpStore %8 %11
+ OpReturn
+ OpFunctionEnd
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 20
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
++OpEntryPoint Vertex %12 "main2" %13 %14 %15
+ OpEntryPoint Vertex %4 "main1" %8 %10
+-OpEntryPoint Vertex %12 "main2" %13 %14 %15
+ OpSource ESSL 310
+ OpDecorate %8 Location 0
+ OpDecorate %10 Location 0
+ OpDecorate %13 Location 0
+ OpDecorate %14 Location 0
+ OpDecorate %15 Location 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Output %6
+ %9 = OpTypePointer Input %6
+ %8 = OpVariable %7 Output
+ %10 = OpVariable %9 Input
+ %13 = OpVariable %7 Output
+ %14 = OpVariable %9 Input
+ %15 = OpVariable %9 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpLoad %6 %10
+ OpStore %8 %11
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %2 None %3
+ %16 = OpLabel
+ %17 = OpLoad %6 %14
+ %18 = OpLoad %6 %15
+ %19 = OpFAdd %6 %17 %18
+ OpStore %13 %19
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+TEST(DiffTest, MultipleSameEntryPointsDumpIds) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 20
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
++OpEntryPoint Vertex %12 "main2" %13 %14 %15
+ OpEntryPoint Vertex %4 "main1" %8 %10
+-OpEntryPoint Vertex %12 "main2" %13 %14 %15
+ OpSource ESSL 310
+ OpName %4 "main1"
+ OpName %12 "main2"
+ OpName %8 "v"
+ OpName %10 "a"
+ OpName %13 "v"
+ OpName %14 "a"
+ OpName %15 "b"
+ OpDecorate %8 Location 0
+ OpDecorate %10 Location 0
+ OpDecorate %13 Location 0
+ OpDecorate %14 Location 0
+ OpDecorate %15 Location 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Output %6
+ %9 = OpTypePointer Input %6
+ %8 = OpVariable %7 Output
+ %10 = OpVariable %9 Input
+ %13 = OpVariable %7 Output
+ %14 = OpVariable %9 Input
+ %15 = OpVariable %9 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpLoad %6 %10
+ OpStore %8 %11
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %2 None %3
+ %16 = OpLabel
+ %17 = OpLoad %6 %14
+ %18 = OpLoad %6 %15
+ %19 = OpFAdd %6 %17 %18
+ OpStore %13 %19
+ OpReturn
+ OpFunctionEnd
+ Src -> Dst
+ 1 -> 1 [ExtInstImport]
+ 2 -> 2 [TypeVoid]
+ 3 -> 3 [TypeFunction]
+ 4 -> 12 [Function]
+ 5 -> 5 [Label]
+ 6 -> 6 [TypeFloat]
+ 7 -> 7 [TypePointer]
+ 8 -> 8 [Variable]
+ 9 -> 9 [TypePointer]
+ 10 -> 10 [Variable]
+ 11 -> 11 [Load]
+ 12 -> 4 [Function]
+ 13 -> 13 [Variable]
+ 14 -> 14 [Variable]
+ 15 -> 15 [Variable]
+ 16 -> 16 [Label]
+ 17 -> 17 [Load]
+ 18 -> 18 [Load]
+ 19 -> 19 [FAdd]
+)";
+ Options options;
+ options.dump_id_map = true;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/multiple_same_entry_points_dst.spvasm b/test/diff/diff_files/multiple_same_entry_points_dst.spvasm
new file mode 100644
index 00000000..e2007220
--- /dev/null
+++ b/test/diff/diff_files/multiple_same_entry_points_dst.spvasm
@@ -0,0 +1,45 @@
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %4 "main2" %13 %14 %15
+ OpEntryPoint Vertex %12 "main1" %8 %10
+ OpSource ESSL 310
+ OpName %12 "main1"
+ OpName %4 "main2"
+ OpName %8 "v"
+ OpName %10 "a"
+ OpName %13 "v"
+ OpName %14 "a"
+ OpName %15 "b"
+ OpDecorate %8 Location 0
+ OpDecorate %10 Location 0
+ OpDecorate %13 Location 0
+ OpDecorate %14 Location 0
+ OpDecorate %15 Location 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+
+ %7 = OpTypePointer Output %6
+ %9 = OpTypePointer Input %6
+ %8 = OpVariable %7 Output
+ %10 = OpVariable %9 Input
+ %13 = OpVariable %7 Output
+ %14 = OpVariable %9 Input
+ %15 = OpVariable %9 Input
+
+ %4 = OpFunction %2 None %3
+ %16 = OpLabel
+ %17 = OpLoad %6 %14
+ %18 = OpLoad %6 %15
+ %19 = OpFAdd %6 %17 %18
+ OpStore %13 %19
+ OpReturn
+ OpFunctionEnd
+
+ %12 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpLoad %6 %10
+ OpStore %8 %11
+ OpReturn
+ OpFunctionEnd
diff --git a/test/diff/diff_files/multiple_same_entry_points_src.spvasm b/test/diff/diff_files/multiple_same_entry_points_src.spvasm
new file mode 100644
index 00000000..17001b57
--- /dev/null
+++ b/test/diff/diff_files/multiple_same_entry_points_src.spvasm
@@ -0,0 +1,46 @@
+;; Test for multiple entry points with the same execution model.
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %4 "main1" %8 %10
+ OpEntryPoint Vertex %12 "main2" %13 %14 %15
+ OpSource ESSL 310
+ OpName %4 "main1"
+ OpName %12 "main2"
+ OpName %8 "v"
+ OpName %10 "a"
+ OpName %13 "v"
+ OpName %14 "a"
+ OpName %15 "b"
+ OpDecorate %8 Location 0
+ OpDecorate %10 Location 0
+ OpDecorate %13 Location 0
+ OpDecorate %14 Location 0
+ OpDecorate %15 Location 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+
+ %7 = OpTypePointer Output %6
+ %9 = OpTypePointer Input %6
+ %8 = OpVariable %7 Output
+ %10 = OpVariable %9 Input
+ %13 = OpVariable %7 Output
+ %14 = OpVariable %9 Input
+ %15 = OpVariable %9 Input
+
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpLoad %6 %10
+ OpStore %8 %11
+ OpReturn
+ OpFunctionEnd
+
+ %12 = OpFunction %2 None %3
+ %16 = OpLabel
+ %17 = OpLoad %6 %14
+ %18 = OpLoad %6 %15
+ %19 = OpFAdd %6 %17 %18
+ OpStore %13 %19
+ OpReturn
+ OpFunctionEnd
diff --git a/test/diff/diff_files/reordered_if_blocks_autogen.cpp b/test/diff/diff_files/reordered_if_blocks_autogen.cpp
new file mode 100644
index 00000000..0788199f
--- /dev/null
+++ b/test/diff/diff_files/reordered_if_blocks_autogen.cpp
@@ -0,0 +1,568 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Test where src and dst have the true and false blocks of an if reordered.
+constexpr char kSrc[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %8 %44
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "v"
+ OpName %44 "color"
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %8 Location 0
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %18 RelaxedPrecision
+ OpDecorate %19 RelaxedPrecision
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %23 RelaxedPrecision
+ OpDecorate %24 RelaxedPrecision
+ OpDecorate %25 RelaxedPrecision
+ OpDecorate %26 RelaxedPrecision
+ OpDecorate %27 RelaxedPrecision
+ OpDecorate %28 RelaxedPrecision
+ OpDecorate %29 RelaxedPrecision
+ OpDecorate %30 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %33 RelaxedPrecision
+ OpDecorate %34 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %40 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %44 RelaxedPrecision
+ OpDecorate %44 Location 0
+ OpDecorate %45 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Input %6
+ %8 = OpVariable %7 Input
+ %10 = OpConstant %6 0
+ %11 = OpTypeBool
+ %15 = OpTypeVector %6 4
+ %16 = OpTypePointer Function %15
+ %21 = OpConstant %6 -0.5
+ %22 = OpConstant %6 -0.300000012
+ %38 = OpConstant %6 0.5
+ %43 = OpTypePointer Output %15
+ %44 = OpVariable %43 Output
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %9 = OpLoad %6 %8
+ %12 = OpFOrdLessThanEqual %11 %9 %10
+ OpSelectionMerge %14 None
+ OpBranchConditional %12 %13 %32
+ %13 = OpLabel
+ %18 = OpLoad %6 %8
+ %19 = OpExtInst %6 %1 Log %18
+ %20 = OpLoad %6 %8
+ %23 = OpExtInst %6 %1 FClamp %20 %21 %22
+ %24 = OpFMul %6 %19 %23
+ %25 = OpLoad %6 %8
+ %26 = OpExtInst %6 %1 Sin %25
+ %27 = OpLoad %6 %8
+ %28 = OpExtInst %6 %1 Cos %27
+ %29 = OpLoad %6 %8
+ %30 = OpExtInst %6 %1 Exp %29
+ %31 = OpCompositeConstruct %15 %24 %26 %28 %30
+ OpBranch %14
+ %32 = OpLabel
+ %33 = OpLoad %6 %8
+ %34 = OpExtInst %6 %1 Sqrt %33
+ %35 = OpLoad %6 %8
+ %36 = OpExtInst %6 %1 FSign %35
+ %37 = OpLoad %6 %8
+ %39 = OpExtInst %6 %1 FMax %37 %38
+ %40 = OpLoad %6 %8
+ %41 = OpExtInst %6 %1 Floor %40
+ %42 = OpCompositeConstruct %15 %34 %36 %39 %41
+ OpBranch %14
+ %14 = OpLabel
+ %45 = OpPhi %15 %31 %13 %42 %32
+ OpStore %44 %45
+ OpReturn
+ OpFunctionEnd)";
+constexpr char kDst[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %8 %44
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "v"
+ OpName %44 "color"
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %8 Location 0
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %18 RelaxedPrecision
+ OpDecorate %19 RelaxedPrecision
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %21 RelaxedPrecision
+ OpDecorate %22 RelaxedPrecision
+ OpDecorate %24 RelaxedPrecision
+ OpDecorate %25 RelaxedPrecision
+ OpDecorate %26 RelaxedPrecision
+ OpDecorate %27 RelaxedPrecision
+ OpDecorate %29 RelaxedPrecision
+ OpDecorate %30 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %34 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %38 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %40 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %44 RelaxedPrecision
+ OpDecorate %44 Location 0
+ OpDecorate %45 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Input %6
+ %8 = OpVariable %7 Input
+ %10 = OpConstant %6 0
+ %11 = OpTypeBool
+ %15 = OpTypeVector %6 4
+ %16 = OpTypePointer Function %15
+ %23 = OpConstant %6 0.5
+ %32 = OpConstant %6 -0.5
+ %33 = OpConstant %6 -0.300000012
+ %43 = OpTypePointer Output %15
+ %44 = OpVariable %43 Output
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %9 = OpLoad %6 %8
+ %12 = OpFOrdLessThanEqual %11 %9 %10
+ OpSelectionMerge %14 None
+ OpBranchConditional %12 %28 %13
+ %13 = OpLabel
+ %18 = OpLoad %6 %8
+ %19 = OpExtInst %6 %1 Sqrt %18
+ %20 = OpLoad %6 %8
+ %21 = OpExtInst %6 %1 FSign %20
+ %22 = OpLoad %6 %8
+ %24 = OpExtInst %6 %1 FMax %22 %23
+ %25 = OpLoad %6 %8
+ %26 = OpExtInst %6 %1 Floor %25
+ %27 = OpCompositeConstruct %15 %19 %21 %24 %26
+ OpBranch %14
+ %28 = OpLabel
+ %29 = OpLoad %6 %8
+ %30 = OpExtInst %6 %1 Log %29
+ %31 = OpLoad %6 %8
+ %34 = OpExtInst %6 %1 FClamp %31 %32 %33
+ %35 = OpFMul %6 %30 %34
+ %36 = OpLoad %6 %8
+ %37 = OpExtInst %6 %1 Sin %36
+ %38 = OpLoad %6 %8
+ %39 = OpExtInst %6 %1 Cos %38
+ %40 = OpLoad %6 %8
+ %41 = OpExtInst %6 %1 Exp %40
+ %42 = OpCompositeConstruct %15 %35 %37 %39 %41
+ OpBranch %14
+ %14 = OpLabel
+ %45 = OpPhi %15 %27 %13 %42 %28
+ OpStore %44 %45
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+TEST(DiffTest, ReorderedIfBlocks) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 46
++; Bound: 47
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %8 %44
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "v"
+ OpName %44 "color"
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %8 Location 0
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %18 RelaxedPrecision
+ OpDecorate %19 RelaxedPrecision
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %23 RelaxedPrecision
+ OpDecorate %24 RelaxedPrecision
+ OpDecorate %25 RelaxedPrecision
+ OpDecorate %26 RelaxedPrecision
+ OpDecorate %27 RelaxedPrecision
+ OpDecorate %28 RelaxedPrecision
+ OpDecorate %29 RelaxedPrecision
+ OpDecorate %30 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %33 RelaxedPrecision
+ OpDecorate %34 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %40 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %44 RelaxedPrecision
+ OpDecorate %44 Location 0
+ OpDecorate %45 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Input %6
+ %8 = OpVariable %7 Input
+ %10 = OpConstant %6 0
+ %11 = OpTypeBool
+ %15 = OpTypeVector %6 4
+ %16 = OpTypePointer Function %15
+ %21 = OpConstant %6 -0.5
+ %22 = OpConstant %6 -0.300000012
+ %38 = OpConstant %6 0.5
+ %43 = OpTypePointer Output %15
+ %44 = OpVariable %43 Output
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %9 = OpLoad %6 %8
+ %12 = OpFOrdLessThanEqual %11 %9 %10
+ OpSelectionMerge %14 None
+ OpBranchConditional %12 %13 %32
+ %32 = OpLabel
+ %33 = OpLoad %6 %8
+ %34 = OpExtInst %6 %1 Sqrt %33
+ %35 = OpLoad %6 %8
+ %36 = OpExtInst %6 %1 FSign %35
+ %37 = OpLoad %6 %8
+ %39 = OpExtInst %6 %1 FMax %37 %38
+ %40 = OpLoad %6 %8
+ %41 = OpExtInst %6 %1 Floor %40
+ %42 = OpCompositeConstruct %15 %34 %36 %39 %41
+ OpBranch %14
+ %13 = OpLabel
+ %18 = OpLoad %6 %8
+ %19 = OpExtInst %6 %1 Log %18
+ %20 = OpLoad %6 %8
+ %23 = OpExtInst %6 %1 FClamp %20 %21 %22
+ %24 = OpFMul %6 %19 %23
+ %25 = OpLoad %6 %8
+ %26 = OpExtInst %6 %1 Sin %25
+ %27 = OpLoad %6 %8
+ %28 = OpExtInst %6 %1 Cos %27
+ %29 = OpLoad %6 %8
+ %30 = OpExtInst %6 %1 Exp %29
+ %31 = OpCompositeConstruct %15 %24 %26 %28 %30
+ OpBranch %14
+ %14 = OpLabel
+-%45 = OpPhi %15 %31 %13 %42 %32
++%45 = OpPhi %15 %42 %32 %31 %13
+ OpStore %44 %45
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, ReorderedIfBlocksNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %8 %44
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %8 Location 0
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %18 RelaxedPrecision
+ OpDecorate %19 RelaxedPrecision
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %23 RelaxedPrecision
+ OpDecorate %24 RelaxedPrecision
+ OpDecorate %25 RelaxedPrecision
+ OpDecorate %26 RelaxedPrecision
+ OpDecorate %27 RelaxedPrecision
+ OpDecorate %28 RelaxedPrecision
+ OpDecorate %29 RelaxedPrecision
+ OpDecorate %30 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %33 RelaxedPrecision
+ OpDecorate %34 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %40 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %44 RelaxedPrecision
+ OpDecorate %44 Location 0
+ OpDecorate %45 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Input %6
+ %8 = OpVariable %7 Input
+ %10 = OpConstant %6 0
+ %11 = OpTypeBool
+ %15 = OpTypeVector %6 4
+ %16 = OpTypePointer Function %15
+ %21 = OpConstant %6 -0.5
+ %22 = OpConstant %6 -0.300000012
+ %38 = OpConstant %6 0.5
+ %43 = OpTypePointer Output %15
+ %44 = OpVariable %43 Output
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %9 = OpLoad %6 %8
+ %12 = OpFOrdLessThanEqual %11 %9 %10
+ OpSelectionMerge %14 None
+ OpBranchConditional %12 %13 %32
+ %13 = OpLabel
+ %18 = OpLoad %6 %8
+ %19 = OpExtInst %6 %1 Log %18
+ %20 = OpLoad %6 %8
+ %23 = OpExtInst %6 %1 FClamp %20 %21 %22
+ %24 = OpFMul %6 %19 %23
+ %25 = OpLoad %6 %8
+ %26 = OpExtInst %6 %1 Sin %25
+ %27 = OpLoad %6 %8
+ %28 = OpExtInst %6 %1 Cos %27
+ %29 = OpLoad %6 %8
+ %30 = OpExtInst %6 %1 Exp %29
+ %31 = OpCompositeConstruct %15 %24 %26 %28 %30
+ OpBranch %14
+ %32 = OpLabel
+ %33 = OpLoad %6 %8
+ %34 = OpExtInst %6 %1 Sqrt %33
+ %35 = OpLoad %6 %8
+ %36 = OpExtInst %6 %1 FSign %35
+ %37 = OpLoad %6 %8
+ %39 = OpExtInst %6 %1 FMax %37 %38
+ %40 = OpLoad %6 %8
+ %41 = OpExtInst %6 %1 Floor %40
+ %42 = OpCompositeConstruct %15 %34 %36 %39 %41
+ OpBranch %14
+ %14 = OpLabel
+ %45 = OpPhi %15 %31 %13 %42 %32
+ OpStore %44 %45
+ OpReturn
+ OpFunctionEnd
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %8 %44
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %8 Location 0
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %18 RelaxedPrecision
+ OpDecorate %19 RelaxedPrecision
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %21 RelaxedPrecision
+ OpDecorate %22 RelaxedPrecision
+ OpDecorate %24 RelaxedPrecision
+ OpDecorate %25 RelaxedPrecision
+ OpDecorate %26 RelaxedPrecision
+ OpDecorate %27 RelaxedPrecision
+ OpDecorate %29 RelaxedPrecision
+ OpDecorate %30 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %34 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %38 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %40 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %44 RelaxedPrecision
+ OpDecorate %44 Location 0
+ OpDecorate %45 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Input %6
+ %8 = OpVariable %7 Input
+ %10 = OpConstant %6 0
+ %11 = OpTypeBool
+ %15 = OpTypeVector %6 4
+ %16 = OpTypePointer Function %15
+ %23 = OpConstant %6 0.5
+ %32 = OpConstant %6 -0.5
+ %33 = OpConstant %6 -0.300000012
+ %43 = OpTypePointer Output %15
+ %44 = OpVariable %43 Output
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %9 = OpLoad %6 %8
+ %12 = OpFOrdLessThanEqual %11 %9 %10
+ OpSelectionMerge %14 None
+ OpBranchConditional %12 %28 %13
+ %13 = OpLabel
+ %18 = OpLoad %6 %8
+ %19 = OpExtInst %6 %1 Sqrt %18
+ %20 = OpLoad %6 %8
+ %21 = OpExtInst %6 %1 FSign %20
+ %22 = OpLoad %6 %8
+ %24 = OpExtInst %6 %1 FMax %22 %23
+ %25 = OpLoad %6 %8
+ %26 = OpExtInst %6 %1 Floor %25
+ %27 = OpCompositeConstruct %15 %19 %21 %24 %26
+ OpBranch %14
+ %28 = OpLabel
+ %29 = OpLoad %6 %8
+ %30 = OpExtInst %6 %1 Log %29
+ %31 = OpLoad %6 %8
+ %34 = OpExtInst %6 %1 FClamp %31 %32 %33
+ %35 = OpFMul %6 %30 %34
+ %36 = OpLoad %6 %8
+ %37 = OpExtInst %6 %1 Sin %36
+ %38 = OpLoad %6 %8
+ %39 = OpExtInst %6 %1 Cos %38
+ %40 = OpLoad %6 %8
+ %41 = OpExtInst %6 %1 Exp %40
+ %42 = OpCompositeConstruct %15 %35 %37 %39 %41
+ OpBranch %14
+ %14 = OpLabel
+ %45 = OpPhi %15 %27 %13 %42 %28
+ OpStore %44 %45
+ OpReturn
+ OpFunctionEnd
+
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 46
++; Bound: 47
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %8 %44
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %8 Location 0
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %18 RelaxedPrecision
+ OpDecorate %19 RelaxedPrecision
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %23 RelaxedPrecision
+ OpDecorate %24 RelaxedPrecision
+ OpDecorate %25 RelaxedPrecision
+ OpDecorate %26 RelaxedPrecision
+ OpDecorate %27 RelaxedPrecision
+ OpDecorate %28 RelaxedPrecision
+ OpDecorate %29 RelaxedPrecision
+ OpDecorate %30 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %33 RelaxedPrecision
+ OpDecorate %34 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %40 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %44 RelaxedPrecision
+ OpDecorate %44 Location 0
+ OpDecorate %45 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Input %6
+ %8 = OpVariable %7 Input
+ %10 = OpConstant %6 0
+ %11 = OpTypeBool
+ %15 = OpTypeVector %6 4
+ %16 = OpTypePointer Function %15
+ %21 = OpConstant %6 -0.5
+ %22 = OpConstant %6 -0.300000012
+ %38 = OpConstant %6 0.5
+ %43 = OpTypePointer Output %15
+ %44 = OpVariable %43 Output
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %9 = OpLoad %6 %8
+ %12 = OpFOrdLessThanEqual %11 %9 %10
+ OpSelectionMerge %14 None
+ OpBranchConditional %12 %13 %32
+ %32 = OpLabel
+ %33 = OpLoad %6 %8
+ %34 = OpExtInst %6 %1 Sqrt %33
+ %35 = OpLoad %6 %8
+ %36 = OpExtInst %6 %1 FSign %35
+ %37 = OpLoad %6 %8
+ %39 = OpExtInst %6 %1 FMax %37 %38
+ %40 = OpLoad %6 %8
+ %41 = OpExtInst %6 %1 Floor %40
+ %42 = OpCompositeConstruct %15 %34 %36 %39 %41
+ OpBranch %14
+ %13 = OpLabel
+ %18 = OpLoad %6 %8
+ %19 = OpExtInst %6 %1 Log %18
+ %20 = OpLoad %6 %8
+ %23 = OpExtInst %6 %1 FClamp %20 %21 %22
+ %24 = OpFMul %6 %19 %23
+ %25 = OpLoad %6 %8
+ %26 = OpExtInst %6 %1 Sin %25
+ %27 = OpLoad %6 %8
+ %28 = OpExtInst %6 %1 Cos %27
+ %29 = OpLoad %6 %8
+ %30 = OpExtInst %6 %1 Exp %29
+ %31 = OpCompositeConstruct %15 %24 %26 %28 %30
+ OpBranch %14
+ %14 = OpLabel
+-%45 = OpPhi %15 %31 %13 %42 %32
++%45 = OpPhi %15 %42 %32 %31 %13
+ OpStore %44 %45
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/reordered_if_blocks_dst.spvasm b/test/diff/diff_files/reordered_if_blocks_dst.spvasm
new file mode 100644
index 00000000..cd1d6d52
--- /dev/null
+++ b/test/diff/diff_files/reordered_if_blocks_dst.spvasm
@@ -0,0 +1,87 @@
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %8 %44
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "v"
+ OpName %44 "color"
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %8 Location 0
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %18 RelaxedPrecision
+ OpDecorate %19 RelaxedPrecision
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %21 RelaxedPrecision
+ OpDecorate %22 RelaxedPrecision
+ OpDecorate %24 RelaxedPrecision
+ OpDecorate %25 RelaxedPrecision
+ OpDecorate %26 RelaxedPrecision
+ OpDecorate %27 RelaxedPrecision
+ OpDecorate %29 RelaxedPrecision
+ OpDecorate %30 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %34 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %38 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %40 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %44 RelaxedPrecision
+ OpDecorate %44 Location 0
+ OpDecorate %45 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Input %6
+ %8 = OpVariable %7 Input
+ %10 = OpConstant %6 0
+ %11 = OpTypeBool
+ %15 = OpTypeVector %6 4
+ %16 = OpTypePointer Function %15
+ %23 = OpConstant %6 0.5
+ %32 = OpConstant %6 -0.5
+ %33 = OpConstant %6 -0.300000012
+ %43 = OpTypePointer Output %15
+ %44 = OpVariable %43 Output
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %9 = OpLoad %6 %8
+ %12 = OpFOrdLessThanEqual %11 %9 %10
+ OpSelectionMerge %14 None
+ OpBranchConditional %12 %28 %13
+ %13 = OpLabel
+ %18 = OpLoad %6 %8
+ %19 = OpExtInst %6 %1 Sqrt %18
+ %20 = OpLoad %6 %8
+ %21 = OpExtInst %6 %1 FSign %20
+ %22 = OpLoad %6 %8
+ %24 = OpExtInst %6 %1 FMax %22 %23
+ %25 = OpLoad %6 %8
+ %26 = OpExtInst %6 %1 Floor %25
+ %27 = OpCompositeConstruct %15 %19 %21 %24 %26
+ OpBranch %14
+ %28 = OpLabel
+ %29 = OpLoad %6 %8
+ %30 = OpExtInst %6 %1 Log %29
+ %31 = OpLoad %6 %8
+ %34 = OpExtInst %6 %1 FClamp %31 %32 %33
+ %35 = OpFMul %6 %30 %34
+ %36 = OpLoad %6 %8
+ %37 = OpExtInst %6 %1 Sin %36
+ %38 = OpLoad %6 %8
+ %39 = OpExtInst %6 %1 Cos %38
+ %40 = OpLoad %6 %8
+ %41 = OpExtInst %6 %1 Exp %40
+ %42 = OpCompositeConstruct %15 %35 %37 %39 %41
+ OpBranch %14
+ %14 = OpLabel
+ %45 = OpPhi %15 %27 %13 %42 %28
+ OpStore %44 %45
+ OpReturn
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/reordered_if_blocks_src.spvasm b/test/diff/diff_files/reordered_if_blocks_src.spvasm
new file mode 100644
index 00000000..209cb45b
--- /dev/null
+++ b/test/diff/diff_files/reordered_if_blocks_src.spvasm
@@ -0,0 +1,87 @@
+;; Test where src and dst have the true and false blocks of an if reordered.
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %8 %44
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "v"
+ OpName %44 "color"
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %8 Location 0
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %18 RelaxedPrecision
+ OpDecorate %19 RelaxedPrecision
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %23 RelaxedPrecision
+ OpDecorate %24 RelaxedPrecision
+ OpDecorate %25 RelaxedPrecision
+ OpDecorate %26 RelaxedPrecision
+ OpDecorate %27 RelaxedPrecision
+ OpDecorate %28 RelaxedPrecision
+ OpDecorate %29 RelaxedPrecision
+ OpDecorate %30 RelaxedPrecision
+ OpDecorate %31 RelaxedPrecision
+ OpDecorate %33 RelaxedPrecision
+ OpDecorate %34 RelaxedPrecision
+ OpDecorate %35 RelaxedPrecision
+ OpDecorate %36 RelaxedPrecision
+ OpDecorate %37 RelaxedPrecision
+ OpDecorate %39 RelaxedPrecision
+ OpDecorate %40 RelaxedPrecision
+ OpDecorate %41 RelaxedPrecision
+ OpDecorate %42 RelaxedPrecision
+ OpDecorate %44 RelaxedPrecision
+ OpDecorate %44 Location 0
+ OpDecorate %45 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Input %6
+ %8 = OpVariable %7 Input
+ %10 = OpConstant %6 0
+ %11 = OpTypeBool
+ %15 = OpTypeVector %6 4
+ %16 = OpTypePointer Function %15
+ %21 = OpConstant %6 -0.5
+ %22 = OpConstant %6 -0.300000012
+ %38 = OpConstant %6 0.5
+ %43 = OpTypePointer Output %15
+ %44 = OpVariable %43 Output
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %9 = OpLoad %6 %8
+ %12 = OpFOrdLessThanEqual %11 %9 %10
+ OpSelectionMerge %14 None
+ OpBranchConditional %12 %13 %32
+ %13 = OpLabel
+ %18 = OpLoad %6 %8
+ %19 = OpExtInst %6 %1 Log %18
+ %20 = OpLoad %6 %8
+ %23 = OpExtInst %6 %1 FClamp %20 %21 %22
+ %24 = OpFMul %6 %19 %23
+ %25 = OpLoad %6 %8
+ %26 = OpExtInst %6 %1 Sin %25
+ %27 = OpLoad %6 %8
+ %28 = OpExtInst %6 %1 Cos %27
+ %29 = OpLoad %6 %8
+ %30 = OpExtInst %6 %1 Exp %29
+ %31 = OpCompositeConstruct %15 %24 %26 %28 %30
+ OpBranch %14
+ %32 = OpLabel
+ %33 = OpLoad %6 %8
+ %34 = OpExtInst %6 %1 Sqrt %33
+ %35 = OpLoad %6 %8
+ %36 = OpExtInst %6 %1 FSign %35
+ %37 = OpLoad %6 %8
+ %39 = OpExtInst %6 %1 FMax %37 %38
+ %40 = OpLoad %6 %8
+ %41 = OpExtInst %6 %1 Floor %40
+ %42 = OpCompositeConstruct %15 %34 %36 %39 %41
+ OpBranch %14
+ %14 = OpLabel
+ %45 = OpPhi %15 %31 %13 %42 %32
+ OpStore %44 %45
+ OpReturn
+ OpFunctionEnd
diff --git a/test/diff/diff_files/reordered_switch_blocks_autogen.cpp b/test/diff/diff_files/reordered_switch_blocks_autogen.cpp
new file mode 100644
index 00000000..c0ba48d1
--- /dev/null
+++ b/test/diff/diff_files/reordered_switch_blocks_autogen.cpp
@@ -0,0 +1,582 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Test where src and dst have cases of a switch in different order.
+constexpr char kSrc[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %7 "BufferIn"
+ OpMemberName %7 0 "i"
+ OpName %9 ""
+ OpName %23 "BufferOut"
+ OpMemberName %23 0 "o"
+ OpName %25 ""
+ OpMemberDecorate %7 0 Offset 0
+ OpDecorate %7 Block
+ OpDecorate %9 DescriptorSet 0
+ OpDecorate %9 Binding 0
+ OpMemberDecorate %23 0 Offset 0
+ OpDecorate %23 BufferBlock
+ OpDecorate %25 DescriptorSet 0
+ OpDecorate %25 Binding 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpTypeStruct %6
+ %8 = OpTypePointer Uniform %7
+ %9 = OpVariable %8 Uniform
+ %10 = OpTypeInt 32 1
+ %11 = OpConstant %10 0
+ %12 = OpTypePointer Uniform %6
+ %23 = OpTypeStruct %6
+ %24 = OpTypePointer Uniform %23
+ %25 = OpVariable %24 Uniform
+ %28 = OpConstant %10 1
+ %34 = OpConstant %6 2
+ %52 = OpConstant %6 1
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %13 = OpAccessChain %12 %9 %11
+ %14 = OpLoad %6 %13
+ OpSelectionMerge %22 None
+ OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20
+ %21 = OpLabel
+ %54 = OpAccessChain %12 %25 %11
+ %55 = OpLoad %6 %54
+ %56 = OpIAdd %6 %55 %34
+ %57 = OpAccessChain %12 %25 %11
+ OpStore %57 %56
+ OpBranch %22
+ %15 = OpLabel
+ %26 = OpAccessChain %12 %25 %11
+ %27 = OpLoad %6 %26
+ %29 = OpIAdd %6 %27 %28
+ OpStore %26 %29
+ OpBranch %22
+ %16 = OpLabel
+ %31 = OpAccessChain %12 %25 %11
+ %32 = OpLoad %6 %31
+ %33 = OpISub %6 %32 %28
+ OpStore %31 %33
+ OpBranch %17
+ %17 = OpLabel
+ %35 = OpAccessChain %12 %25 %11
+ %36 = OpLoad %6 %35
+ %37 = OpIMul %6 %36 %34
+ %38 = OpAccessChain %12 %25 %11
+ OpStore %38 %37
+ OpBranch %22
+ %18 = OpLabel
+ %40 = OpAccessChain %12 %25 %11
+ %41 = OpLoad %6 %40
+ %42 = OpUDiv %6 %41 %34
+ %43 = OpAccessChain %12 %25 %11
+ OpStore %43 %42
+ OpBranch %22
+ %19 = OpLabel
+ %45 = OpAccessChain %12 %25 %11
+ %46 = OpLoad %6 %45
+ %47 = OpAccessChain %12 %25 %11
+ %48 = OpLoad %6 %47
+ %49 = OpIMul %6 %46 %48
+ %50 = OpAccessChain %12 %25 %11
+ OpStore %50 %49
+ OpBranch %22
+ %20 = OpLabel
+ %53 = OpAccessChain %12 %25 %11
+ OpStore %53 %52
+ OpBranch %21
+ %22 = OpLabel
+ OpReturn
+ OpFunctionEnd)";
+constexpr char kDst[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %7 "BufferIn"
+ OpMemberName %7 0 "i"
+ OpName %9 ""
+ OpName %23 "BufferOut"
+ OpMemberName %23 0 "o"
+ OpName %25 ""
+ OpMemberDecorate %7 0 Offset 0
+ OpDecorate %7 Block
+ OpDecorate %9 DescriptorSet 0
+ OpDecorate %9 Binding 0
+ OpMemberDecorate %23 0 Offset 0
+ OpDecorate %23 BufferBlock
+ OpDecorate %25 DescriptorSet 0
+ OpDecorate %25 Binding 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpTypeStruct %6
+ %8 = OpTypePointer Uniform %7
+ %9 = OpVariable %8 Uniform
+ %10 = OpTypeInt 32 1
+ %11 = OpConstant %10 0
+ %12 = OpTypePointer Uniform %6
+ %23 = OpTypeStruct %6
+ %24 = OpTypePointer Uniform %23
+ %25 = OpVariable %24 Uniform
+ %28 = OpConstant %10 1
+ %34 = OpConstant %6 2
+ %52 = OpConstant %6 1
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %13 = OpAccessChain %12 %9 %11
+ %14 = OpLoad %6 %13
+ OpSelectionMerge %22 None
+ OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20
+ %17 = OpLabel
+ %35 = OpAccessChain %12 %25 %11
+ %36 = OpLoad %6 %35
+ %37 = OpIMul %6 %36 %34
+ %38 = OpAccessChain %12 %25 %11
+ OpStore %38 %37
+ OpBranch %22
+ %18 = OpLabel
+ %40 = OpAccessChain %12 %25 %11
+ %41 = OpLoad %6 %40
+ %42 = OpUDiv %6 %41 %34
+ %43 = OpAccessChain %12 %25 %11
+ OpStore %43 %42
+ OpBranch %22
+ %21 = OpLabel
+ %54 = OpAccessChain %12 %25 %11
+ %55 = OpLoad %6 %54
+ %56 = OpIAdd %6 %55 %34
+ %57 = OpAccessChain %12 %25 %11
+ OpStore %57 %56
+ OpBranch %22
+ %20 = OpLabel
+ %53 = OpAccessChain %12 %25 %11
+ OpStore %53 %52
+ OpBranch %21
+ %15 = OpLabel
+ %26 = OpAccessChain %12 %25 %11
+ %27 = OpLoad %6 %26
+ %29 = OpIAdd %6 %27 %28
+ OpStore %26 %29
+ OpBranch %22
+ %19 = OpLabel
+ %45 = OpAccessChain %12 %25 %11
+ %46 = OpLoad %6 %45
+ %47 = OpAccessChain %12 %25 %11
+ %48 = OpLoad %6 %47
+ %49 = OpIMul %6 %46 %48
+ %50 = OpAccessChain %12 %25 %11
+ OpStore %50 %49
+ OpBranch %22
+ %16 = OpLabel
+ %31 = OpAccessChain %12 %25 %11
+ %32 = OpLoad %6 %31
+ %33 = OpISub %6 %32 %28
+ OpStore %31 %33
+ OpBranch %17
+ %22 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+TEST(DiffTest, ReorderedSwitchBlocks) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 58
++; Bound: 62
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %7 "BufferIn"
+ OpMemberName %7 0 "i"
+ OpName %9 ""
+ OpName %23 "BufferOut"
+ OpMemberName %23 0 "o"
+ OpName %25 ""
+ OpMemberDecorate %7 0 Offset 0
+ OpDecorate %7 Block
+ OpDecorate %9 DescriptorSet 0
+ OpDecorate %9 Binding 0
+ OpMemberDecorate %23 0 Offset 0
+ OpDecorate %23 BufferBlock
+ OpDecorate %25 DescriptorSet 0
+ OpDecorate %25 Binding 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpTypeStruct %6
+ %8 = OpTypePointer Uniform %7
+ %9 = OpVariable %8 Uniform
+ %10 = OpTypeInt 32 1
+ %11 = OpConstant %10 0
+ %12 = OpTypePointer Uniform %6
+ %23 = OpTypeStruct %6
+ %24 = OpTypePointer Uniform %23
+ %25 = OpVariable %24 Uniform
+ %28 = OpConstant %10 1
+ %34 = OpConstant %6 2
+ %52 = OpConstant %6 1
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %13 = OpAccessChain %12 %9 %11
+ %14 = OpLoad %6 %13
+ OpSelectionMerge %22 None
+ OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20
+ %20 = OpLabel
+ %53 = OpAccessChain %12 %25 %11
+ OpStore %53 %52
+ OpBranch %21
+ %19 = OpLabel
+ %45 = OpAccessChain %12 %25 %11
+ %46 = OpLoad %6 %45
+ %47 = OpAccessChain %12 %25 %11
+ %48 = OpLoad %6 %47
+ %49 = OpIMul %6 %46 %48
+ %50 = OpAccessChain %12 %25 %11
+ OpStore %50 %49
+ OpBranch %22
+ %18 = OpLabel
+ %40 = OpAccessChain %12 %25 %11
+ %41 = OpLoad %6 %40
+ %42 = OpUDiv %6 %41 %34
+ %43 = OpAccessChain %12 %25 %11
+ OpStore %43 %42
+ OpBranch %22
+ %16 = OpLabel
+ %31 = OpAccessChain %12 %25 %11
+ %32 = OpLoad %6 %31
+ %33 = OpISub %6 %32 %28
+ OpStore %31 %33
+ OpBranch %17
+ %17 = OpLabel
+ %35 = OpAccessChain %12 %25 %11
+ %36 = OpLoad %6 %35
+ %37 = OpIMul %6 %36 %34
+ %38 = OpAccessChain %12 %25 %11
+ OpStore %38 %37
+ OpBranch %22
+ %15 = OpLabel
+ %26 = OpAccessChain %12 %25 %11
+ %27 = OpLoad %6 %26
+ %29 = OpIAdd %6 %27 %28
+ OpStore %26 %29
+ OpBranch %22
+ %21 = OpLabel
+ %54 = OpAccessChain %12 %25 %11
+ %55 = OpLoad %6 %54
+ %56 = OpIAdd %6 %55 %34
+ %57 = OpAccessChain %12 %25 %11
+ OpStore %57 %56
+ OpBranch %22
+ %22 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, ReorderedSwitchBlocksNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpMemberDecorate %7 0 Offset 0
+ OpDecorate %7 Block
+ OpDecorate %9 DescriptorSet 0
+ OpDecorate %9 Binding 0
+ OpMemberDecorate %23 0 Offset 0
+ OpDecorate %23 BufferBlock
+ OpDecorate %25 DescriptorSet 0
+ OpDecorate %25 Binding 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpTypeStruct %6
+ %8 = OpTypePointer Uniform %7
+ %9 = OpVariable %8 Uniform
+ %10 = OpTypeInt 32 1
+ %11 = OpConstant %10 0
+ %12 = OpTypePointer Uniform %6
+ %23 = OpTypeStruct %6
+ %24 = OpTypePointer Uniform %23
+ %25 = OpVariable %24 Uniform
+ %28 = OpConstant %10 1
+ %34 = OpConstant %6 2
+ %52 = OpConstant %6 1
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %13 = OpAccessChain %12 %9 %11
+ %14 = OpLoad %6 %13
+ OpSelectionMerge %22 None
+ OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20
+ %21 = OpLabel
+ %54 = OpAccessChain %12 %25 %11
+ %55 = OpLoad %6 %54
+ %56 = OpIAdd %6 %55 %34
+ %57 = OpAccessChain %12 %25 %11
+ OpStore %57 %56
+ OpBranch %22
+ %15 = OpLabel
+ %26 = OpAccessChain %12 %25 %11
+ %27 = OpLoad %6 %26
+ %29 = OpIAdd %6 %27 %28
+ OpStore %26 %29
+ OpBranch %22
+ %16 = OpLabel
+ %31 = OpAccessChain %12 %25 %11
+ %32 = OpLoad %6 %31
+ %33 = OpISub %6 %32 %28
+ OpStore %31 %33
+ OpBranch %17
+ %17 = OpLabel
+ %35 = OpAccessChain %12 %25 %11
+ %36 = OpLoad %6 %35
+ %37 = OpIMul %6 %36 %34
+ %38 = OpAccessChain %12 %25 %11
+ OpStore %38 %37
+ OpBranch %22
+ %18 = OpLabel
+ %40 = OpAccessChain %12 %25 %11
+ %41 = OpLoad %6 %40
+ %42 = OpUDiv %6 %41 %34
+ %43 = OpAccessChain %12 %25 %11
+ OpStore %43 %42
+ OpBranch %22
+ %19 = OpLabel
+ %45 = OpAccessChain %12 %25 %11
+ %46 = OpLoad %6 %45
+ %47 = OpAccessChain %12 %25 %11
+ %48 = OpLoad %6 %47
+ %49 = OpIMul %6 %46 %48
+ %50 = OpAccessChain %12 %25 %11
+ OpStore %50 %49
+ OpBranch %22
+ %20 = OpLabel
+ %53 = OpAccessChain %12 %25 %11
+ OpStore %53 %52
+ OpBranch %21
+ %22 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpMemberDecorate %7 0 Offset 0
+ OpDecorate %7 Block
+ OpDecorate %9 DescriptorSet 0
+ OpDecorate %9 Binding 0
+ OpMemberDecorate %23 0 Offset 0
+ OpDecorate %23 BufferBlock
+ OpDecorate %25 DescriptorSet 0
+ OpDecorate %25 Binding 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpTypeStruct %6
+ %8 = OpTypePointer Uniform %7
+ %9 = OpVariable %8 Uniform
+ %10 = OpTypeInt 32 1
+ %11 = OpConstant %10 0
+ %12 = OpTypePointer Uniform %6
+ %23 = OpTypeStruct %6
+ %24 = OpTypePointer Uniform %23
+ %25 = OpVariable %24 Uniform
+ %28 = OpConstant %10 1
+ %34 = OpConstant %6 2
+ %52 = OpConstant %6 1
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %13 = OpAccessChain %12 %9 %11
+ %14 = OpLoad %6 %13
+ OpSelectionMerge %22 None
+ OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20
+ %17 = OpLabel
+ %35 = OpAccessChain %12 %25 %11
+ %36 = OpLoad %6 %35
+ %37 = OpIMul %6 %36 %34
+ %38 = OpAccessChain %12 %25 %11
+ OpStore %38 %37
+ OpBranch %22
+ %18 = OpLabel
+ %40 = OpAccessChain %12 %25 %11
+ %41 = OpLoad %6 %40
+ %42 = OpUDiv %6 %41 %34
+ %43 = OpAccessChain %12 %25 %11
+ OpStore %43 %42
+ OpBranch %22
+ %21 = OpLabel
+ %54 = OpAccessChain %12 %25 %11
+ %55 = OpLoad %6 %54
+ %56 = OpIAdd %6 %55 %34
+ %57 = OpAccessChain %12 %25 %11
+ OpStore %57 %56
+ OpBranch %22
+ %20 = OpLabel
+ %53 = OpAccessChain %12 %25 %11
+ OpStore %53 %52
+ OpBranch %21
+ %15 = OpLabel
+ %26 = OpAccessChain %12 %25 %11
+ %27 = OpLoad %6 %26
+ %29 = OpIAdd %6 %27 %28
+ OpStore %26 %29
+ OpBranch %22
+ %19 = OpLabel
+ %45 = OpAccessChain %12 %25 %11
+ %46 = OpLoad %6 %45
+ %47 = OpAccessChain %12 %25 %11
+ %48 = OpLoad %6 %47
+ %49 = OpIMul %6 %46 %48
+ %50 = OpAccessChain %12 %25 %11
+ OpStore %50 %49
+ OpBranch %22
+ %16 = OpLabel
+ %31 = OpAccessChain %12 %25 %11
+ %32 = OpLoad %6 %31
+ %33 = OpISub %6 %32 %28
+ OpStore %31 %33
+ OpBranch %17
+ %22 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 58
++; Bound: 62
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpMemberDecorate %7 0 Offset 0
+ OpDecorate %7 Block
+ OpDecorate %9 DescriptorSet 0
+ OpDecorate %9 Binding 0
+ OpMemberDecorate %23 0 Offset 0
+ OpDecorate %23 BufferBlock
+ OpDecorate %25 DescriptorSet 0
+ OpDecorate %25 Binding 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpTypeStruct %6
+ %8 = OpTypePointer Uniform %7
+ %9 = OpVariable %8 Uniform
+ %10 = OpTypeInt 32 1
+ %11 = OpConstant %10 0
+ %12 = OpTypePointer Uniform %6
+ %23 = OpTypeStruct %6
+ %24 = OpTypePointer Uniform %23
+ %25 = OpVariable %24 Uniform
+ %28 = OpConstant %10 1
+ %34 = OpConstant %6 2
+ %52 = OpConstant %6 1
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %13 = OpAccessChain %12 %9 %11
+ %14 = OpLoad %6 %13
+ OpSelectionMerge %22 None
+ OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20
+ %20 = OpLabel
+ %53 = OpAccessChain %12 %25 %11
+ OpStore %53 %52
+ OpBranch %21
+ %19 = OpLabel
+ %45 = OpAccessChain %12 %25 %11
+ %46 = OpLoad %6 %45
+ %47 = OpAccessChain %12 %25 %11
+ %48 = OpLoad %6 %47
+ %49 = OpIMul %6 %46 %48
+ %50 = OpAccessChain %12 %25 %11
+ OpStore %50 %49
+ OpBranch %22
+ %18 = OpLabel
+ %40 = OpAccessChain %12 %25 %11
+ %41 = OpLoad %6 %40
+ %42 = OpUDiv %6 %41 %34
+ %43 = OpAccessChain %12 %25 %11
+ OpStore %43 %42
+ OpBranch %22
+ %16 = OpLabel
+ %31 = OpAccessChain %12 %25 %11
+ %32 = OpLoad %6 %31
+ %33 = OpISub %6 %32 %28
+ OpStore %31 %33
+ OpBranch %17
+ %17 = OpLabel
+ %35 = OpAccessChain %12 %25 %11
+ %36 = OpLoad %6 %35
+ %37 = OpIMul %6 %36 %34
+ %38 = OpAccessChain %12 %25 %11
+ OpStore %38 %37
+ OpBranch %22
+ %15 = OpLabel
+ %26 = OpAccessChain %12 %25 %11
+ %27 = OpLoad %6 %26
+ %29 = OpIAdd %6 %27 %28
+ OpStore %26 %29
+ OpBranch %22
+ %21 = OpLabel
+ %54 = OpAccessChain %12 %25 %11
+ %55 = OpLoad %6 %54
+ %56 = OpIAdd %6 %55 %34
+ %57 = OpAccessChain %12 %25 %11
+ OpStore %57 %56
+ OpBranch %22
+ %22 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/reordered_switch_blocks_dst.spvasm b/test/diff/diff_files/reordered_switch_blocks_dst.spvasm
new file mode 100644
index 00000000..8eabb4ef
--- /dev/null
+++ b/test/diff/diff_files/reordered_switch_blocks_dst.spvasm
@@ -0,0 +1,91 @@
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %7 "BufferIn"
+ OpMemberName %7 0 "i"
+ OpName %9 ""
+ OpName %23 "BufferOut"
+ OpMemberName %23 0 "o"
+ OpName %25 ""
+ OpMemberDecorate %7 0 Offset 0
+ OpDecorate %7 Block
+ OpDecorate %9 DescriptorSet 0
+ OpDecorate %9 Binding 0
+ OpMemberDecorate %23 0 Offset 0
+ OpDecorate %23 BufferBlock
+ OpDecorate %25 DescriptorSet 0
+ OpDecorate %25 Binding 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpTypeStruct %6
+ %8 = OpTypePointer Uniform %7
+ %9 = OpVariable %8 Uniform
+ %10 = OpTypeInt 32 1
+ %11 = OpConstant %10 0
+ %12 = OpTypePointer Uniform %6
+ %23 = OpTypeStruct %6
+ %24 = OpTypePointer Uniform %23
+ %25 = OpVariable %24 Uniform
+ %28 = OpConstant %10 1
+ %34 = OpConstant %6 2
+ %52 = OpConstant %6 1
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %13 = OpAccessChain %12 %9 %11
+ %14 = OpLoad %6 %13
+ OpSelectionMerge %22 None
+ OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20
+ %17 = OpLabel
+ %35 = OpAccessChain %12 %25 %11
+ %36 = OpLoad %6 %35
+ %37 = OpIMul %6 %36 %34
+ %38 = OpAccessChain %12 %25 %11
+ OpStore %38 %37
+ OpBranch %22
+ %18 = OpLabel
+ %40 = OpAccessChain %12 %25 %11
+ %41 = OpLoad %6 %40
+ %42 = OpUDiv %6 %41 %34
+ %43 = OpAccessChain %12 %25 %11
+ OpStore %43 %42
+ OpBranch %22
+ %21 = OpLabel
+ %54 = OpAccessChain %12 %25 %11
+ %55 = OpLoad %6 %54
+ %56 = OpIAdd %6 %55 %34
+ %57 = OpAccessChain %12 %25 %11
+ OpStore %57 %56
+ OpBranch %22
+ %20 = OpLabel
+ %53 = OpAccessChain %12 %25 %11
+ OpStore %53 %52
+ OpBranch %21
+ %15 = OpLabel
+ %26 = OpAccessChain %12 %25 %11
+ %27 = OpLoad %6 %26
+ %29 = OpIAdd %6 %27 %28
+ OpStore %26 %29
+ OpBranch %22
+ %19 = OpLabel
+ %45 = OpAccessChain %12 %25 %11
+ %46 = OpLoad %6 %45
+ %47 = OpAccessChain %12 %25 %11
+ %48 = OpLoad %6 %47
+ %49 = OpIMul %6 %46 %48
+ %50 = OpAccessChain %12 %25 %11
+ OpStore %50 %49
+ OpBranch %22
+ %16 = OpLabel
+ %31 = OpAccessChain %12 %25 %11
+ %32 = OpLoad %6 %31
+ %33 = OpISub %6 %32 %28
+ OpStore %31 %33
+ OpBranch %17
+ %22 = OpLabel
+ OpReturn
+ OpFunctionEnd
diff --git a/test/diff/diff_files/reordered_switch_blocks_src.spvasm b/test/diff/diff_files/reordered_switch_blocks_src.spvasm
new file mode 100644
index 00000000..e3772282
--- /dev/null
+++ b/test/diff/diff_files/reordered_switch_blocks_src.spvasm
@@ -0,0 +1,92 @@
+;; Test where src and dst have cases of a switch in different order.
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %7 "BufferIn"
+ OpMemberName %7 0 "i"
+ OpName %9 ""
+ OpName %23 "BufferOut"
+ OpMemberName %23 0 "o"
+ OpName %25 ""
+ OpMemberDecorate %7 0 Offset 0
+ OpDecorate %7 Block
+ OpDecorate %9 DescriptorSet 0
+ OpDecorate %9 Binding 0
+ OpMemberDecorate %23 0 Offset 0
+ OpDecorate %23 BufferBlock
+ OpDecorate %25 DescriptorSet 0
+ OpDecorate %25 Binding 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpTypeStruct %6
+ %8 = OpTypePointer Uniform %7
+ %9 = OpVariable %8 Uniform
+ %10 = OpTypeInt 32 1
+ %11 = OpConstant %10 0
+ %12 = OpTypePointer Uniform %6
+ %23 = OpTypeStruct %6
+ %24 = OpTypePointer Uniform %23
+ %25 = OpVariable %24 Uniform
+ %28 = OpConstant %10 1
+ %34 = OpConstant %6 2
+ %52 = OpConstant %6 1
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %13 = OpAccessChain %12 %9 %11
+ %14 = OpLoad %6 %13
+ OpSelectionMerge %22 None
+ OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20
+ %21 = OpLabel
+ %54 = OpAccessChain %12 %25 %11
+ %55 = OpLoad %6 %54
+ %56 = OpIAdd %6 %55 %34
+ %57 = OpAccessChain %12 %25 %11
+ OpStore %57 %56
+ OpBranch %22
+ %15 = OpLabel
+ %26 = OpAccessChain %12 %25 %11
+ %27 = OpLoad %6 %26
+ %29 = OpIAdd %6 %27 %28
+ OpStore %26 %29
+ OpBranch %22
+ %16 = OpLabel
+ %31 = OpAccessChain %12 %25 %11
+ %32 = OpLoad %6 %31
+ %33 = OpISub %6 %32 %28
+ OpStore %31 %33
+ OpBranch %17
+ %17 = OpLabel
+ %35 = OpAccessChain %12 %25 %11
+ %36 = OpLoad %6 %35
+ %37 = OpIMul %6 %36 %34
+ %38 = OpAccessChain %12 %25 %11
+ OpStore %38 %37
+ OpBranch %22
+ %18 = OpLabel
+ %40 = OpAccessChain %12 %25 %11
+ %41 = OpLoad %6 %40
+ %42 = OpUDiv %6 %41 %34
+ %43 = OpAccessChain %12 %25 %11
+ OpStore %43 %42
+ OpBranch %22
+ %19 = OpLabel
+ %45 = OpAccessChain %12 %25 %11
+ %46 = OpLoad %6 %45
+ %47 = OpAccessChain %12 %25 %11
+ %48 = OpLoad %6 %47
+ %49 = OpIMul %6 %46 %48
+ %50 = OpAccessChain %12 %25 %11
+ OpStore %50 %49
+ OpBranch %22
+ %20 = OpLabel
+ %53 = OpAccessChain %12 %25 %11
+ OpStore %53 %52
+ OpBranch %21
+ %22 = OpLabel
+ OpReturn
+ OpFunctionEnd
diff --git a/test/diff/diff_files/small_functions_small_diffs_autogen.cpp b/test/diff/diff_files/small_functions_small_diffs_autogen.cpp
new file mode 100644
index 00000000..c1a91009
--- /dev/null
+++ b/test/diff/diff_files/small_functions_small_diffs_autogen.cpp
@@ -0,0 +1,747 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Test where src and dst have many small functions with small differences.
+constexpr char kSrc[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "f1("
+ OpName %8 "f2("
+ OpName %10 "f3("
+ OpName %12 "f4("
+ OpName %14 "f5("
+ OpName %17 "BufferOut"
+ OpMemberName %17 0 "o"
+ OpName %19 ""
+ OpName %22 "BufferIn"
+ OpMemberName %22 0 "i"
+ OpName %24 ""
+ OpMemberDecorate %17 0 Offset 0
+ OpDecorate %17 BufferBlock
+ OpDecorate %19 DescriptorSet 0
+ OpDecorate %19 Binding 1
+ OpMemberDecorate %22 0 Offset 0
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %16 = OpTypeInt 32 0
+ %17 = OpTypeStruct %16
+ %18 = OpTypePointer Uniform %17
+ %19 = OpVariable %18 Uniform
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 0
+ %22 = OpTypeStruct %16
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %16
+ %31 = OpConstant %20 1
+ %36 = OpConstant %16 2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %45 = OpFunctionCall %2 %6
+ %46 = OpFunctionCall %2 %8
+ %47 = OpFunctionCall %2 %10
+ %48 = OpFunctionCall %2 %12
+ %49 = OpFunctionCall %2 %14
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %26 = OpAccessChain %25 %24 %21
+ %27 = OpLoad %16 %26
+ %28 = OpAccessChain %25 %19 %21
+ OpStore %28 %27
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %29 = OpAccessChain %25 %19 %21
+ %30 = OpLoad %16 %29
+ %32 = OpIAdd %16 %30 %31
+ OpStore %29 %32
+ OpReturn
+ OpFunctionEnd
+ %10 = OpFunction %2 None %3
+ %11 = OpLabel
+ %33 = OpAccessChain %25 %19 %21
+ %34 = OpLoad %16 %33
+ %35 = OpISub %16 %34 %31
+ OpStore %33 %35
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %37 = OpAccessChain %25 %19 %21
+ %38 = OpLoad %16 %37
+ %39 = OpIMul %16 %38 %36
+ %40 = OpAccessChain %25 %19 %21
+ OpStore %40 %39
+ OpReturn
+ OpFunctionEnd
+ %14 = OpFunction %2 None %3
+ %15 = OpLabel
+ %41 = OpAccessChain %25 %19 %21
+ %42 = OpLoad %16 %41
+ %43 = OpUDiv %16 %42 %36
+ %44 = OpAccessChain %25 %19 %21
+ OpStore %44 %43
+ OpReturn
+ OpFunctionEnd
+)";
+constexpr char kDst[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "f1("
+ OpName %8 "f2("
+ OpName %10 "f3("
+ OpName %12 "f4("
+ OpName %14 "f5("
+ OpName %17 "BufferOut"
+ OpMemberName %17 0 "o"
+ OpName %19 ""
+ OpName %22 "BufferIn"
+ OpMemberName %22 0 "i"
+ OpName %24 ""
+ OpMemberDecorate %17 0 Offset 0
+ OpDecorate %17 BufferBlock
+ OpDecorate %19 DescriptorSet 0
+ OpDecorate %19 Binding 1
+ OpMemberDecorate %22 0 Offset 0
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %16 = OpTypeInt 32 0
+ %17 = OpTypeStruct %16
+ %18 = OpTypePointer Uniform %17
+ %19 = OpVariable %18 Uniform
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 0
+ %22 = OpTypeStruct %16
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %16
+ %31 = OpConstant %20 1
+ %36 = OpConstant %16 2
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %26 = OpAccessChain %25 %24 %21
+ %27 = OpLoad %16 %26
+ %28 = OpAccessChain %25 %19 %21
+ OpStore %28 %27
+ OpReturn
+ OpFunctionEnd
+ %14 = OpFunction %2 None %3
+ %15 = OpLabel
+ %41 = OpAccessChain %25 %19 %21
+ %42 = OpLoad %16 %41
+ %43 = OpIAdd %16 %42 %36
+ %44 = OpAccessChain %25 %19 %21
+ OpStore %44 %43
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %29 = OpAccessChain %25 %19 %21
+ %30 = OpLoad %16 %29
+ %32 = OpISub %16 %30 %31
+ OpStore %29 %32
+ OpReturn
+ OpFunctionEnd
+ %10 = OpFunction %2 None %3
+ %11 = OpLabel
+ %33 = OpAccessChain %25 %19 %21
+ %34 = OpLoad %16 %33
+ %35 = OpIAdd %16 %34 %31
+ OpStore %33 %35
+ OpReturn
+ OpFunctionEnd
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %45 = OpFunctionCall %2 %6
+ %46 = OpFunctionCall %2 %8
+ %47 = OpFunctionCall %2 %10
+ %48 = OpFunctionCall %2 %12
+ %49 = OpFunctionCall %2 %14
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %37 = OpAccessChain %25 %19 %21
+ %38 = OpLoad %16 %37
+ %39 = OpISub %16 %38 %36
+ %40 = OpAccessChain %25 %19 %21
+ OpStore %40 %39
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+TEST(DiffTest, SmallFunctionsSmallDiffs) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 50
++; Bound: 54
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "f1("
+ OpName %8 "f2("
+ OpName %10 "f3("
+ OpName %12 "f4("
+ OpName %14 "f5("
+ OpName %17 "BufferOut"
+ OpMemberName %17 0 "o"
+ OpName %19 ""
+ OpName %22 "BufferIn"
+ OpMemberName %22 0 "i"
+ OpName %24 ""
+ OpMemberDecorate %17 0 Offset 0
+ OpDecorate %17 BufferBlock
+ OpDecorate %19 DescriptorSet 0
+ OpDecorate %19 Binding 1
+ OpMemberDecorate %22 0 Offset 0
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %16 = OpTypeInt 32 0
+ %17 = OpTypeStruct %16
+ %18 = OpTypePointer Uniform %17
+ %19 = OpVariable %18 Uniform
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 0
+ %22 = OpTypeStruct %16
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %16
+ %31 = OpConstant %20 1
+ %36 = OpConstant %16 2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %45 = OpFunctionCall %2 %6
+ %46 = OpFunctionCall %2 %8
+ %47 = OpFunctionCall %2 %10
+ %48 = OpFunctionCall %2 %12
+ %49 = OpFunctionCall %2 %14
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %26 = OpAccessChain %25 %24 %21
+ %27 = OpLoad %16 %26
+ %28 = OpAccessChain %25 %19 %21
+ OpStore %28 %27
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %29 = OpAccessChain %25 %19 %21
+ %30 = OpLoad %16 %29
+-%32 = OpIAdd %16 %30 %31
++%50 = OpISub %16 %30 %31
+-OpStore %29 %32
++OpStore %29 %50
+ OpReturn
+ OpFunctionEnd
+ %10 = OpFunction %2 None %3
+ %11 = OpLabel
+ %33 = OpAccessChain %25 %19 %21
+ %34 = OpLoad %16 %33
+-%35 = OpISub %16 %34 %31
++%51 = OpIAdd %16 %34 %31
+-OpStore %33 %35
++OpStore %33 %51
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %37 = OpAccessChain %25 %19 %21
+ %38 = OpLoad %16 %37
+-%39 = OpIMul %16 %38 %36
++%52 = OpISub %16 %38 %36
+ %40 = OpAccessChain %25 %19 %21
+-OpStore %40 %39
++OpStore %40 %52
+ OpReturn
+ OpFunctionEnd
+ %14 = OpFunction %2 None %3
+ %15 = OpLabel
+ %41 = OpAccessChain %25 %19 %21
+ %42 = OpLoad %16 %41
+-%43 = OpUDiv %16 %42 %36
++%53 = OpIAdd %16 %42 %36
+ %44 = OpAccessChain %25 %19 %21
+-OpStore %44 %43
++OpStore %44 %53
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, SmallFunctionsSmallDiffsNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpMemberDecorate %17 0 Offset 0
+ OpDecorate %17 BufferBlock
+ OpDecorate %19 DescriptorSet 0
+ OpDecorate %19 Binding 1
+ OpMemberDecorate %22 0 Offset 0
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %16 = OpTypeInt 32 0
+ %17 = OpTypeStruct %16
+ %18 = OpTypePointer Uniform %17
+ %19 = OpVariable %18 Uniform
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 0
+ %22 = OpTypeStruct %16
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %16
+ %31 = OpConstant %20 1
+ %36 = OpConstant %16 2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %45 = OpFunctionCall %2 %6
+ %46 = OpFunctionCall %2 %8
+ %47 = OpFunctionCall %2 %10
+ %48 = OpFunctionCall %2 %12
+ %49 = OpFunctionCall %2 %14
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %26 = OpAccessChain %25 %24 %21
+ %27 = OpLoad %16 %26
+ %28 = OpAccessChain %25 %19 %21
+ OpStore %28 %27
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %29 = OpAccessChain %25 %19 %21
+ %30 = OpLoad %16 %29
+ %32 = OpIAdd %16 %30 %31
+ OpStore %29 %32
+ OpReturn
+ OpFunctionEnd
+ %10 = OpFunction %2 None %3
+ %11 = OpLabel
+ %33 = OpAccessChain %25 %19 %21
+ %34 = OpLoad %16 %33
+ %35 = OpISub %16 %34 %31
+ OpStore %33 %35
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %37 = OpAccessChain %25 %19 %21
+ %38 = OpLoad %16 %37
+ %39 = OpIMul %16 %38 %36
+ %40 = OpAccessChain %25 %19 %21
+ OpStore %40 %39
+ OpReturn
+ OpFunctionEnd
+ %14 = OpFunction %2 None %3
+ %15 = OpLabel
+ %41 = OpAccessChain %25 %19 %21
+ %42 = OpLoad %16 %41
+ %43 = OpUDiv %16 %42 %36
+ %44 = OpAccessChain %25 %19 %21
+ OpStore %44 %43
+ OpReturn
+ OpFunctionEnd
+
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpMemberDecorate %17 0 Offset 0
+ OpDecorate %17 BufferBlock
+ OpDecorate %19 DescriptorSet 0
+ OpDecorate %19 Binding 1
+ OpMemberDecorate %22 0 Offset 0
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %16 = OpTypeInt 32 0
+ %17 = OpTypeStruct %16
+ %18 = OpTypePointer Uniform %17
+ %19 = OpVariable %18 Uniform
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 0
+ %22 = OpTypeStruct %16
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %16
+ %31 = OpConstant %20 1
+ %36 = OpConstant %16 2
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %26 = OpAccessChain %25 %24 %21
+ %27 = OpLoad %16 %26
+ %28 = OpAccessChain %25 %19 %21
+ OpStore %28 %27
+ OpReturn
+ OpFunctionEnd
+ %14 = OpFunction %2 None %3
+ %15 = OpLabel
+ %41 = OpAccessChain %25 %19 %21
+ %42 = OpLoad %16 %41
+ %43 = OpIAdd %16 %42 %36
+ %44 = OpAccessChain %25 %19 %21
+ OpStore %44 %43
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %29 = OpAccessChain %25 %19 %21
+ %30 = OpLoad %16 %29
+ %32 = OpISub %16 %30 %31
+ OpStore %29 %32
+ OpReturn
+ OpFunctionEnd
+ %10 = OpFunction %2 None %3
+ %11 = OpLabel
+ %33 = OpAccessChain %25 %19 %21
+ %34 = OpLoad %16 %33
+ %35 = OpIAdd %16 %34 %31
+ OpStore %33 %35
+ OpReturn
+ OpFunctionEnd
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %45 = OpFunctionCall %2 %6
+ %46 = OpFunctionCall %2 %8
+ %47 = OpFunctionCall %2 %10
+ %48 = OpFunctionCall %2 %12
+ %49 = OpFunctionCall %2 %14
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %37 = OpAccessChain %25 %19 %21
+ %38 = OpLoad %16 %37
+ %39 = OpISub %16 %38 %36
+ %40 = OpAccessChain %25 %19 %21
+ OpStore %40 %39
+ OpReturn
+ OpFunctionEnd
+
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 50
++; Bound: 52
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpMemberDecorate %17 0 Offset 0
+ OpDecorate %17 BufferBlock
+ OpDecorate %19 DescriptorSet 0
+ OpDecorate %19 Binding 1
+ OpMemberDecorate %22 0 Offset 0
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %16 = OpTypeInt 32 0
+ %17 = OpTypeStruct %16
+ %18 = OpTypePointer Uniform %17
+ %19 = OpVariable %18 Uniform
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 0
+ %22 = OpTypeStruct %16
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %16
+ %31 = OpConstant %20 1
+ %36 = OpConstant %16 2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %45 = OpFunctionCall %2 %6
+-%46 = OpFunctionCall %2 %8
++%46 = OpFunctionCall %2 %10
+-%47 = OpFunctionCall %2 %10
++%47 = OpFunctionCall %2 %8
+ %48 = OpFunctionCall %2 %12
+ %49 = OpFunctionCall %2 %14
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %26 = OpAccessChain %25 %24 %21
+ %27 = OpLoad %16 %26
+ %28 = OpAccessChain %25 %19 %21
+ OpStore %28 %27
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %29 = OpAccessChain %25 %19 %21
+ %30 = OpLoad %16 %29
+ %32 = OpIAdd %16 %30 %31
+ OpStore %29 %32
+ OpReturn
+ OpFunctionEnd
+ %10 = OpFunction %2 None %3
+ %11 = OpLabel
+ %33 = OpAccessChain %25 %19 %21
+ %34 = OpLoad %16 %33
+ %35 = OpISub %16 %34 %31
+ OpStore %33 %35
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %37 = OpAccessChain %25 %19 %21
+ %38 = OpLoad %16 %37
+-%39 = OpIMul %16 %38 %36
++%50 = OpISub %16 %38 %36
+ %40 = OpAccessChain %25 %19 %21
+-OpStore %40 %39
++OpStore %40 %50
+ OpReturn
+ OpFunctionEnd
+ %14 = OpFunction %2 None %3
+ %15 = OpLabel
+ %41 = OpAccessChain %25 %19 %21
+ %42 = OpLoad %16 %41
+-%43 = OpUDiv %16 %42 %36
++%51 = OpIAdd %16 %42 %36
+ %44 = OpAccessChain %25 %19 %21
+-OpStore %44 %43
++OpStore %44 %51
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+TEST(DiffTest, SmallFunctionsSmallDiffsDumpIds) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 50
++; Bound: 54
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "f1("
+ OpName %8 "f2("
+ OpName %10 "f3("
+ OpName %12 "f4("
+ OpName %14 "f5("
+ OpName %17 "BufferOut"
+ OpMemberName %17 0 "o"
+ OpName %19 ""
+ OpName %22 "BufferIn"
+ OpMemberName %22 0 "i"
+ OpName %24 ""
+ OpMemberDecorate %17 0 Offset 0
+ OpDecorate %17 BufferBlock
+ OpDecorate %19 DescriptorSet 0
+ OpDecorate %19 Binding 1
+ OpMemberDecorate %22 0 Offset 0
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %16 = OpTypeInt 32 0
+ %17 = OpTypeStruct %16
+ %18 = OpTypePointer Uniform %17
+ %19 = OpVariable %18 Uniform
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 0
+ %22 = OpTypeStruct %16
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %16
+ %31 = OpConstant %20 1
+ %36 = OpConstant %16 2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %45 = OpFunctionCall %2 %6
+ %46 = OpFunctionCall %2 %8
+ %47 = OpFunctionCall %2 %10
+ %48 = OpFunctionCall %2 %12
+ %49 = OpFunctionCall %2 %14
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %26 = OpAccessChain %25 %24 %21
+ %27 = OpLoad %16 %26
+ %28 = OpAccessChain %25 %19 %21
+ OpStore %28 %27
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %29 = OpAccessChain %25 %19 %21
+ %30 = OpLoad %16 %29
+-%32 = OpIAdd %16 %30 %31
++%50 = OpISub %16 %30 %31
+-OpStore %29 %32
++OpStore %29 %50
+ OpReturn
+ OpFunctionEnd
+ %10 = OpFunction %2 None %3
+ %11 = OpLabel
+ %33 = OpAccessChain %25 %19 %21
+ %34 = OpLoad %16 %33
+-%35 = OpISub %16 %34 %31
++%51 = OpIAdd %16 %34 %31
+-OpStore %33 %35
++OpStore %33 %51
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %37 = OpAccessChain %25 %19 %21
+ %38 = OpLoad %16 %37
+-%39 = OpIMul %16 %38 %36
++%52 = OpISub %16 %38 %36
+ %40 = OpAccessChain %25 %19 %21
+-OpStore %40 %39
++OpStore %40 %52
+ OpReturn
+ OpFunctionEnd
+ %14 = OpFunction %2 None %3
+ %15 = OpLabel
+ %41 = OpAccessChain %25 %19 %21
+ %42 = OpLoad %16 %41
+-%43 = OpUDiv %16 %42 %36
++%53 = OpIAdd %16 %42 %36
+ %44 = OpAccessChain %25 %19 %21
+-OpStore %44 %43
++OpStore %44 %53
+ OpReturn
+ OpFunctionEnd
+ Src -> Dst
+ 1 -> 1 [ExtInstImport]
+ 2 -> 2 [TypeVoid]
+ 3 -> 3 [TypeFunction]
+ 4 -> 4 [Function]
+ 5 -> 5 [Label]
+ 6 -> 6 [Function]
+ 7 -> 7 [Label]
+ 8 -> 8 [Function]
+ 9 -> 9 [Label]
+ 10 -> 10 [Function]
+ 11 -> 11 [Label]
+ 12 -> 12 [Function]
+ 13 -> 13 [Label]
+ 14 -> 14 [Function]
+ 15 -> 15 [Label]
+ 16 -> 16 [TypeInt]
+ 17 -> 17 [TypeStruct]
+ 18 -> 18 [TypePointer]
+ 19 -> 19 [Variable]
+ 20 -> 20 [TypeInt]
+ 21 -> 21 [Constant]
+ 22 -> 22 [TypeStruct]
+ 23 -> 23 [TypePointer]
+ 24 -> 24 [Variable]
+ 25 -> 25 [TypePointer]
+ 26 -> 26 [AccessChain]
+ 27 -> 27 [Load]
+ 28 -> 28 [AccessChain]
+ 29 -> 29 [AccessChain]
+ 30 -> 30 [Load]
+ 31 -> 31 [Constant]
+ 32 -> 50 [IAdd]
+ 33 -> 33 [AccessChain]
+ 34 -> 34 [Load]
+ 35 -> 51 [ISub]
+ 36 -> 36 [Constant]
+ 37 -> 37 [AccessChain]
+ 38 -> 38 [Load]
+ 39 -> 52 [IMul]
+ 40 -> 40 [AccessChain]
+ 41 -> 41 [AccessChain]
+ 42 -> 42 [Load]
+ 43 -> 53 [UDiv]
+ 44 -> 44 [AccessChain]
+ 45 -> 45 [FunctionCall]
+ 46 -> 46 [FunctionCall]
+ 47 -> 47 [FunctionCall]
+ 48 -> 48 [FunctionCall]
+ 49 -> 49 [FunctionCall]
+)";
+ Options options;
+ options.dump_id_map = true;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/small_functions_small_diffs_dst.spvasm b/test/diff/diff_files/small_functions_small_diffs_dst.spvasm
new file mode 100644
index 00000000..fabf5692
--- /dev/null
+++ b/test/diff/diff_files/small_functions_small_diffs_dst.spvasm
@@ -0,0 +1,92 @@
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "f1("
+ OpName %8 "f2("
+ OpName %10 "f3("
+ OpName %12 "f4("
+ OpName %14 "f5("
+ OpName %17 "BufferOut"
+ OpMemberName %17 0 "o"
+ OpName %19 ""
+ OpName %22 "BufferIn"
+ OpMemberName %22 0 "i"
+ OpName %24 ""
+ OpMemberDecorate %17 0 Offset 0
+ OpDecorate %17 BufferBlock
+ OpDecorate %19 DescriptorSet 0
+ OpDecorate %19 Binding 1
+ OpMemberDecorate %22 0 Offset 0
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %16 = OpTypeInt 32 0
+ %17 = OpTypeStruct %16
+ %18 = OpTypePointer Uniform %17
+ %19 = OpVariable %18 Uniform
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 0
+ %22 = OpTypeStruct %16
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %16
+ %31 = OpConstant %20 1
+ %36 = OpConstant %16 2
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %26 = OpAccessChain %25 %24 %21
+ %27 = OpLoad %16 %26
+ %28 = OpAccessChain %25 %19 %21
+ OpStore %28 %27
+ OpReturn
+ OpFunctionEnd
+ %14 = OpFunction %2 None %3
+ %15 = OpLabel
+ %41 = OpAccessChain %25 %19 %21
+ %42 = OpLoad %16 %41
+ %43 = OpIAdd %16 %42 %36
+ %44 = OpAccessChain %25 %19 %21
+ OpStore %44 %43
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %29 = OpAccessChain %25 %19 %21
+ %30 = OpLoad %16 %29
+ %32 = OpISub %16 %30 %31
+ OpStore %29 %32
+ OpReturn
+ OpFunctionEnd
+ %10 = OpFunction %2 None %3
+ %11 = OpLabel
+ %33 = OpAccessChain %25 %19 %21
+ %34 = OpLoad %16 %33
+ %35 = OpIAdd %16 %34 %31
+ OpStore %33 %35
+ OpReturn
+ OpFunctionEnd
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %45 = OpFunctionCall %2 %6
+ %46 = OpFunctionCall %2 %8
+ %47 = OpFunctionCall %2 %10
+ %48 = OpFunctionCall %2 %12
+ %49 = OpFunctionCall %2 %14
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %37 = OpAccessChain %25 %19 %21
+ %38 = OpLoad %16 %37
+ %39 = OpISub %16 %38 %36
+ %40 = OpAccessChain %25 %19 %21
+ OpStore %40 %39
+ OpReturn
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/small_functions_small_diffs_src.spvasm b/test/diff/diff_files/small_functions_small_diffs_src.spvasm
new file mode 100644
index 00000000..895285b1
--- /dev/null
+++ b/test/diff/diff_files/small_functions_small_diffs_src.spvasm
@@ -0,0 +1,93 @@
+;; Test where src and dst have many small functions with small differences.
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "f1("
+ OpName %8 "f2("
+ OpName %10 "f3("
+ OpName %12 "f4("
+ OpName %14 "f5("
+ OpName %17 "BufferOut"
+ OpMemberName %17 0 "o"
+ OpName %19 ""
+ OpName %22 "BufferIn"
+ OpMemberName %22 0 "i"
+ OpName %24 ""
+ OpMemberDecorate %17 0 Offset 0
+ OpDecorate %17 BufferBlock
+ OpDecorate %19 DescriptorSet 0
+ OpDecorate %19 Binding 1
+ OpMemberDecorate %22 0 Offset 0
+ OpDecorate %22 Block
+ OpDecorate %24 DescriptorSet 0
+ OpDecorate %24 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %16 = OpTypeInt 32 0
+ %17 = OpTypeStruct %16
+ %18 = OpTypePointer Uniform %17
+ %19 = OpVariable %18 Uniform
+ %20 = OpTypeInt 32 1
+ %21 = OpConstant %20 0
+ %22 = OpTypeStruct %16
+ %23 = OpTypePointer Uniform %22
+ %24 = OpVariable %23 Uniform
+ %25 = OpTypePointer Uniform %16
+ %31 = OpConstant %20 1
+ %36 = OpConstant %16 2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %45 = OpFunctionCall %2 %6
+ %46 = OpFunctionCall %2 %8
+ %47 = OpFunctionCall %2 %10
+ %48 = OpFunctionCall %2 %12
+ %49 = OpFunctionCall %2 %14
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %26 = OpAccessChain %25 %24 %21
+ %27 = OpLoad %16 %26
+ %28 = OpAccessChain %25 %19 %21
+ OpStore %28 %27
+ OpReturn
+ OpFunctionEnd
+ %8 = OpFunction %2 None %3
+ %9 = OpLabel
+ %29 = OpAccessChain %25 %19 %21
+ %30 = OpLoad %16 %29
+ %32 = OpIAdd %16 %30 %31
+ OpStore %29 %32
+ OpReturn
+ OpFunctionEnd
+ %10 = OpFunction %2 None %3
+ %11 = OpLabel
+ %33 = OpAccessChain %25 %19 %21
+ %34 = OpLoad %16 %33
+ %35 = OpISub %16 %34 %31
+ OpStore %33 %35
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %37 = OpAccessChain %25 %19 %21
+ %38 = OpLoad %16 %37
+ %39 = OpIMul %16 %38 %36
+ %40 = OpAccessChain %25 %19 %21
+ OpStore %40 %39
+ OpReturn
+ OpFunctionEnd
+ %14 = OpFunction %2 None %3
+ %15 = OpLabel
+ %41 = OpAccessChain %25 %19 %21
+ %42 = OpLoad %16 %41
+ %43 = OpUDiv %16 %42 %36
+ %44 = OpAccessChain %25 %19 %21
+ OpStore %44 %43
+ OpReturn
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/spec_constant_array_size_autogen.cpp b/test/diff/diff_files/spec_constant_array_size_autogen.cpp
new file mode 100644
index 00000000..1962d27e
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_array_size_autogen.cpp
@@ -0,0 +1,310 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests that identical specialization constants are not matched with constants
+// when used as array size.
+constexpr char kSrc[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %22 "main" %4 %19
+OpSource GLSL 450
+OpName %4 "_ua_position"
+OpName %17 "gl_PerVertex"
+OpMemberName %17 0 "gl_Position"
+OpMemberName %17 1 "gl_PointSize"
+OpMemberName %17 2 "gl_ClipDistance"
+OpMemberName %17 3 "gl_CullDistance"
+OpName %19 ""
+OpName %22 "main"
+OpDecorate %4 Location 0
+OpMemberDecorate %17 1 RelaxedPrecision
+OpMemberDecorate %17 0 BuiltIn Position
+OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%8 = OpTypeVector %5 4
+%15 = OpConstant %5 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %5 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd)";
+constexpr char kDst[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %22 "main" %4 %19
+OpSource GLSL 450
+OpName %4 "_ua_position"
+OpName %17 "gl_PerVertex"
+OpMemberName %17 0 "gl_Position"
+OpMemberName %17 1 "gl_PointSize"
+OpMemberName %17 2 "gl_ClipDistance"
+OpMemberName %17 3 "gl_CullDistance"
+OpName %19 ""
+OpName %22 "main"
+OpDecorate %4 Location 0
+OpDecorate %15 SpecId 4
+OpMemberDecorate %17 1 RelaxedPrecision
+OpMemberDecorate %17 0 BuiltIn Position
+OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%8 = OpTypeVector %5 4
+%15 = OpSpecConstant %5 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %5 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd
+)";
+
+TEST(DiffTest, SpecConstantArraySize) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 27
++; Bound: 36
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %22 "main" %4 %19
+ OpSource GLSL 450
+ OpName %4 "_ua_position"
+ OpName %17 "gl_PerVertex"
+ OpMemberName %17 0 "gl_Position"
+ OpMemberName %17 1 "gl_PointSize"
+ OpMemberName %17 2 "gl_ClipDistance"
+ OpMemberName %17 3 "gl_CullDistance"
+ OpName %19 ""
+ OpName %22 "main"
+ OpDecorate %4 Location 0
++OpDecorate %34 SpecId 4
+ OpMemberDecorate %17 1 RelaxedPrecision
+ OpMemberDecorate %17 0 BuiltIn Position
+ OpMemberDecorate %17 1 BuiltIn PointSize
+ OpMemberDecorate %17 2 BuiltIn ClipDistance
+ OpMemberDecorate %17 3 BuiltIn CullDistance
+ OpDecorate %17 Block
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %5 = OpTypeInt 32 0
+ %8 = OpTypeVector %5 4
+-%15 = OpConstant %5 8
+-%16 = OpTypeArray %1 %15
++%34 = OpSpecConstant %5 8
++%35 = OpTypeArray %1 %34
+-%17 = OpTypeStruct %2 %1 %16 %16
++%17 = OpTypeStruct %2 %1 %35 %35
+ %20 = OpTypeVoid
+ %25 = OpConstant %5 0
+ %3 = OpTypePointer Input %2
+ %13 = OpTypePointer Output %2
+ %18 = OpTypePointer Output %17
+ %21 = OpTypeFunction %20
+ %4 = OpVariable %3 Input
+ %19 = OpVariable %18 Output
+ %22 = OpFunction %20 None %21
+ %23 = OpLabel
+ %24 = OpLoad %2 %4
+ %26 = OpAccessChain %13 %19 %25
+ OpStore %26 %24
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, SpecConstantArraySizeNoDebug) {
+ constexpr char kSrcNoDebug[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %22 "main" %4 %19
+OpSource GLSL 450
+OpDecorate %4 Location 0
+OpMemberDecorate %17 1 RelaxedPrecision
+OpMemberDecorate %17 0 BuiltIn Position
+OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%8 = OpTypeVector %5 4
+%15 = OpConstant %5 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %5 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd
+)";
+ constexpr char kDstNoDebug[] = R"(; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %22 "main" %4 %19
+OpSource GLSL 450
+OpDecorate %4 Location 0
+OpDecorate %15 SpecId 4
+OpMemberDecorate %17 1 RelaxedPrecision
+OpMemberDecorate %17 0 BuiltIn Position
+OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%8 = OpTypeVector %5 4
+%15 = OpSpecConstant %5 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %5 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 27
++; Bound: 36
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %22 "main" %4 %19
+ OpSource GLSL 450
+ OpDecorate %4 Location 0
++OpDecorate %34 SpecId 4
+ OpMemberDecorate %17 1 RelaxedPrecision
+ OpMemberDecorate %17 0 BuiltIn Position
+ OpMemberDecorate %17 1 BuiltIn PointSize
+ OpMemberDecorate %17 2 BuiltIn ClipDistance
+ OpMemberDecorate %17 3 BuiltIn CullDistance
+ OpDecorate %17 Block
+ %1 = OpTypeFloat 32
+ %2 = OpTypeVector %1 4
+ %5 = OpTypeInt 32 0
+ %8 = OpTypeVector %5 4
+-%15 = OpConstant %5 8
+-%16 = OpTypeArray %1 %15
++%34 = OpSpecConstant %5 8
++%35 = OpTypeArray %1 %34
+-%17 = OpTypeStruct %2 %1 %16 %16
++%17 = OpTypeStruct %2 %1 %35 %35
+ %20 = OpTypeVoid
+ %25 = OpConstant %5 0
+ %3 = OpTypePointer Input %2
+ %13 = OpTypePointer Output %2
+ %18 = OpTypePointer Output %17
+ %21 = OpTypeFunction %20
+ %4 = OpVariable %3 Input
+ %19 = OpVariable %18 Output
+ %22 = OpFunction %20 None %21
+ %23 = OpLabel
+ %24 = OpLoad %2 %4
+ %26 = OpAccessChain %13 %19 %25
+ OpStore %26 %24
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/spec_constant_array_size_dst.spvasm b/test/diff/diff_files/spec_constant_array_size_dst.spvasm
new file mode 100644
index 00000000..7cedf083
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_array_size_dst.spvasm
@@ -0,0 +1,47 @@
+; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %22 "main" %4 %19
+OpSource GLSL 450
+OpName %4 "_ua_position"
+OpName %17 "gl_PerVertex"
+OpMemberName %17 0 "gl_Position"
+OpMemberName %17 1 "gl_PointSize"
+OpMemberName %17 2 "gl_ClipDistance"
+OpMemberName %17 3 "gl_CullDistance"
+OpName %19 ""
+OpName %22 "main"
+OpDecorate %4 Location 0
+OpDecorate %15 SpecId 4
+OpMemberDecorate %17 1 RelaxedPrecision
+OpMemberDecorate %17 0 BuiltIn Position
+OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%8 = OpTypeVector %5 4
+%15 = OpSpecConstant %5 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %5 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd
diff --git a/test/diff/diff_files/spec_constant_array_size_src.spvasm b/test/diff/diff_files/spec_constant_array_size_src.spvasm
new file mode 100644
index 00000000..e17e04ac
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_array_size_src.spvasm
@@ -0,0 +1,48 @@
+;; Tests that identical specialization constants are not matched with constants
+;; when used as array size.
+; SPIR-V
+; Version: 1.0
+; Generator: Google ANGLE Shader Compiler; 0
+; Bound: 27
+; Schema: 0
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %22 "main" %4 %19
+OpSource GLSL 450
+OpName %4 "_ua_position"
+OpName %17 "gl_PerVertex"
+OpMemberName %17 0 "gl_Position"
+OpMemberName %17 1 "gl_PointSize"
+OpMemberName %17 2 "gl_ClipDistance"
+OpMemberName %17 3 "gl_CullDistance"
+OpName %19 ""
+OpName %22 "main"
+OpDecorate %4 Location 0
+OpMemberDecorate %17 1 RelaxedPrecision
+OpMemberDecorate %17 0 BuiltIn Position
+OpMemberDecorate %17 1 BuiltIn PointSize
+OpMemberDecorate %17 2 BuiltIn ClipDistance
+OpMemberDecorate %17 3 BuiltIn CullDistance
+OpDecorate %17 Block
+%1 = OpTypeFloat 32
+%2 = OpTypeVector %1 4
+%5 = OpTypeInt 32 0
+%8 = OpTypeVector %5 4
+%15 = OpConstant %5 8
+%16 = OpTypeArray %1 %15
+%17 = OpTypeStruct %2 %1 %16 %16
+%20 = OpTypeVoid
+%25 = OpConstant %5 0
+%3 = OpTypePointer Input %2
+%13 = OpTypePointer Output %2
+%18 = OpTypePointer Output %17
+%21 = OpTypeFunction %20
+%4 = OpVariable %3 Input
+%19 = OpVariable %18 Output
+%22 = OpFunction %20 None %21
+%23 = OpLabel
+%24 = OpLoad %2 %4
+%26 = OpAccessChain %13 %19 %25
+OpStore %26 %24
+OpReturn
+OpFunctionEnd
diff --git a/test/diff/diff_files/spec_constant_composite_autogen.cpp b/test/diff/diff_files/spec_constant_composite_autogen.cpp
new file mode 100644
index 00000000..e4b52cb6
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_composite_autogen.cpp
@@ -0,0 +1,186 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests OpSpecConstantComposite matching.
+constexpr char kSrc[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ OpSource GLSL 450
+ OpName %main "main"
+ OpDecorate %7 SpecId 3
+ OpDecorate %8 SpecId 4
+ OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %7 = OpSpecConstant %uint 1
+ %8 = OpSpecConstant %uint 1
+ %uint_1 = OpConstant %uint 1
+ %v3uint = OpTypeVector %uint 3
+%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd)";
+constexpr char kDst[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ OpSource GLSL 450
+ OpName %main "main"
+ OpDecorate %7 SpecId 3
+ OpDecorate %8 SpecId 4
+ OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %7 = OpSpecConstant %uint 2048
+ %8 = OpSpecConstant %uint 1
+ %uint_1 = OpConstant %uint 1
+ %v3uint = OpTypeVector %uint 3
+%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+TEST(DiffTest, SpecConstantComposite) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 12
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %2 "main"
+ OpExecutionMode %2 LocalSize 1 1 1
+ OpSource GLSL 450
+ OpName %2 "main"
+ OpDecorate %7 SpecId 3
+ OpDecorate %8 SpecId 4
+ OpDecorate %4 BuiltIn WorkgroupSize
+ %6 = OpTypeVoid
+ %3 = OpTypeFunction %6
+ %9 = OpTypeInt 32 0
+-%7 = OpSpecConstant %9 1
++%7 = OpSpecConstant %9 2048
+ %8 = OpSpecConstant %9 1
+ %10 = OpConstant %9 1
+ %11 = OpTypeVector %9 3
+ %4 = OpSpecConstantComposite %11 %7 %8 %10
+ %2 = OpFunction %6 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, SpecConstantCompositeNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ OpSource GLSL 450
+ OpDecorate %7 SpecId 3
+ OpDecorate %8 SpecId 4
+ OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %7 = OpSpecConstant %uint 1
+ %8 = OpSpecConstant %uint 1
+ %uint_1 = OpConstant %uint 1
+ %v3uint = OpTypeVector %uint 3
+%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ OpSource GLSL 450
+ OpDecorate %7 SpecId 3
+ OpDecorate %8 SpecId 4
+ OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %7 = OpSpecConstant %uint 2048
+ %8 = OpSpecConstant %uint 1
+ %uint_1 = OpConstant %uint 1
+ %v3uint = OpTypeVector %uint 3
+%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 12
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %2 "main"
+ OpExecutionMode %2 LocalSize 1 1 1
+ OpSource GLSL 450
+ OpDecorate %7 SpecId 3
+ OpDecorate %8 SpecId 4
+ OpDecorate %4 BuiltIn WorkgroupSize
+ %6 = OpTypeVoid
+ %3 = OpTypeFunction %6
+ %9 = OpTypeInt 32 0
+-%7 = OpSpecConstant %9 1
++%7 = OpSpecConstant %9 2048
+ %8 = OpSpecConstant %9 1
+ %10 = OpConstant %9 1
+ %11 = OpTypeVector %9 3
+ %4 = OpSpecConstantComposite %11 %7 %8 %10
+ %2 = OpFunction %6 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/spec_constant_composite_dst.spvasm b/test/diff/diff_files/spec_constant_composite_dst.spvasm
new file mode 100644
index 00000000..3ab8d4d3
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_composite_dst.spvasm
@@ -0,0 +1,22 @@
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ OpSource GLSL 450
+ OpName %main "main"
+ OpDecorate %7 SpecId 3
+ OpDecorate %8 SpecId 4
+ OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %7 = OpSpecConstant %uint 2048
+ %8 = OpSpecConstant %uint 1
+ %uint_1 = OpConstant %uint 1
+ %v3uint = OpTypeVector %uint 3
+%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
diff --git a/test/diff/diff_files/spec_constant_composite_src.spvasm b/test/diff/diff_files/spec_constant_composite_src.spvasm
new file mode 100644
index 00000000..ee48ef9d
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_composite_src.spvasm
@@ -0,0 +1,23 @@
+;; Tests OpSpecConstantComposite matching.
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ OpSource GLSL 450
+ OpName %main "main"
+ OpDecorate %7 SpecId 3
+ OpDecorate %8 SpecId 4
+ OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %7 = OpSpecConstant %uint 1
+ %8 = OpSpecConstant %uint 1
+ %uint_1 = OpConstant %uint 1
+ %v3uint = OpTypeVector %uint 3
+%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
diff --git a/test/diff/diff_files/spec_constant_op_autogen.cpp b/test/diff/diff_files/spec_constant_op_autogen.cpp
new file mode 100644
index 00000000..e88e15be
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_op_autogen.cpp
@@ -0,0 +1,162 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests OpSpecConstantOp matching.
+constexpr char kSrc[] = R"( OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %1 "main"
+ OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %5 = OpTypeVector %4 3
+ %6 = OpConstant %4 1
+ %7 = OpSpecConstantComposite %5 %6 %6 %6
+ %8 = OpSpecConstantOp %4 CompositeExtract %7 2
+ %9 = OpSpecConstantOp %4 CompositeExtract %7 1
+ %10 = OpSpecConstantOp %4 CompositeExtract %7 0
+ %1 = OpFunction %2 None %3
+ %11 = OpLabel
+ OpReturn
+ OpFunctionEnd)";
+constexpr char kDst[] = R"( OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %1 "main"
+ OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %5 = OpTypeVector %4 3
+ %6 = OpConstant %4 1
+ %7 = OpSpecConstantComposite %5 %6 %6 %6
+ %8 = OpSpecConstantOp %4 CompositeExtract %7 2
+ %9 = OpSpecConstantOp %4 CompositeExtract %7 3
+ %10 = OpSpecConstantOp %4 IMul %8 %8
+ %1 = OpFunction %2 None %3
+ %11 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+TEST(DiffTest, SpecConstantOp) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 12
++; Bound: 14
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %1 "main"
+ OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %5 = OpTypeVector %4 3
+ %6 = OpConstant %4 1
+ %7 = OpSpecConstantComposite %5 %6 %6 %6
+ %8 = OpSpecConstantOp %4 CompositeExtract %7 2
+-%9 = OpSpecConstantOp %4 CompositeExtract %7 1
+-%10 = OpSpecConstantOp %4 CompositeExtract %7 0
++%12 = OpSpecConstantOp %4 CompositeExtract %7 3
++%13 = OpSpecConstantOp %4 IMul %8 %8
+ %1 = OpFunction %2 None %3
+ %11 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, SpecConstantOpNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %1 "main"
+ OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %5 = OpTypeVector %4 3
+ %6 = OpConstant %4 1
+ %7 = OpSpecConstantComposite %5 %6 %6 %6
+ %8 = OpSpecConstantOp %4 CompositeExtract %7 2
+ %9 = OpSpecConstantOp %4 CompositeExtract %7 1
+ %10 = OpSpecConstantOp %4 CompositeExtract %7 0
+ %1 = OpFunction %2 None %3
+ %11 = OpLabel
+ OpReturn
+ OpFunctionEnd)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %1 "main"
+ OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %5 = OpTypeVector %4 3
+ %6 = OpConstant %4 1
+ %7 = OpSpecConstantComposite %5 %6 %6 %6
+ %8 = OpSpecConstantOp %4 CompositeExtract %7 2
+ %9 = OpSpecConstantOp %4 CompositeExtract %7 3
+ %10 = OpSpecConstantOp %4 IMul %8 %8
+ %1 = OpFunction %2 None %3
+ %11 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 12
++; Bound: 14
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %1 "main"
+ OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %5 = OpTypeVector %4 3
+ %6 = OpConstant %4 1
+ %7 = OpSpecConstantComposite %5 %6 %6 %6
+ %8 = OpSpecConstantOp %4 CompositeExtract %7 2
+-%9 = OpSpecConstantOp %4 CompositeExtract %7 1
+-%10 = OpSpecConstantOp %4 CompositeExtract %7 0
++%12 = OpSpecConstantOp %4 CompositeExtract %7 3
++%13 = OpSpecConstantOp %4 IMul %8 %8
+ %1 = OpFunction %2 None %3
+ %11 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/spec_constant_op_dst.spvasm b/test/diff/diff_files/spec_constant_op_dst.spvasm
new file mode 100644
index 00000000..b5b10e52
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_op_dst.spvasm
@@ -0,0 +1,17 @@
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %1 "main"
+ OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %5 = OpTypeVector %4 3
+ %6 = OpConstant %4 1
+ %7 = OpSpecConstantComposite %5 %6 %6 %6
+ %8 = OpSpecConstantOp %4 CompositeExtract %7 2
+ %9 = OpSpecConstantOp %4 CompositeExtract %7 3
+ %10 = OpSpecConstantOp %4 IMul %8 %8
+ %1 = OpFunction %2 None %3
+ %11 = OpLabel
+ OpReturn
+ OpFunctionEnd
diff --git a/test/diff/diff_files/spec_constant_op_src.spvasm b/test/diff/diff_files/spec_constant_op_src.spvasm
new file mode 100644
index 00000000..09306f8b
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_op_src.spvasm
@@ -0,0 +1,18 @@
+;; Tests OpSpecConstantOp matching.
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %1 "main"
+ OpExecutionMode %1 LocalSize 1 1 1
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %5 = OpTypeVector %4 3
+ %6 = OpConstant %4 1
+ %7 = OpSpecConstantComposite %5 %6 %6 %6
+ %8 = OpSpecConstantOp %4 CompositeExtract %7 2
+ %9 = OpSpecConstantOp %4 CompositeExtract %7 1
+ %10 = OpSpecConstantOp %4 CompositeExtract %7 0
+ %1 = OpFunction %2 None %3
+ %11 = OpLabel
+ OpReturn
+ OpFunctionEnd \ No newline at end of file
diff --git a/test/diff/diff_files/spec_constant_specid_autogen.cpp b/test/diff/diff_files/spec_constant_specid_autogen.cpp
new file mode 100644
index 00000000..240dc59c
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_specid_autogen.cpp
@@ -0,0 +1,141 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests OpSpecConstantComposite matching.
+constexpr char kSrc[] = R"( OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ OpDecorate %sc SpecId 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %v3uint = OpTypeVector %uint 3
+ %sc = OpSpecConstant %uint 10
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd)";
+constexpr char kDst[] = R"( OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %v3uint = OpTypeVector %uint 3
+ %ss = OpSpecConstant %uint 10
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+TEST(DiffTest, SpecConstantSpecid) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 8
++; Bound: 9
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %1 "main"
+ OpExecutionMode %1 LocalSize 1 1 1
+-OpDecorate %2 SpecId 0
+ %4 = OpTypeVoid
+ %3 = OpTypeFunction %4
+ %6 = OpTypeInt 32 0
+ %7 = OpTypeVector %6 3
+-%2 = OpSpecConstant %6 10
++%8 = OpSpecConstant %6 10
+ %1 = OpFunction %4 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, SpecConstantSpecidNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ OpDecorate %sc SpecId 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %v3uint = OpTypeVector %uint 3
+ %sc = OpSpecConstant %uint 10
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %v3uint = OpTypeVector %uint 3
+ %ss = OpSpecConstant %uint 10
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 8
++; Bound: 9
+ ; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %1 "main"
+ OpExecutionMode %1 LocalSize 1 1 1
+-OpDecorate %2 SpecId 0
+ %4 = OpTypeVoid
+ %3 = OpTypeFunction %4
+ %6 = OpTypeInt 32 0
+ %7 = OpTypeVector %6 3
+-%2 = OpSpecConstant %6 10
++%8 = OpSpecConstant %6 10
+ %1 = OpFunction %4 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/spec_constant_specid_dst.spvasm b/test/diff/diff_files/spec_constant_specid_dst.spvasm
new file mode 100644
index 00000000..0e840edc
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_specid_dst.spvasm
@@ -0,0 +1,13 @@
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %v3uint = OpTypeVector %uint 3
+ %ss = OpSpecConstant %uint 10
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
diff --git a/test/diff/diff_files/spec_constant_specid_src.spvasm b/test/diff/diff_files/spec_constant_specid_src.spvasm
new file mode 100644
index 00000000..7fdfeba2
--- /dev/null
+++ b/test/diff/diff_files/spec_constant_specid_src.spvasm
@@ -0,0 +1,15 @@
+;; Tests OpSpecConstantComposite matching.
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ OpDecorate %sc SpecId 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %v3uint = OpTypeVector %uint 3
+ %sc = OpSpecConstant %uint 10
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
diff --git a/test/diff/diff_files/unrelated_shaders_autogen.cpp b/test/diff/diff_files/unrelated_shaders_autogen.cpp
new file mode 100644
index 00000000..e1a58ccc
--- /dev/null
+++ b/test/diff/diff_files/unrelated_shaders_autogen.cpp
@@ -0,0 +1,230 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests diff of unrelated shaders (with different execution models).
+constexpr char kSrc[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %4 "main" %8 %10
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "v"
+ OpName %10 "a"
+ OpDecorate %8 Location 0
+ OpDecorate %10 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Output %6
+ %8 = OpVariable %7 Output
+ %9 = OpTypePointer Input %6
+ %10 = OpVariable %9 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpLoad %6 %10
+ OpStore %8 %11
+ OpReturn
+ OpFunctionEnd
+)";
+constexpr char kDst[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %9 "color"
+ OpName %11 "v"
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpCompositeConstruct %7 %12 %12 %12 %12
+ OpStore %9 %13
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+TEST(DiffTest, UnrelatedShaders) {
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 12
++; Bound: 16
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+-OpEntryPoint Vertex %4 "main" %8 %10
++OpEntryPoint Fragment %4 "main" %14 %8
++OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
++OpName %14 "color"
+ OpName %8 "v"
+-OpName %10 "a"
++OpDecorate %14 RelaxedPrecision
++OpDecorate %14 Location 0
++OpDecorate %8 RelaxedPrecision
+ OpDecorate %8 Location 0
+-OpDecorate %10 Location 0
++OpDecorate %11 RelaxedPrecision
++OpDecorate %15 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+-%7 = OpTypePointer Output %6
++%12 = OpTypeVector %6 4
++%13 = OpTypePointer Output %12
++%14 = OpVariable %13 Output
+-%8 = OpVariable %7 Output
++%8 = OpVariable %9 Input
+ %9 = OpTypePointer Input %6
+-%10 = OpVariable %9 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+-%11 = OpLoad %6 %10
++%11 = OpLoad %6 %8
+-OpStore %8 %11
++%15 = OpCompositeConstruct %12 %11 %11 %11 %11
++OpStore %14 %15
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, UnrelatedShadersNoDebug) {
+ constexpr char kSrcNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %4 "main" %8 %10
+ OpSource ESSL 310
+ OpDecorate %8 Location 0
+ OpDecorate %10 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Output %6
+ %8 = OpVariable %7 Output
+ %9 = OpTypePointer Input %6
+ %10 = OpVariable %9 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpLoad %6 %10
+ OpStore %8 %11
+ OpReturn
+ OpFunctionEnd
+
+)";
+ constexpr char kDstNoDebug[] = R"( OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpCompositeConstruct %7 %12 %12 %12 %12
+ OpStore %9 %13
+ OpReturn
+ OpFunctionEnd
+
+)";
+ constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 12
++; Bound: 15
+ ; Schema: 0
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+-OpEntryPoint Vertex %4 "main" %8 %10
++OpEntryPoint Fragment %4 "main" %8 %10
++OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
++OpDecorate %8 RelaxedPrecision
+ OpDecorate %8 Location 0
++OpDecorate %10 RelaxedPrecision
+ OpDecorate %10 Location 0
++OpDecorate %11 RelaxedPrecision
++OpDecorate %14 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+-%7 = OpTypePointer Output %6
++%12 = OpTypeVector %6 4
++%13 = OpTypePointer Output %12
+-%8 = OpVariable %7 Output
++%8 = OpVariable %13 Output
+ %9 = OpTypePointer Input %6
+ %10 = OpVariable %9 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpLoad %6 %10
++%14 = OpCompositeConstruct %12 %11 %11 %11 %11
+-OpStore %8 %11
++OpStore %8 %14
+ OpReturn
+ OpFunctionEnd
+)";
+ Options options;
+ DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_files/unrelated_shaders_dst.spvasm b/test/diff/diff_files/unrelated_shaders_dst.spvasm
new file mode 100644
index 00000000..719715ba
--- /dev/null
+++ b/test/diff/diff_files/unrelated_shaders_dst.spvasm
@@ -0,0 +1,31 @@
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %9 "color"
+ OpName %11 "v"
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpCompositeConstruct %7 %12 %12 %12 %12
+ OpStore %9 %13
+ OpReturn
+ OpFunctionEnd
+
diff --git a/test/diff/diff_files/unrelated_shaders_src.spvasm b/test/diff/diff_files/unrelated_shaders_src.spvasm
new file mode 100644
index 00000000..e77b2d20
--- /dev/null
+++ b/test/diff/diff_files/unrelated_shaders_src.spvasm
@@ -0,0 +1,25 @@
+;; Tests diff of unrelated shaders (with different execution models).
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %4 "main" %8 %10
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "v"
+ OpName %10 "a"
+ OpDecorate %8 Location 0
+ OpDecorate %10 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Output %6
+ %8 = OpVariable %7 Output
+ %9 = OpTypePointer Input %6
+ %10 = OpVariable %9 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpLoad %6 %10
+ OpStore %8 %11
+ OpReturn
+ OpFunctionEnd
+
diff --git a/test/diff/diff_test.cpp b/test/diff/diff_test.cpp
new file mode 100644
index 00000000..5b11d0eb
--- /dev/null
+++ b/test/diff/diff_test.cpp
@@ -0,0 +1,228 @@
+// Copyright (c) 2022 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/diff/diff.h"
+
+#include "diff_test_utils.h"
+
+#include "source/opt/build_module.h"
+#include "source/opt/ir_context.h"
+#include "source/spirv_constant.h"
+#include "spirv-tools/libspirv.hpp"
+#include "tools/io.h"
+#include "tools/util/cli_consumer.h"
+
+#include <fstream>
+#include <string>
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+
+std::unique_ptr<spvtools::opt::IRContext> Assemble(const std::string& spirv) {
+ spvtools::SpirvTools t(kDefaultEnvironment);
+ t.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
+ std::vector<uint32_t> binary;
+ if (!t.Assemble(spirv, &binary,
+ spvtools::SpirvTools::kDefaultAssembleOption |
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS))
+ return nullptr;
+ return spvtools::BuildModule(kDefaultEnvironment,
+ spvtools::utils::CLIMessageConsumer,
+ binary.data(), binary.size());
+}
+
+TEST(DiffIndentTest, Diff) {
+ const std::string src = R"(OpCapability Shader
+ %ext_inst = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+
+ %main = OpFunction %void None %func
+ %main_entry = OpLabel
+ OpReturn
+ OpFunctionEnd;)";
+
+ const std::string dst = R"(OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+
+ %main = OpFunction %void None %func
+ %main_entry = OpLabel
+ OpReturn
+ OpFunctionEnd;)";
+
+ const std::string diff = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 6
+ ; Schema: 0
+ OpCapability Shader
+- %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ %3 = OpTypeVoid
+ %4 = OpTypeFunction %3
+ %2 = OpFunction %3 None %4
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ Options options;
+ options.indent = true;
+ DoStringDiffTest(src, dst, diff, options);
+}
+
+TEST(DiffNoHeaderTest, Diff) {
+ const std::string src = R"(OpCapability Shader
+ %ext_inst = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+
+ %main = OpFunction %void None %func
+ %main_entry = OpLabel
+ OpReturn
+ OpFunctionEnd;)";
+
+ const std::string dst = R"(OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+
+ %main = OpFunction %void None %func
+ %main_entry = OpLabel
+ OpReturn
+ OpFunctionEnd;)";
+
+ const std::string diff = R"( OpCapability Shader
+-%1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ %3 = OpTypeVoid
+ %4 = OpTypeFunction %3
+ %2 = OpFunction %3 None %4
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ Options options;
+ options.no_header = true;
+ DoStringDiffTest(src, dst, diff, options);
+}
+
+TEST(DiffHeaderTest, Diff) {
+ const std::string src_spirv = R"(OpCapability Shader
+ %ext_inst = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+
+ %main = OpFunction %void None %func
+ %main_entry = OpLabel
+ OpReturn
+ OpFunctionEnd;)";
+
+ const std::string dst_spirv = R"(OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+
+ %main = OpFunction %void None %func
+ %main_entry = OpLabel
+ OpReturn
+ OpFunctionEnd;)";
+
+ const std::string diff = R"( ; SPIR-V
+-; Version: 1.3
++; Version: 1.2
+-; Generator: Khronos SPIR-V Tools Assembler; 3
++; Generator: Khronos Glslang Reference Front End; 10
+ ; Bound: 6
+ ; Schema: 0
+ OpCapability Shader
+-%1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ %3 = OpTypeVoid
+ %4 = OpTypeFunction %3
+ %2 = OpFunction %3 None %4
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ // Load the src and dst modules
+ std::unique_ptr<spvtools::opt::IRContext> src = Assemble(src_spirv);
+ ASSERT_TRUE(src);
+
+ std::unique_ptr<spvtools::opt::IRContext> dst = Assemble(dst_spirv);
+ ASSERT_TRUE(dst);
+
+ // Differentiate them in the header.
+ const spvtools::opt::ModuleHeader src_header = {
+ SpvMagicNumber,
+ SPV_SPIRV_VERSION_WORD(1, 3),
+ SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 3),
+ src->module()->IdBound(),
+ src->module()->schema(),
+ };
+ const spvtools::opt::ModuleHeader dst_header = {
+ SpvMagicNumber,
+ SPV_SPIRV_VERSION_WORD(1, 2),
+ SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_GLSLANG, 10),
+ dst->module()->IdBound(),
+ dst->module()->schema(),
+ };
+
+ src->module()->SetHeader(src_header);
+ dst->module()->SetHeader(dst_header);
+
+ // Take the diff
+ Options options;
+ std::ostringstream diff_result;
+ spv_result_t result =
+ spvtools::diff::Diff(src.get(), dst.get(), diff_result, options);
+ ASSERT_EQ(result, SPV_SUCCESS);
+
+ // Expect they match
+ EXPECT_EQ(diff_result.str(), diff);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_test_utils.cpp b/test/diff/diff_test_utils.cpp
new file mode 100644
index 00000000..14bb8215
--- /dev/null
+++ b/test/diff/diff_test_utils.cpp
@@ -0,0 +1,58 @@
+// Copyright (c) 2022 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 "diff_test_utils.h"
+
+#include "source/opt/build_module.h"
+#include "source/opt/ir_context.h"
+
+#include "spirv-tools/libspirv.hpp"
+#include "tools/io.h"
+#include "tools/util/cli_consumer.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+
+static constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+
+void DoStringDiffTest(const std::string& src_spirv,
+ const std::string& dst_spirv,
+ const std::string& expected_diff, Options options) {
+ // Load the src and dst modules
+ std::unique_ptr<spvtools::opt::IRContext> src = spvtools::BuildModule(
+ kDefaultEnvironment, spvtools::utils::CLIMessageConsumer, src_spirv,
+ spvtools::SpirvTools::kDefaultAssembleOption |
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ASSERT_TRUE(src);
+
+ std::unique_ptr<spvtools::opt::IRContext> dst = spvtools::BuildModule(
+ kDefaultEnvironment, spvtools::utils::CLIMessageConsumer, dst_spirv,
+ spvtools::SpirvTools::kDefaultAssembleOption |
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ASSERT_TRUE(dst);
+
+ // Take the diff
+ std::ostringstream diff_result;
+ spv_result_t result =
+ spvtools::diff::Diff(src.get(), dst.get(), diff_result, options);
+ ASSERT_EQ(result, SPV_SUCCESS);
+
+ // Expect they match
+ EXPECT_EQ(diff_result.str(), expected_diff);
+}
+
+} // namespace diff
+} // namespace spvtools
diff --git a/test/diff/diff_test_utils.h b/test/diff/diff_test_utils.h
new file mode 100644
index 00000000..938236e6
--- /dev/null
+++ b/test/diff/diff_test_utils.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2022 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 TEST_DIFF_DIFF_TEST_UTILS_H_
+#define TEST_DIFF_DIFF_TEST_UTILS_H_
+
+#include "source/diff/diff.h"
+
+namespace spvtools {
+namespace diff {
+
+void DoStringDiffTest(const std::string& src_spirv,
+ const std::string& dst_spirv,
+ const std::string& expected_diff, Options options);
+
+} // namespace diff
+} // namespace spvtools
+
+#endif // TEST_DIFF_DIFF_TEST_UTILS_H_
diff --git a/test/diff/lcs_test.cpp b/test/diff/lcs_test.cpp
new file mode 100644
index 00000000..3e097b3e
--- /dev/null
+++ b/test/diff/lcs_test.cpp
@@ -0,0 +1,329 @@
+// Copyright (c) 2022 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/diff/lcs.h"
+
+#include <string>
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+using Sequence = std::vector<int>;
+using LCS = LongestCommonSubsequence<Sequence>;
+
+void VerifyMatch(const Sequence& src, const Sequence& dst,
+ size_t expected_match_count) {
+ DiffMatch src_match, dst_match;
+
+ LCS lcs(src, dst);
+ size_t match_count =
+ lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match);
+
+ EXPECT_EQ(match_count, expected_match_count);
+
+ size_t src_cur = 0;
+ size_t dst_cur = 0;
+ size_t matches_seen = 0;
+
+ while (src_cur < src.size() && dst_cur < dst.size()) {
+ if (src_match[src_cur] && dst_match[dst_cur]) {
+ EXPECT_EQ(src[src_cur], dst[dst_cur])
+ << "Src: " << src_cur << " Dst: " << dst_cur;
+ ++src_cur;
+ ++dst_cur;
+ ++matches_seen;
+ continue;
+ }
+ if (!src_match[src_cur]) {
+ ++src_cur;
+ }
+ if (!dst_match[dst_cur]) {
+ ++dst_cur;
+ }
+ }
+
+ EXPECT_EQ(matches_seen, expected_match_count);
+}
+
+TEST(LCSTest, EmptySequences) {
+ Sequence src, dst;
+
+ DiffMatch src_match, dst_match;
+
+ LCS lcs(src, dst);
+ size_t match_count =
+ lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match);
+
+ EXPECT_EQ(match_count, 0u);
+ EXPECT_TRUE(src_match.empty());
+ EXPECT_TRUE(dst_match.empty());
+}
+
+TEST(LCSTest, EmptySrc) {
+ Sequence src, dst = {1, 2, 3};
+
+ DiffMatch src_match, dst_match;
+
+ LCS lcs(src, dst);
+ size_t match_count =
+ lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match);
+
+ EXPECT_EQ(match_count, 0u);
+ EXPECT_TRUE(src_match.empty());
+ EXPECT_EQ(dst_match, DiffMatch(3, false));
+}
+
+TEST(LCSTest, EmptyDst) {
+ Sequence src = {1, 2, 3}, dst;
+
+ DiffMatch src_match, dst_match;
+
+ LCS lcs(src, dst);
+ size_t match_count =
+ lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match);
+
+ EXPECT_EQ(match_count, 0u);
+ EXPECT_EQ(src_match, DiffMatch(3, false));
+ EXPECT_TRUE(dst_match.empty());
+}
+
+TEST(LCSTest, Identical) {
+ Sequence src = {1, 2, 3, 4, 5, 6}, dst = {1, 2, 3, 4, 5, 6};
+
+ DiffMatch src_match, dst_match;
+
+ LCS lcs(src, dst);
+ size_t match_count =
+ lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match);
+
+ EXPECT_EQ(match_count, 6u);
+ EXPECT_EQ(src_match, DiffMatch(6, true));
+ EXPECT_EQ(dst_match, DiffMatch(6, true));
+}
+
+TEST(LCSTest, SrcPrefix) {
+ Sequence src = {1, 2, 3, 4}, dst = {1, 2, 3, 4, 5, 6};
+
+ DiffMatch src_match, dst_match;
+
+ LCS lcs(src, dst);
+ size_t match_count =
+ lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match);
+
+ const DiffMatch src_expect = {true, true, true, true};
+ const DiffMatch dst_expect = {true, true, true, true, false, false};
+
+ EXPECT_EQ(match_count, 4u);
+ EXPECT_EQ(src_match, src_expect);
+ EXPECT_EQ(dst_match, dst_expect);
+}
+
+TEST(LCSTest, DstPrefix) {
+ Sequence src = {1, 2, 3, 4, 5, 6}, dst = {1, 2, 3, 4, 5};
+
+ DiffMatch src_match, dst_match;
+
+ LCS lcs(src, dst);
+ size_t match_count =
+ lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match);
+
+ const DiffMatch src_expect = {true, true, true, true, true, false};
+ const DiffMatch dst_expect = {true, true, true, true, true};
+
+ EXPECT_EQ(match_count, 5u);
+ EXPECT_EQ(src_match, src_expect);
+ EXPECT_EQ(dst_match, dst_expect);
+}
+
+TEST(LCSTest, SrcSuffix) {
+ Sequence src = {3, 4, 5, 6}, dst = {1, 2, 3, 4, 5, 6};
+
+ DiffMatch src_match, dst_match;
+
+ LCS lcs(src, dst);
+ size_t match_count =
+ lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match);
+
+ const DiffMatch src_expect = {true, true, true, true};
+ const DiffMatch dst_expect = {false, false, true, true, true, true};
+
+ EXPECT_EQ(match_count, 4u);
+ EXPECT_EQ(src_match, src_expect);
+ EXPECT_EQ(dst_match, dst_expect);
+}
+
+TEST(LCSTest, DstSuffix) {
+ Sequence src = {1, 2, 3, 4, 5, 6}, dst = {5, 6};
+
+ DiffMatch src_match, dst_match;
+
+ LCS lcs(src, dst);
+ size_t match_count =
+ lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match);
+
+ const DiffMatch src_expect = {false, false, false, false, true, true};
+ const DiffMatch dst_expect = {true, true};
+
+ EXPECT_EQ(match_count, 2u);
+ EXPECT_EQ(src_match, src_expect);
+ EXPECT_EQ(dst_match, dst_expect);
+}
+
+TEST(LCSTest, None) {
+ Sequence src = {1, 3, 5, 7, 9}, dst = {2, 4, 6, 8, 10, 12};
+
+ DiffMatch src_match, dst_match;
+
+ LCS lcs(src, dst);
+ size_t match_count =
+ lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match);
+
+ EXPECT_EQ(match_count, 0u);
+ EXPECT_EQ(src_match, DiffMatch(5, false));
+ EXPECT_EQ(dst_match, DiffMatch(6, false));
+}
+
+TEST(LCSTest, NonContiguous) {
+ Sequence src = {1, 2, 3, 4, 5, 6, 10}, dst = {2, 4, 5, 8, 9, 10, 12};
+
+ DiffMatch src_match, dst_match;
+
+ LCS lcs(src, dst);
+ size_t match_count =
+ lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match);
+
+ const DiffMatch src_expect = {false, true, false, true, true, false, true};
+ const DiffMatch dst_expect = {true, true, true, false, false, true, false};
+
+ EXPECT_EQ(match_count, 4u);
+ EXPECT_EQ(src_match, src_expect);
+ EXPECT_EQ(dst_match, dst_expect);
+}
+
+TEST(LCSTest, WithDuplicates) {
+ Sequence src = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4},
+ dst = {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4};
+ VerifyMatch(src, dst, 6);
+}
+
+TEST(LCSTest, Large) {
+ const std::string src_str =
+ "GUJwrJSlkKJXxCVIAxlVgnUyOrdyRyFtlZwWMmFhYGfkFTNnhiBmClgHyrcXMVwfrRxNUfQk"
+ "qaoGvCbPZHAzXsaZpXHPfJxOMCUtRDmIQpfiXKbHQbhTfPqhxBDWvmTQAqwsWTLajZYtMUnf"
+ "hNNCfkuAXkZsaebwEbIZOxTDZsqSMUfCMoGeKJGVSNFgLTiBMbdvchHGfFRkHKcYCDjBfIcj"
+ "todPnvDjzQYWBcvIfVvyBzHikrwpDORaGEZLhmyztIFCLJOqeLhOzERYmVqzlsoUzruTXTXq"
+ "DLTxQRakOCMRrgRzCDTXfwwfDcKMBVnxRZemjcwcEsOVxwtwdBCWJycsDcZKlvrCvZaenKlv"
+ "vyByDQeLdxAyBnPkIMQlMQwqjUfRLybeoaOanlbFkpTPPZdHelQrIvucTHzMpWWQTbuANwvN"
+ "OVhCGGoIcGNDpfIsaBexlMMdHsxMGerTngmjpdPeQQJHfvKZkdYqAzrtDohqtDsaFMxQViVQ"
+ "YszDVgyoSHZdOXAvXkJidojLvGZOzhRajVPhWDwKuGqdaympELxHsrXAJYufdCPwJdGJfWqq"
+ "yvTWpcrFHOIuCEmNLnSCDsxQGRVDwyCykBJazhApfCnrOadnafvqfVuFqEXMSrYbHTfTnzbz"
+ "MhISyOtMUITaurCXvanCbuOXBhHyCjOhVbxnMvhlPmZBMQgEHCghtAJVMXGPNRtszVZlPxVl"
+ "QIPTBnPUPejlyZGPqeICyNngdQGkvKbIoWlTLBtVhMdBeUMozNlKQTIPYBeImVcMdLafuxUf"
+ "TIXysmcTrUTcspOSKBxhdhLwiRnREGFWJTfUKsgGOAQeYojXdrqsGjMJfiKalyoiqrgLnlij"
+ "CtOapoxDGVOOBalNYGzCtBlxbvaAzxipGnJpOEbmXcpeoIsAdxspKBzBDgoPVxnuRBUwmTSr"
+ "CRpWhxikgUYQVCwalLUIeBPRyhhsECGCXJmGDZSCIUaBwROkigzdeVPOXhgCGBEprWtNdYfL"
+ "tOUYJHQXxiIJgSGmWntezJFpNQoTPbRRYAGhtYvAechvBcYWocLkYFxsDAuszvQNLXdhmAHw"
+ "DErcjbtCdQllnKcDADVNWVezljjLrAuyGHetINMgAvJZwOEYakihYVUbZGCsHEufluLNyNHy"
+ "gqtSTSFFjBHiIqQejTPWybLdpWNwZrWvIWnlzUcGNQPEYHVPCbteWknjAnWrdTBeCbHUDBoK"
+ "aHvDStmpNRGIjvlumiZTbdZNAzUeSFnFChCsSExwXeEfDJfjyOoSBofHzJqJErvHLNyUJTjX"
+ "qmtgKPpMKohUPBMhtCteQFcNEpWrUVGbibMOpvBwdiWYXNissArpSasVJFgDzrqTyGkerTMX"
+ "gcrzFUGFZRhNdekaJeKYPogsofJaRsUQmIRyYdkrxKeMgLPpwOfSKJOqzXDoeHljTzhOwEVy"
+ "krOEnACFrWhufajsMitjOWdLOHHchQDddGPzxknEgdwmZepKDvRZGCuPqzeQkjOPqUBKpKLJ"
+ "eKieSsRXkaqxSPGajfvPKmwFWdLByEcLgvrmteazgFjmMGrLYqRRxzUOfOCokenqHVYstBHf"
+ "AwsWsqPTvqsRJUfGGTaYiylZMGbQqTzINhFHvdlRQvvYKBcuAHdBeKlHSxVrSsEKbcAvnIcf"
+ "xzdVDdwQPHMCHeZZRpGHWvKzgTGzSTbYTeOPyKvvYWmQToTpsjAtKUJUjcEHWhmdBLDTBMHJ"
+ "ivBXcLGtCsumNNVFyGbVviGmqHTdyBlkneibXBesKJGOUzOtIwXCPJggqBekSzNQYkALlItk"
+ "cbEhbdXAIKVHYpInLwxXalKZrkrpxtfuagqMGmRJnJbFQaEoYMoqPsxZpocddPXXPyvxVkaF"
+ "qdKISejWDhBImnEEOPDcyWTubbfVfwUztciaFJcsPLhgYVfhqlOfoNjKbmTFptFttYuyBrUI"
+ "zzmZypOqrjQHTGFwlHStpIwxPtMvtsEDpsmWIgwzYgwmdpbMOnfElZMYpVIcvzSWejeJcdUB"
+ "QUoBRUmGQVVWvEDseuozrDjgdXFScPwwsgaUPwSzScfBNrkpmEFDSZLKfNjMqvOmUtocUkbo"
+ "VGFEKgGLbNruwLgXHTloWDrnqymPVAtzjWPutonIsMDPeeCmTjYWAFXcyTAlBeiJTIRkZxiM"
+ "kLjMnAflSNJzmZkatXkYiPEMYSmzHbLKEizHbEjQOxBDzpRHiFjhedqiyMiUMvThjaRFmwll"
+ "aMGgwKBIKepwyoEdnuhtzJzboiNEAFKiqiWxxmkRFRoTiFWXLPAWLuzSCrajgkQhDxAQDqyM"
+ "VwZlhZicQLEDYYisEalesDWZAYzcvENuHUwRutIsGgsdoYwOZiURhcgdbTGWBNqhrFjvTQCj"
+ "VlTPNlRdRLaaqzUBBwbdtyXFkCBUYYMbmRrkFxfxbCqkgZNGyHPKLkOPnezfVTRmRQgCgHbx"
+ "wcZlInVOwmFePnSIbThMJosimzkhfuiqYEpwHQiemqsSDNNdbNhBLzbsPZBJZujSHJGtYKGb"
+ "HaAYGJZxBumsKUrATwPuqXFLfwNyImLQbchBKiJAYRZhkcrKCHXBEGYyBhBGvSqvabcRUrfq"
+ "AbPiMzjHAehGYjDEmxAnYLyoSFdeWVrfJUCuYZPluhXEBuyUpKaRXDKXeiCvGidpvATwMbcz"
+ "DZpzxrhTZYyrFORFQWTbPLCBjMKMhlRMFEiarDgGPttjmkrQVlujztMSkxXffXFNqLWOLThI"
+ "KBoyMHoFTEPCdUAZjLTifAdjjUehyDLEGKlRTFoLpjalziRSUjZfRYbNzhiHgTHowMMkKTwE"
+ "ZgnqiirMtnNpaBJqhcIVrWXPpcPWZfRpsPstHleFJDZYAsxYhOREVbFtebXTZRAIjGgWeoiN"
+ "qPLCCAVadqmUrjOcqIbdCTpcDRWuDVbHrZOQRPhqbyvOWwxAWJphjLiDgoAybcjzgfVktPlj"
+ "kNBCjelpuQfnYsiTgPpCNKYtOrxGaLEEtAuLdGdDsONHNhSn";
+ const std::string dst_str =
+ "KzitfifORCbGhfNEbnbObUdFLLaAsLOpMkOeKupjCoatzqfHBkNJfSgqSMYouswfNMnoQngK"
+ "jWwyPKmEnoZWyPBUdQRmKUNudUclueKXKQefUdXWUyyqtumzsFKznrLVLwfvPZpLChNYrrHK"
+ "AtpfOuVHiUKyeRCrktJAhkyFKmPWrASEMvBLNOzuGlvinZjvZUUXazNEkyMPiOLdqXvCIroC"
+ "MeWsvjHShlLhDwLZrVlpYBnDJmILcsNFDSoaLWOKNNkNGBgNBvVjPCJXAuKfsrKZhYcdEpxK"
+ "UihiRkYvMiLyOUvaqBMklLDwEhvQBfCXHSRoqsLsSCzLZQhIYMhBapvHaPbDoRrHoJXZsNXc"
+ "rxZYCrOMIzYcVPwDCFiHBFnPNTTeAeKEMGeVUeCaAeuWZmngyPWlQBcgWumSUIfbhjVYdnpV"
+ "hRSJXrIoFZubBXfNOMhilAkVPixrhILZKgDoFTvytPFPfBLMnbhSOBmLWCbJsLQxrCrMAlOw"
+ "RmfSQyGhrjhzYVqFSBHeoQBagFwyxIjcHFZngntpVHbSwqhwHeMnWSsISPljTxSNXfCxLebW"
+ "GhMdlphtJbdvhEcjNpwPCFqhdquxCyOxkjsDUPNgjpDcpIMhMwMclNhfESTrroJaoyeGQclV"
+ "gonnhuQRmXcBwcsWeLqjNngZOlyMyfeQBwnwMVJEvGqknDyzSApniRTPgJpFoDkJJhXQFuFB"
+ "VqhuEPMRGCeTDOSEFmXeIHOnDxaJacvnmORwVpmrRhGjDpUCkuODNPdZMdupYExDEDnDLdNF"
+ "iObKBaVWpGVMKdgNLgsNxcpypBPPKKoaajeSGPZQJWSOKrkLjiFexYVmUGxJnbTNsCXXLfZp"
+ "jfxQAEVYvqKehBzMsVHVGWmTshWFAoCNDkNppzzjHBZWckrzSTANICioCJSpLwPwQvtXVxst"
+ "nTRBAboPFREEUFazibpFesCsjzUOnECwoPCOFiwGORlIZVLpUkJyhYXCENmzTBLVigOFuCWO"
+ "IiXBYmiMtsxnUdoqSTTGyEFFrQsNAjcDdOKDtHwlANWoUVwiJCMCQFILdGqzEePuSXFbOEOz"
+ "dLlEnTJbKRSTfAFToOZNtDXTfFgvQiefAKbSUWUXFcpCjRYCBNXCCcLMjjuUDXErpiNsRuIx"
+ "mgHsrObTEXcnmjdqxTGhTjTeYizNnkrJRhNQIqDXmZMwArBccnixpcuiGOOexjgkpcEyGAnz"
+ "UbgiBfflTUyJfZeFFLrZVueFkSRosebnnwAnakIrywTGByhQKWvmNQJsWQezqLhHQzXnEpeD"
+ "rFRTSQSpVxPzSeEzfWYzfpcenxsUyzOMLxhNEhfcuprDtqubsXehuqKqZlLQeSclvoGjuKJK"
+ "XoWrazsgjXXnkWHdqFESZdMGDYldyYdbpSZcgBPgEKLWZHfBirNPLUadmajYkiEzmGuWGELB"
+ "WLiSrMdaGSbptKmgYVqMGcQaaATStiZYteGAPxSEBHuAzzjlRHYsrdDkaGNXmzRGoalJMiCC"
+ "GMtWSDMhgvRSEgKnywbRgnqWXFlwrhXbbvcgLGtWSuKQBiqIlWkfPMozOTWgVoLHavDJGRYI"
+ "YerrmZnTMtuuxmZALWakfzUbksTwoetqkOiRPGqGZepcVXHoZyOaaaijjZWQLlIhYwiQNbfc"
+ "KCwhhFaMQBoaCnOecJEdKzdsMPFEYQuJNPYiiNtsYxaWBRuWjlLqGokHMNtyTQfSJKbgGdol"
+ "fWlOZdupouQMfUWXIYHzyJHefMDnqxxasDxtgArvDqtwjDBaVEMACPkLFpiDOoKCHqkWVizh"
+ "lKqbOHpsPKkhjRQRNGYRYEfxtBjYvlCvHBNUwVuIwDJYMqHxEFtwdLqYWvjdOfQmNiviDfUq"
+ "pbucbNwjNQfMYgwUuPnQWIPOlqHcbjtuDXvTzLtkdBQanJbrmLSyFqSapZCSPMDOrxWVYzyO"
+ "lwDTTJFmKxoyfPunadkHcrcSQaQsAbrQtbhqwSTXGTPURYTCbNozjAVwbmcyVxIbZudBZWYm"
+ "rnSDyelGCRRWYtrUxvOVWlTLHHdYuAmVMGnGbHscbjmjmAzmYLaCxNNwhmMYdExKvySxuYpE"
+ "rVGwfqMngBCHnZodotNaNJZiNRFWubuPDfiywXPiyVWoQMeOlSuWmpilLTIFOvfpjmJTgrWa"
+ "dgoxYeyPyOaglOvZVGdFOBSeqEcGXBwjoeUAXqkpvOxEpSXhmklKZydTvRVYVvfQdRNNDkCT"
+ "dLNfcZCFQbZORdcDOhwotoyccrSbWvlqYMoiAYeEpDzZTvkamapzZMmCpEutZFCcHBWGIIkr"
+ "urwDNHrobaErPpclyEegLJDtkfUWSNWZosWSbBGAHIvJsFNUlJXbnkSVycLkOVQVcNcUtiBy"
+ "djLDIFsycbPBEWaMvCbntNtJlOeCttvXypGnHAQFnFSiXFWWqonWuVIKmVPpKXuJtFguXCWC"
+ "rNExYYvxLGEmuZJLJDjHgjlQyOzeieCpizJxkrdqKCgomyEkvsyVYSsLeyLvOZQrrgEJgRFK"
+ "CjYtoOfluNrLdRMTRkQXmAiMRFwloYECpXCReAMxOkNiwCtutsrqWoMHsrogRqPoUCueonvW"
+ "MTwmkAkajfGJkhnQidwpwIMEttQkzIMOPvvyWZHpqkMHWlNTeSKibfRfwDyxveKENZhtlPwP"
+ "dfAjwegjRcavtFnkkTNVYdCdCrgdUvzsIcqmUjwGmVvuuQvjVrWWIDBmAzQtiZPYvCOEWjce"
+ "rWzeqVKeiYTJBOedmQCVidOgUIEjfRnbGvUbctYxfRybJkdmeAkLZQMRMGPOnsPbFswXAoCK"
+ "IxWGwohoPpEJxslbqHFKSwknxTmrDCITRZWEDkGQeucPxHBdYkduwbYhKnoxCKhgjBFiFawC"
+ "QtgTDldTQmlOsBiGLquMjuecAbrUJJvNtXbFNGjWxaZPimSRXUJWgRbydpsczOqSFIeEtuKA"
+ "ZpRhmLtPdVNKdSDQZeeImUFmUwXApRTUNHItyvFyJtNtn";
+
+ Sequence src;
+ Sequence dst;
+
+ src.reserve(src_str.length());
+ dst.reserve(dst_str.length());
+
+ for (char c : src_str) {
+ src.push_back(c);
+ }
+ for (char c : dst_str) {
+ dst.push_back(c);
+ }
+
+ VerifyMatch(src, dst, 723);
+}
+
+} // namespace
+} // namespace diff
+} // namespace spvtools
diff --git a/test/fuzz/fuzz_test_util.cpp b/test/fuzz/fuzz_test_util.cpp
index bf0a4ff5..93c9c584 100644
--- a/test/fuzz/fuzz_test_util.cpp
+++ b/test/fuzz/fuzz_test_util.cpp
@@ -149,7 +149,7 @@ void DumpTransformationsJson(
json_options.add_whitespace = true;
auto json_generation_status = google::protobuf::util::MessageToJsonString(
transformations, &json_string, json_options);
- if (json_generation_status == google::protobuf::util::Status::OK) {
+ if (json_generation_status.ok()) {
std::ofstream transformations_json_file(filename);
transformations_json_file << json_string;
transformations_json_file.close();
diff --git a/test/hex_float_test.cpp b/test/hex_float_test.cpp
index 7edfd43d..25d3c707 100644
--- a/test/hex_float_test.cpp
+++ b/test/hex_float_test.cpp
@@ -1395,6 +1395,47 @@ INSTANTIATE_TEST_SUITE_P(
{"0x1.0p1+", true, "+", 2.0f},
{"0x1.0p1-", true, "-", 2.0f}}));
+INSTANTIATE_TEST_SUITE_P(
+ HexFloatPositiveExponentOverflow, FloatStreamParseTest,
+ ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
+ // Positive exponents
+ {"0x1.0p1", true, "", 2.0f}, // fine, a normal number
+ {"0x1.0p15", true, "", 32768.0f}, // fine, a normal number
+ {"0x1.0p127", true, "", float(ldexp(1.0f, 127))}, // good large number
+ {"0x0.8p128", true, "", float(ldexp(1.0f, 127))}, // good large number
+ {"0x0.1p131", true, "", float(ldexp(1.0f, 127))}, // good large number
+ {"0x0.01p135", true, "", float(ldexp(1.0f, 127))}, // good large number
+ {"0x1.0p128", true, "", float(ldexp(1.0f, 128))}, // infinity
+ {"0x1.0p4294967295", true, "", float(ldexp(1.0f, 128))}, // infinity
+ {"0x1.0p5000000000", true, "", float(ldexp(1.0f, 128))}, // infinity
+ {"0x0.0p5000000000", true, "", 0.0f}, // zero mantissa, zero result
+ }));
+
+INSTANTIATE_TEST_SUITE_P(
+ HexFloatNegativeExponentOverflow, FloatStreamParseTest,
+ ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
+ // Positive results, digits before '.'
+ {"0x1.0p-126", true, "",
+ float(ldexp(1.0f, -126))}, // fine, a small normal number
+ {"0x1.0p-127", true, "", float(ldexp(1.0f, -127))}, // denorm number
+ {"0x1.0p-149", true, "",
+ float(ldexp(1.0f, -149))}, // smallest positive denormal
+ {"0x0.8p-148", true, "",
+ float(ldexp(1.0f, -149))}, // smallest positive denormal
+ {"0x0.1p-145", true, "",
+ float(ldexp(1.0f, -149))}, // smallest positive denormal
+ {"0x0.01p-141", true, "",
+ float(ldexp(1.0f, -149))}, // smallest positive denormal
+
+ // underflow rounds down to zero
+ {"0x1.0p-150", true, "", 0.0f},
+ {"0x1.0p-4294967296", true, "",
+ 0.0f}, // avoid exponent overflow in parser
+ {"0x1.0p-5000000000", true, "",
+ 0.0f}, // avoid exponent overflow in parser
+ {"0x0.0p-5000000000", true, "", 0.0f}, // zero mantissa, zero result
+ }));
+
// TODO(awoloszyn): Add fp16 tests and HexFloatTraits.
} // namespace
} // namespace utils
diff --git a/test/link/entry_points_test.cpp b/test/link/entry_points_test.cpp
index df7ea20c..edf9f424 100644
--- a/test/link/entry_points_test.cpp
+++ b/test/link/entry_points_test.cpp
@@ -104,5 +104,48 @@ OpFunctionEnd
"GLCompute, was already defined."));
}
+TEST_F(EntryPoints, LinkedVariables) {
+ const std::string body1 = R"(
+ OpCapability Addresses
+OpCapability Linkage
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
+OpDecorate %7 LinkageAttributes "foo" Export
+%1 = OpTypeInt 32 0
+%2 = OpTypeVector %1 3
+%3 = OpTypePointer Input %2
+%4 = OpVariable %3 Input
+%5 = OpTypeVoid
+%6 = OpTypeFunction %5
+%7 = OpFunction %5 None %6
+%8 = OpLabel
+%9 = OpLoad %2 %4 Aligned 32
+OpReturn
+OpFunctionEnd
+)";
+ const std::string body2 = R"(
+OpCapability Linkage
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %4 "bar"
+OpDecorate %3 LinkageAttributes "foo" Import
+%1 = OpTypeVoid
+%2 = OpTypeFunction %1
+%3 = OpFunction %1 None %2
+OpFunctionEnd
+%4 = OpFunction %1 None %2
+%5 = OpLabel
+%6 = OpFunctionCall %1 %3
+OpReturn
+OpFunctionEnd
+)";
+
+ spvtest::Binary linked_binary;
+ EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary));
+ EXPECT_THAT(GetErrorMessage(), std::string());
+ EXPECT_TRUE(Validate(linked_binary));
+ EXPECT_THAT(GetErrorMessage(), std::string());
+}
+
} // namespace
} // namespace spvtools
diff --git a/test/link/linker_fixture.h b/test/link/linker_fixture.h
index 7bb12235..d005288c 100644
--- a/test/link/linker_fixture.h
+++ b/test/link/linker_fixture.h
@@ -208,6 +208,10 @@ class LinkerTest : public ::testing::Test {
// Returns the accumulated error messages for the test.
std::string GetErrorMessage() const { return error_message_; }
+ bool Validate(const spvtest::Binary& binary) {
+ return tools_.Validate(binary);
+ }
+
private:
spvtools::Context context_;
spvtools::SpirvTools
diff --git a/test/operand_capabilities_test.cpp b/test/operand_capabilities_test.cpp
index bc0ee055..60503461 100644
--- a/test/operand_capabilities_test.cpp
+++ b/test/operand_capabilities_test.cpp
@@ -97,6 +97,14 @@ TEST_P(EnumCapabilityTest, Sample) {
} \
}
+#define CASE6(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4, CAP5, CAP6) \
+ { \
+ SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \
+ SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3, \
+ SpvCapability##CAP4, SpvCapability##CAP5, SpvCapability##CAP6 \
+ } \
+ }
+
// See SPIR-V Section 3.3 Execution Model
INSTANTIATE_TEST_SUITE_P(
ExecutionModel, EnumCapabilityTest,
@@ -168,10 +176,10 @@ INSTANTIATE_TEST_SUITE_P(
Geometry),
CASE1(EXECUTION_MODE, ExecutionModeQuads, Tessellation),
CASE1(EXECUTION_MODE, ExecutionModeIsolines, Tessellation),
- CASE3(EXECUTION_MODE, ExecutionModeOutputVertices, Geometry,
- Tessellation, MeshShadingNV),
- CASE2(EXECUTION_MODE, ExecutionModeOutputPoints, Geometry,
- MeshShadingNV),
+ CASE4(EXECUTION_MODE, ExecutionModeOutputVertices, Geometry,
+ Tessellation, MeshShadingNV, MeshShadingEXT),
+ CASE3(EXECUTION_MODE, ExecutionModeOutputPoints, Geometry,
+ MeshShadingNV, MeshShadingEXT),
CASE1(EXECUTION_MODE, ExecutionModeOutputLineStrip, Geometry),
CASE1(EXECUTION_MODE, ExecutionModeOutputTriangleStrip, Geometry),
CASE1(EXECUTION_MODE, ExecutionModeVecTypeHint, Kernel),
@@ -486,11 +494,11 @@ INSTANTIATE_TEST_SUITE_P(
CASE1(BUILT_IN, BuiltInCullDistance, CullDistance), // Bug 1407, 15234
CASE1(BUILT_IN, BuiltInVertexId, Shader),
CASE1(BUILT_IN, BuiltInInstanceId, Shader),
- CASE5(BUILT_IN, BuiltInPrimitiveId, Geometry, Tessellation,
- RayTracingNV, RayTracingKHR, MeshShadingNV),
+ CASE6(BUILT_IN, BuiltInPrimitiveId, Geometry, Tessellation,
+ RayTracingNV, RayTracingKHR, MeshShadingNV, MeshShadingEXT),
CASE2(BUILT_IN, BuiltInInvocationId, Geometry, Tessellation),
- CASE3(BUILT_IN, BuiltInLayer, Geometry, ShaderViewportIndexLayerEXT, MeshShadingNV),
- CASE3(BUILT_IN, BuiltInViewportIndex, MultiViewport, ShaderViewportIndexLayerEXT, MeshShadingNV), // Bug 15234
+ CASE4(BUILT_IN, BuiltInLayer, Geometry, ShaderViewportIndexLayerEXT, MeshShadingNV, MeshShadingEXT),
+ CASE4(BUILT_IN, BuiltInViewportIndex, MultiViewport, ShaderViewportIndexLayerEXT, MeshShadingNV, MeshShadingEXT), // Bug 15234
CASE1(BUILT_IN, BuiltInTessLevelOuter, Tessellation),
CASE1(BUILT_IN, BuiltInTessLevelInner, Tessellation),
CASE1(BUILT_IN, BuiltInTessCoord, Tessellation),
@@ -533,11 +541,11 @@ INSTANTIATE_TEST_SUITE_P(
Values(SPV_ENV_UNIVERSAL_1_5),
ValuesIn(std::vector<EnumCapabilityCase>{
// SPIR-V 1.5 adds new capabilities to enable these two builtins.
- CASE4(BUILT_IN, BuiltInLayer, Geometry, ShaderLayer,
- ShaderViewportIndexLayerEXT, MeshShadingNV),
- CASE4(BUILT_IN, BuiltInViewportIndex, MultiViewport,
+ CASE5(BUILT_IN, BuiltInLayer, Geometry, ShaderLayer,
+ ShaderViewportIndexLayerEXT, MeshShadingNV, MeshShadingEXT),
+ CASE5(BUILT_IN, BuiltInViewportIndex, MultiViewport,
ShaderViewportIndex, ShaderViewportIndexLayerEXT,
- MeshShadingNV),
+ MeshShadingNV, MeshShadingEXT),
})));
// See SPIR-V Section 3.22 Selection Control
diff --git a/test/operand_pattern_test.cpp b/test/operand_pattern_test.cpp
index 1caf008f..a98a9d76 100644
--- a/test/operand_pattern_test.cpp
+++ b/test/operand_pattern_test.cpp
@@ -91,9 +91,14 @@ INSTANTIATE_TEST_SUITE_P(
{SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, 0, {PREFIX0}, {PREFIX0}},
// Unknown bits means no change. Use all bits that aren't in the
// grammar.
- // The last mask enum is 0x20
+ // The used mask bits are:
+ // 1 through...
+ // 0x20 SpvMemoryAccessNonPrivatePointerMask
+ // also
+ // 0x10000 SpvMemoryAccessAliasScopeINTELMaskShift
+ // 0x20000 SpvMemoryAccessNoAliasINTELMaskMask
{SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS,
- 0xffffffc0,
+ 0xffffffc0 ^ (0x10000) ^ (0x20000),
{PREFIX1},
{PREFIX1}},
// Volatile has no operands.
@@ -111,6 +116,7 @@ INSTANTIATE_TEST_SUITE_P(
SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask,
{PREFIX1},
{PREFIX1, SPV_OPERAND_TYPE_LITERAL_INTEGER}},
+ // Newer masks are not tested
}));
#undef PREFIX0
#undef PREFIX1
diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt
index 759d4237..15966c18 100644
--- a/test/opt/CMakeLists.txt
+++ b/test/opt/CMakeLists.txt
@@ -42,8 +42,10 @@ add_spvtools_unittest(TARGET opt
desc_sroa_test.cpp
eliminate_dead_const_test.cpp
eliminate_dead_functions_test.cpp
+ eliminate_dead_input_components_test.cpp
eliminate_dead_member_test.cpp
feature_manager_test.cpp
+ fix_func_call_arguments_test.cpp
fix_storage_class_test.cpp
flatten_decoration_test.cpp
fold_spec_const_op_composite_test.cpp
@@ -60,6 +62,7 @@ add_spvtools_unittest(TARGET opt
inst_debug_printf_test.cpp
instruction_list_test.cpp
instruction_test.cpp
+ interface_var_sroa_test.cpp
interp_fixup_test.cpp
ir_builder.cpp
ir_context_test.cpp
@@ -82,7 +85,8 @@ add_spvtools_unittest(TARGET opt
propagator_test.cpp
reduce_load_size_test.cpp
redundancy_elimination_test.cpp
- remove_unused_interface_variables_test.cpp
+ remove_dontinline_test.cpp
+ remove_unused_interface_variables_test.cpp
register_liveness.cpp
relax_float_ops_test.cpp
replace_desc_array_access_using_var_index_test.cpp
@@ -94,7 +98,7 @@ add_spvtools_unittest(TARGET opt
spread_volatile_semantics_test.cpp
strength_reduction_test.cpp
strip_debug_info_test.cpp
- strip_nonsemantic_info_test.cpp
+ strip_nonsemantic_info_test.cpp
struct_cfg_analysis_test.cpp
type_manager_test.cpp
types_test.cpp
diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp
index 25f85416..e51098ed 100644
--- a/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -3541,9 +3541,11 @@ OpLoopMerge %14 %15 None
OpBranch %16
%16 = OpLabel
OpSelectionMerge %18 None
-OpSwitch %13 %18 0 %17 1 %15
+OpSwitch %13 %18 0 %17 1 %19
%17 = OpLabel
OpStore %3 %uint_1
+OpBranch %19
+%19 = OpLabel
OpBranch %15
%15 = OpLabel
OpBranch %12
@@ -7626,6 +7628,155 @@ OpFunctionEnd
SinglePassRunAndCheck<AggressiveDCEPass>(text, text, false);
}
+TEST_F(AggressiveDCETest, FunctionBecomesUnreachableAfterDCE) {
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ OpSource ESSL 320
+ %void = OpTypeVoid
+ %4 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+ %7 = OpTypeFunction %int
+ %int_n1 = OpConstant %int -1
+ %2 = OpFunction %void None %4
+ %9 = OpLabel
+ OpKill
+ %10 = OpLabel
+ %11 = OpFunctionCall %int %12
+ OpReturn
+ OpFunctionEnd
+; CHECK: {{%\w+}} = OpFunction %int DontInline|Pure
+ %12 = OpFunction %int DontInline|Pure %7
+; CHECK-NEXT: {{%\w+}} = OpLabel
+ %13 = OpLabel
+ %14 = OpVariable %_ptr_Function_int Function
+; CHECK-NEXT: OpBranch [[header:%\w+]]
+ OpBranch %15
+; CHECK-NEXT: [[header]] = OpLabel
+; CHECK-NEXT: OpBranch [[merge:%\w+]]
+ %15 = OpLabel
+ OpLoopMerge %16 %17 None
+ OpBranch %18
+ %18 = OpLabel
+ %19 = OpLoad %int %14
+ OpBranch %17
+ %17 = OpLabel
+ OpBranch %15
+; CHECK-NEXT: [[merge]] = OpLabel
+ %16 = OpLabel
+; CHECK-NEXT: OpReturnValue %int_n1
+ OpReturnValue %int_n1
+; CHECK-NEXT: OpFunctionEnd
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
+}
+
+TEST_F(AggressiveDCETest, KeepTopLevelDebugInfo) {
+ // Don't eliminate DebugCompilationUnit, DebugSourceContinued, and
+ // DebugEntryPoint
+ const std::string text = R"(
+ OpCapability Shader
+ OpExtension "SPV_KHR_non_semantic_info"
+ %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %MainPs "MainPs" %out_var_SV_Target0
+ OpExecutionMode %MainPs OriginUpperLeft
+ %4 = OpString "foo2.frag"
+ %5 = OpString "
+struct PS_OUTPUT
+{
+ float4 vColor : SV_Target0 ;
+} ;
+
+"
+ %6 = OpString "
+PS_OUTPUT MainPs ( )
+{
+ PS_OUTPUT ps_output ;
+ ps_output . vColor = float4( 1.0, 0.0, 0.0, 0.0 );
+ return ps_output ;
+}
+
+"
+ %7 = OpString "float"
+ %8 = OpString "vColor"
+ %9 = OpString "PS_OUTPUT"
+ %10 = OpString "MainPs"
+ %11 = OpString ""
+ %12 = OpString "ps_output"
+ %13 = OpString "97a939fb"
+ %14 = OpString " foo2.frag -E MainPs -T ps_6_1 -spirv -fspv-target-env=vulkan1.2 -fspv-debug=vulkan-with-source -fcgl -Fo foo2.frag.nopt.spv -Qembed_debug"
+ OpName %out_var_SV_Target0 "out.var.SV_Target0"
+ OpName %MainPs "MainPs"
+ OpDecorate %out_var_SV_Target0 Location 0
+ %float = OpTypeFloat 32
+ %float_1 = OpConstant %float 1
+ %float_0 = OpConstant %float 0
+ %v4float = OpTypeVector %float 4
+ %23 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_0
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %uint_1 = OpConstant %uint 1
+ %uint_4 = OpConstant %uint 4
+ %uint_5 = OpConstant %uint 5
+ %uint_3 = OpConstant %uint 3
+ %uint_0 = OpConstant %uint 0
+ %uint_128 = OpConstant %uint 128
+ %uint_12 = OpConstant %uint 12
+ %uint_2 = OpConstant %uint 2
+ %uint_8 = OpConstant %uint 8
+ %uint_7 = OpConstant %uint 7
+ %uint_9 = OpConstant %uint 9
+ %uint_15 = OpConstant %uint 15
+ %42 = OpTypeFunction %void
+ %uint_10 = OpConstant %uint 10
+ %uint_53 = OpConstant %uint 53
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+ %49 = OpExtInst %void %1 DebugExpression
+ %50 = OpExtInst %void %1 DebugSource %4 %5
+ %51 = OpExtInst %void %1 DebugSourceContinued %6
+; CHECK: %51 = OpExtInst %void %1 DebugSourceContinued %6
+ %52 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %50 %uint_5
+; CHECK: %52 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %50 %uint_5
+ %53 = OpExtInst %void %1 DebugTypeBasic %7 %uint_32 %uint_3 %uint_0
+ %54 = OpExtInst %void %1 DebugTypeVector %53 %uint_4
+ %55 = OpExtInst %void %1 DebugTypeMember %8 %54 %50 %uint_4 %uint_12 %uint_0 %uint_128 %uint_3
+ %56 = OpExtInst %void %1 DebugTypeComposite %9 %uint_1 %50 %uint_2 %uint_8 %52 %9 %uint_128 %uint_3 %55
+ %57 = OpExtInst %void %1 DebugTypeFunction %uint_3 %56
+ %58 = OpExtInst %void %1 DebugFunction %10 %57 %50 %uint_7 %uint_1 %52 %11 %uint_3 %uint_8
+ %59 = OpExtInst %void %1 DebugLexicalBlock %50 %uint_8 %uint_1 %58
+ %60 = OpExtInst %void %1 DebugLocalVariable %12 %56 %50 %uint_9 %uint_15 %59 %uint_4
+ %61 = OpExtInst %void %1 DebugEntryPoint %58 %52 %13 %14
+; CHECK: %61 = OpExtInst %void %1 DebugEntryPoint %58 %52 %13 %14
+ %MainPs = OpFunction %void None %42
+ %62 = OpLabel
+ %63 = OpExtInst %void %1 DebugFunctionDefinition %58 %MainPs
+ %112 = OpExtInst %void %1 DebugScope %59
+ %111 = OpExtInst %void %1 DebugLine %50 %uint_10 %uint_10 %uint_5 %uint_53
+ %110 = OpExtInst %void %1 DebugValue %60 %23 %49 %int_0
+ %113 = OpExtInst %void %1 DebugNoLine
+ %114 = OpExtInst %void %1 DebugNoScope
+ OpStore %out_var_SV_Target0 %23
+ %66 = OpExtInst %void %1 DebugLine %50 %uint_12 %uint_12 %uint_1 %uint_1
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_2);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/ccp_test.cpp b/test/opt/ccp_test.cpp
index ae7043b9..f0f24362 100644
--- a/test/opt/ccp_test.cpp
+++ b/test/opt/ccp_test.cpp
@@ -582,6 +582,35 @@ TEST_F(CCPTest, SkipSpecConstantInstrucitons) {
EXPECT_EQ(std::get<1>(res), Pass::Status::SuccessWithoutChange);
}
+TEST_F(CCPTest, FoldConstantCompositeInstrucitonsWithSpecConst) {
+ const std::string spv_asm = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+ %void = OpTypeVoid
+ %4 = OpTypeFunction %void
+ %bool = OpTypeBool
+ %v3bool = OpTypeVector %bool 3
+ %_struct_8 = OpTypeStruct %v3bool
+ %true = OpConstantTrue %bool
+; CHECK: [[spec_const:%\w+]] = OpSpecConstantComposite %v3bool
+ %11 = OpSpecConstantComposite %v3bool %true %true %true
+ %12 = OpConstantComposite %_struct_8 %11
+; CHECK: OpFunction
+ %1 = OpFunction %void None %4
+ %29 = OpLabel
+ %31 = OpCompositeExtract %v3bool %12 0
+; CHECK: OpCompositeExtract %bool [[spec_const]] 0
+ %32 = OpCompositeExtract %bool %31 0
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ auto result = SinglePassRunAndMatch<CCPPass>(spv_asm, true);
+ EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
TEST_F(CCPTest, UpdateSubsequentPhisToVarying) {
const std::string text = R"(
OpCapability Shader
diff --git a/test/opt/cfg_test.cpp b/test/opt/cfg_test.cpp
index 2cfc9f37..7dfd2bc8 100644
--- a/test/opt/cfg_test.cpp
+++ b/test/opt/cfg_test.cpp
@@ -200,6 +200,125 @@ OpFunctionEnd
ContainerEq(expected_result2)));
}
+TEST_F(CFGTest, SplitLoopHeaderForSingleBlockLoop) {
+ const std::string test = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ %void = OpTypeVoid
+ %uint = OpTypeInt 32 0
+ %uint_0 = OpConstant %uint 0
+ %6 = OpTypeFunction %void
+ %2 = OpFunction %void None %6
+ %7 = OpLabel
+ OpBranch %8
+ %8 = OpLabel
+ %9 = OpPhi %uint %uint_0 %7 %9 %8
+ OpLoopMerge %10 %8 None
+ OpBranch %8
+ %10 = OpLabel
+ OpUnreachable
+ OpFunctionEnd
+)";
+
+ const std::string expected_result = R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main"
+OpExecutionMode %2 OriginUpperLeft
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%6 = OpTypeFunction %void
+%2 = OpFunction %void None %6
+%7 = OpLabel
+OpBranch %8
+%8 = OpLabel
+OpBranch %11
+%11 = OpLabel
+%9 = OpPhi %uint %9 %11 %uint_0 %8
+OpLoopMerge %10 %11 None
+OpBranch %11
+%10 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ASSERT_NE(nullptr, context);
+
+ BasicBlock* loop_header = context->get_instr_block(8);
+ ASSERT_TRUE(loop_header->GetLoopMergeInst() != nullptr);
+
+ CFG* cfg = context->cfg();
+ cfg->SplitLoopHeader(loop_header);
+
+ std::vector<uint32_t> binary;
+ bool skip_nop = false;
+ context->module()->ToBinary(&binary, skip_nop);
+
+ std::string optimized_asm;
+ SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+ EXPECT_TRUE(tools.Disassemble(binary, &optimized_asm,
+ SpirvTools::kDefaultDisassembleOption))
+ << "Disassembling failed for shader\n"
+ << std::endl;
+
+ EXPECT_EQ(optimized_asm, expected_result);
+}
+
+TEST_F(CFGTest, ComputeStructedOrderForLoop) {
+ const std::string test = R"(
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main"
+OpName %main "main"
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%5 = OpConstant %uint 5
+%main = OpFunction %void None %4
+%8 = OpLabel
+OpBranch %9
+%9 = OpLabel
+OpLoopMerge %11 %10 None
+OpBranchConditional %true %11 %10
+%10 = OpLabel
+OpBranch %9
+%11 = OpLabel
+OpBranch %12
+%12 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ASSERT_NE(nullptr, context);
+
+ CFG* cfg = context->cfg();
+ Module* module = context->module();
+ Function* function = &*module->begin();
+ std::list<BasicBlock*> order;
+ cfg->ComputeStructuredOrder(function, context->get_instr_block(9),
+ context->get_instr_block(11), &order);
+
+ // Order should contain the loop header, the continue target, and the merge
+ // node.
+ std::list<BasicBlock*> expected_result = {context->get_instr_block(9),
+ context->get_instr_block(10),
+ context->get_instr_block(11)};
+ EXPECT_THAT(order, ContainerEq(expected_result));
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/compact_ids_test.cpp b/test/opt/compact_ids_test.cpp
index ba31d84b..7c232fe4 100644
--- a/test/opt/compact_ids_test.cpp
+++ b/test/opt/compact_ids_test.cpp
@@ -310,6 +310,41 @@ OpFunctionEnd
EXPECT_THAT(disassembly, ::testing::Eq(expected));
}
+TEST(CompactIds, ResetIdBound) {
+ const std::string input(R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %1 "main"
+OpExecutionMode %1 OriginUpperLeft
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%1 = OpFunction %void None %3
+%4 = OpLabel
+OpReturn
+OpFunctionEnd
+)");
+
+ spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, input,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ASSERT_NE(context, nullptr);
+
+ CompactIdsPass compact_id_pass;
+ context->module()->SetIdBound(20000);
+ const auto status = compact_id_pass.Run(context.get());
+ EXPECT_EQ(status, Pass::Status::SuccessWithChange);
+ EXPECT_EQ(context->module()->id_bound(), 5);
+
+ // Test output just in case
+ std::vector<uint32_t> binary;
+ context->module()->ToBinary(&binary, false);
+ std::string disassembly;
+ tools.Disassemble(binary, &disassembly,
+ SpirvTools::kDefaultDisassembleOption);
+
+ EXPECT_THAT(disassembly, ::testing::Eq(input));
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/copy_prop_array_test.cpp b/test/opt/copy_prop_array_test.cpp
index a4599f0f..d6e376ec 100644
--- a/test/opt/copy_prop_array_test.cpp
+++ b/test/opt/copy_prop_array_test.cpp
@@ -1839,6 +1839,110 @@ OpFunctionEnd
SinglePassRunAndCheck<CopyPropagateArrays>(text, text, false);
}
+
+// Since Spir-V 1.4, resources that are used by a shader must be on the
+// OpEntryPoint instruction with the inputs and outputs. This test ensures that
+// this does not stop the pass from working.
+TEST_F(CopyPropArrayPassTest, EntryPointUser) {
+ const std::string before = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %g_rwTexture3d
+OpExecutionMode %main LocalSize 256 1 1
+OpSource HLSL 660
+OpName %type_3d_image "type.3d.image"
+OpName %g_rwTexture3d "g_rwTexture3d"
+OpName %main "main"
+OpDecorate %g_rwTexture3d DescriptorSet 0
+OpDecorate %g_rwTexture3d Binding 0
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%uint_2 = OpConstant %uint 2
+%uint_3 = OpConstant %uint 3
+%v3uint = OpTypeVector %uint 3
+%10 = OpConstantComposite %v3uint %uint_1 %uint_2 %uint_3
+%type_3d_image = OpTypeImage %uint 3D 2 0 0 2 R32ui
+%_ptr_UniformConstant_type_3d_image = OpTypePointer UniformConstant %type_3d_image
+%void = OpTypeVoid
+%13 = OpTypeFunction %void
+%_ptr_Function_type_3d_image = OpTypePointer Function %type_3d_image
+%_ptr_Image_uint = OpTypePointer Image %uint
+%g_rwTexture3d = OpVariable %_ptr_UniformConstant_type_3d_image UniformConstant
+%main = OpFunction %void None %13
+%16 = OpLabel
+%17 = OpVariable %_ptr_Function_type_3d_image Function
+%18 = OpLoad %type_3d_image %g_rwTexture3d
+OpStore %17 %18
+; CHECK: %19 = OpImageTexelPointer %_ptr_Image_uint %g_rwTexture3d %10 %uint_0
+%19 = OpImageTexelPointer %_ptr_Image_uint %17 %10 %uint_0
+%20 = OpAtomicIAdd %uint %19 %uint_1 %uint_0 %uint_1
+OpReturn
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+ SinglePassRunAndMatch<CopyPropagateArrays>(before, false);
+}
+
+// As per SPIRV spec, struct cannot be indexed with non-constant indices
+// through OpAccessChain, only arrays.
+// The copy-propagate-array pass tries to remove superfluous copies when the
+// original array could be indexed instead of the copy.
+//
+// This test verifies we handle this case:
+// struct SRC { int field1; ...; int fieldN }
+// int tmp_arr[N] = { SRC.field1, ..., SRC.fieldN }
+// return tmp_arr[index];
+//
+// In such case, we cannot optimize the access: this array was added to allow
+// dynamic indexing in the struct.
+TEST_F(CopyPropArrayPassTest, StructIndexCannotBecomeDynamic) {
+ const std::string text = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %1 "main"
+OpDecorate %2 DescriptorSet 0
+OpDecorate %2 Binding 0
+OpMemberDecorate %_struct_3 0 Offset 0
+OpDecorate %_struct_3 Block
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_struct_3 = OpTypeStruct %v4float
+%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3
+%uint = OpTypeInt 32 0
+%void = OpTypeVoid
+%11 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+%13 = OpTypeFunction %v4float %_ptr_Function_uint
+%uint_1 = OpConstant %uint 1
+%_arr_v4float_uint_1 = OpTypeArray %v4float %uint_1
+%_ptr_Function__arr_v4float_uint_1 = OpTypePointer Function %_arr_v4float_uint_1
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+%2 = OpVariable %_ptr_Uniform__struct_3 Uniform
+%19 = OpUndef %v4float
+%1 = OpFunction %void None %11
+%20 = OpLabel
+OpReturn
+OpFunctionEnd
+%21 = OpFunction %v4float None %13
+%22 = OpFunctionParameter %_ptr_Function_uint
+%23 = OpLabel
+%24 = OpVariable %_ptr_Function__arr_v4float_uint_1 Function
+%25 = OpAccessChain %_ptr_Uniform_v4float %2 %int_0
+%26 = OpLoad %v4float %25
+%27 = OpCompositeConstruct %_arr_v4float_uint_1 %26
+OpStore %24 %27
+%28 = OpLoad %uint %22
+%29 = OpAccessChain %_ptr_Function_v4float %24 %28
+OpReturnValue %19
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<CopyPropagateArrays>(text, text, false);
+}
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/dead_branch_elim_test.cpp b/test/opt/dead_branch_elim_test.cpp
index b04c8f5e..1095d3b8 100644
--- a/test/opt/dead_branch_elim_test.cpp
+++ b/test/opt/dead_branch_elim_test.cpp
@@ -2570,9 +2570,8 @@ OpFunctionEnd
TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoop3) {
// Checks that if a selection merge construct contains a conditional branch
- // to the merge of a surrounding loop, the selection merge, and another block
- // inside the selection merge, then we must keep the OpSelectionMerge
- // instruction on that branch.
+ // to the selection merge, and another block inside the selection merge,
+ // then we must keep the OpSelectionMerge instruction on that branch.
const std::string predefs = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@@ -2586,6 +2585,7 @@ OpSource GLSL 140
%true = OpConstantTrue %bool
%uint = OpTypeInt 32 0
%undef_int = OpUndef %uint
+%undef_bool = OpUndef %bool
)";
const std::string body =
@@ -2596,7 +2596,7 @@ OpSource GLSL 140
; CHECK-NEXT: OpBranch [[bb2:%\w+]]
; CHECK: [[bb2]] = OpLabel
; CHECK-NEXT: OpSelectionMerge [[sel_merge:%\w+]] None
-; CHECK-NEXT: OpSwitch {{%\w+}} [[sel_merge]] 0 [[loop_merge]] 1 [[bb3:%\w+]]
+; CHECK-NEXT: OpBranchConditional {{%\w+}} [[sel_merge]] [[bb3:%\w+]]
; CHECK: [[bb3]] = OpLabel
; CHECK-NEXT: OpBranch [[sel_merge]]
; CHECK: [[sel_merge]] = OpLabel
@@ -2613,7 +2613,8 @@ OpBranch %bb1
OpSelectionMerge %sel_merge None
OpBranchConditional %true %bb2 %bb4
%bb2 = OpLabel
-OpSwitch %undef_int %sel_merge 0 %loop_merge 1 %bb3
+;OpSwitch %undef_int %sel_merge 0 %loop_merge 1 %bb3
+OpBranchConditional %undef_bool %sel_merge %bb3
%bb3 = OpLabel
OpBranch %sel_merge
%bb4 = OpLabel
@@ -2632,9 +2633,8 @@ OpFunctionEnd
TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoopContinue3) {
// Checks that if a selection merge construct contains a conditional branch
- // to the merge of a surrounding loop, the selection merge, and another block
- // inside the selection merge, then we must keep the OpSelectionMerge
- // instruction on that branch.
+ // the selection merge, and another block inside the selection merge, then we
+ // must keep the OpSelectionMerge instruction on that branch.
const std::string predefs = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@@ -2648,6 +2648,7 @@ OpSource GLSL 140
%true = OpConstantTrue %bool
%uint = OpTypeInt 32 0
%undef_int = OpUndef %uint
+%undef_bool = OpUndef %bool
)";
const std::string body =
@@ -2660,7 +2661,7 @@ OpSource GLSL 140
; CHECK-NEXT: OpBranch [[bb2:%\w+]]
; CHECK: [[bb2]] = OpLabel
; CHECK-NEXT: OpSelectionMerge [[sel_merge:%\w+]] None
-; CHECK-NEXT: OpSwitch {{%\w+}} [[sel_merge]] 0 [[loop_continue]] 1 [[bb3:%\w+]]
+; CHECK-NEXT: OpBranchConditional {{%\w+}} [[sel_merge]] [[bb3:%\w+]]
; CHECK: [[bb3]] = OpLabel
; CHECK-NEXT: OpBranch [[sel_merge]]
; CHECK: [[sel_merge]] = OpLabel
@@ -2679,131 +2680,7 @@ OpBranch %bb1
OpSelectionMerge %sel_merge None
OpBranchConditional %true %bb2 %bb4
%bb2 = OpLabel
-OpSwitch %undef_int %sel_merge 0 %cont 1 %bb3
-%bb3 = OpLabel
-OpBranch %sel_merge
-%bb4 = OpLabel
-OpBranch %sel_merge
-%sel_merge = OpLabel
-OpBranch %loop_merge
-%cont = OpLabel
-OpBranch %loop_header
-%loop_merge = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
-}
-
-TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoop4) {
- // Same as |SelectionMergeWithExitToLoop|, except the branch in the selection
- // construct is an |OpSwitch| instead of an |OpConditionalBranch|. The
- // OpSelectionMerge instruction is not needed in this case either.
- const std::string predefs = R"(
-OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main"
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 140
-%void = OpTypeVoid
-%func_type = OpTypeFunction %void
-%bool = OpTypeBool
-%true = OpConstantTrue %bool
-%uint = OpTypeInt 32 0
-%undef_int = OpUndef %uint
-)";
-
- const std::string body =
- R"(
-; CHECK: OpLoopMerge [[loop_merge:%\w+]]
-; CHECK-NEXT: OpBranch [[bb1:%\w+]]
-; CHECK: [[bb1]] = OpLabel
-; CHECK-NEXT: OpBranch [[bb2:%\w+]]
-; CHECK: [[bb2]] = OpLabel
-; CHECK-NEXT: OpSelectionMerge
-; CHECK-NEXT: OpSwitch {{%\w+}} [[bb3:%\w+]] 0 [[loop_merge]] 1 [[bb3:%\w+]]
-; CHECK: [[bb3]] = OpLabel
-; CHECK-NEXT: OpBranch [[sel_merge:%\w+]]
-; CHECK: [[sel_merge]] = OpLabel
-; CHECK-NEXT: OpBranch [[loop_merge]]
-; CHECK: [[loop_merge]] = OpLabel
-; CHECK-NEXT: OpReturn
-%main = OpFunction %void None %func_type
-%entry_bb = OpLabel
-OpBranch %loop_header
-%loop_header = OpLabel
-OpLoopMerge %loop_merge %cont None
-OpBranch %bb1
-%bb1 = OpLabel
-OpSelectionMerge %sel_merge None
-OpBranchConditional %true %bb2 %bb4
-%bb2 = OpLabel
-OpSelectionMerge %bb3 None
-OpSwitch %undef_int %bb3 0 %loop_merge 1 %bb3
-%bb3 = OpLabel
-OpBranch %sel_merge
-%bb4 = OpLabel
-OpBranch %sel_merge
-%sel_merge = OpLabel
-OpBranch %loop_merge
-%cont = OpLabel
-OpBranch %loop_header
-%loop_merge = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
-}
-
-TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoopContinue4) {
- // Same as |SelectionMergeWithExitToLoopContinue|, except the branch in the
- // selection construct is an |OpSwitch| instead of an |OpConditionalBranch|.
- // The OpSelectionMerge instruction is not needed in this case either.
- const std::string predefs = R"(
-OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main"
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 140
-%void = OpTypeVoid
-%func_type = OpTypeFunction %void
-%bool = OpTypeBool
-%true = OpConstantTrue %bool
-%uint = OpTypeInt 32 0
-%undef_int = OpUndef %uint
-)";
-
- const std::string body =
- R"(
-; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]]
-; CHECK-NEXT: OpBranch [[bb1:%\w+]]
-; CHECK: [[bb1]] = OpLabel
-; CHECK-NEXT: OpBranch [[bb2:%\w+]]
-; CHECK: [[bb2]] = OpLabel
-; CHECK-NEXT: OpSelectionMerge
-; CHECK-NEXT: OpSwitch {{%\w+}} [[bb3:%\w+]] 0 [[loop_cont]] 1 [[bb3:%\w+]]
-; CHECK: [[bb3]] = OpLabel
-; CHECK-NEXT: OpBranch [[sel_merge:%\w+]]
-; CHECK: [[sel_merge]] = OpLabel
-; CHECK-NEXT: OpBranch [[loop_merge]]
-; CHECK: [[loop_merge]] = OpLabel
-; CHECK-NEXT: OpReturn
-%main = OpFunction %void None %func_type
-%entry_bb = OpLabel
-OpBranch %loop_header
-%loop_header = OpLabel
-OpLoopMerge %loop_merge %cont None
-OpBranch %bb1
-%bb1 = OpLabel
-OpSelectionMerge %sel_merge None
-OpBranchConditional %true %bb2 %bb4
-%bb2 = OpLabel
-OpSelectionMerge %bb3 None
-OpSwitch %undef_int %bb3 0 %cont 1 %bb3
+OpBranchConditional %undef_bool %sel_merge %bb3
%bb3 = OpLabel
OpBranch %sel_merge
%bb4 = OpLabel
@@ -3036,9 +2913,11 @@ TEST_F(DeadBranchElimTest, UnreachableMergeAndContinueSameBlock) {
; CHECK-NEXT: OpLoopMerge [[outer_merge:%\w+]] [[outer_cont:%\w+]] None
; CHECK-NEXT: OpBranch [[inner:%\w+]]
; CHECK: [[inner]] = OpLabel
-; CHECK: OpLoopMerge [[outer_cont]] [[inner_cont:%\w+]] None
+; CHECK: OpLoopMerge [[inner_merge:%\w+]] [[inner_cont:%\w+]] None
; CHECK: [[inner_cont]] = OpLabel
; CHECK-NEXT: OpBranch [[inner]]
+; CHECK: [[inner_merge]] = OpLabel
+; CHECK-NEXT: OpUnreachable
; CHECK: [[outer_cont]] = OpLabel
; CHECK-NEXT: OpBranch [[outer]]
; CHECK: [[outer_merge]] = OpLabel
@@ -3058,7 +2937,7 @@ OpBranch %outer_loop
OpLoopMerge %outer_merge %outer_continue None
OpBranch %inner_loop
%inner_loop = OpLabel
-OpLoopMerge %outer_continue %inner_continue None
+OpLoopMerge %inner_merge %inner_continue None
OpBranch %inner_body
%inner_body = OpLabel
OpSelectionMerge %inner_continue None
@@ -3066,7 +2945,9 @@ OpBranchConditional %true %ret %inner_continue
%ret = OpLabel
OpReturn
%inner_continue = OpLabel
-OpBranchConditional %true %outer_continue %inner_loop
+OpBranchConditional %true %inner_merge %inner_loop
+%inner_merge = OpLabel
+OpBranch %outer_continue
%outer_continue = OpLabel
OpBranchConditional %true %outer_merge %outer_loop
%outer_merge = OpLabel
diff --git a/test/opt/eliminate_dead_input_components_test.cpp b/test/opt/eliminate_dead_input_components_test.cpp
new file mode 100644
index 00000000..822914a8
--- /dev/null
+++ b/test/opt/eliminate_dead_input_components_test.cpp
@@ -0,0 +1,468 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// 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 <vector>
+
+#include "gmock/gmock.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using ElimDeadInputComponentsTest = PassTest<::testing::Test>;
+
+TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndex) {
+ // Should reduce to uv[2]
+ //
+ // #version 450
+ //
+ // layout(location = 0) in vec4 uv[8];
+ //
+ // out gl_PerVertex {
+ // vec4 gl_Position;
+ // };
+ //
+ // void main()
+ // {
+ // gl_Position = uv[1];
+ // }
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %_ %uv
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %gl_PerVertex "gl_PerVertex"
+ OpMemberName %gl_PerVertex 0 "gl_Position"
+ OpName %_ ""
+ OpName %uv "uv"
+ OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+ OpDecorate %gl_PerVertex Block
+ OpDecorate %uv Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+ %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %uint = OpTypeInt 32 0
+ %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+ %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+ %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_2 Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
+ %21 = OpLoad %v4float %20
+ %23 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+ OpStore %23 %21
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
+}
+
+TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndexInBounds) {
+ // Same as ElimOneConstantIndex but with OpInBoundsAccessChain
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %_ %uv
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %gl_PerVertex "gl_PerVertex"
+ OpMemberName %gl_PerVertex 0 "gl_Position"
+ OpName %_ ""
+ OpName %uv "uv"
+ OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+ OpDecorate %gl_PerVertex Block
+ OpDecorate %uv Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+ %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %uint = OpTypeInt 32 0
+ %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+ %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+ %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_2 Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %20 = OpInBoundsAccessChain %_ptr_Input_v4float %uv %int_1
+ %21 = OpLoad %v4float %20
+ %23 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+ OpStore %23 %21
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
+}
+
+TEST_F(ElimDeadInputComponentsTest, ElimTwoConstantIndices) {
+ // Should reduce to uv[4]
+ //
+ // #version 450
+ //
+ // layout(location = 0) in vec4 uv[8];
+ //
+ // out gl_PerVertex {
+ // vec4 gl_Position;
+ // };
+ //
+ // void main()
+ // {
+ // gl_Position = uv[1] + uv[3];
+ // }
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %_ %uv
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %gl_PerVertex "gl_PerVertex"
+ OpMemberName %gl_PerVertex 0 "gl_Position"
+ OpName %_ ""
+ OpName %uv "uv"
+ OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+ OpDecorate %gl_PerVertex Block
+ OpDecorate %uv Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+ %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %uint = OpTypeInt 32 0
+ %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+ %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+ %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+ %int_3 = OpConstant %int 3
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_4 Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
+ %21 = OpLoad %v4float %20
+ %23 = OpAccessChain %_ptr_Input_v4float %uv %int_3
+ %24 = OpLoad %v4float %23
+ %25 = OpFAdd %v4float %21 %24
+ %27 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+ OpStore %27 %25
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
+}
+
+TEST_F(ElimDeadInputComponentsTest, NoElimMaxConstantIndex) {
+ // Should not reduce uv[8] because of max index of 7
+ //
+ // #version 450
+ //
+ // layout(location = 0) in vec4 uv[8];
+ //
+ // out gl_PerVertex {
+ // vec4 gl_Position;
+ // };
+ //
+ // void main()
+ // {
+ // gl_Position = uv[1] + uv[7];
+ // }
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %_ %uv
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %gl_PerVertex "gl_PerVertex"
+ OpMemberName %gl_PerVertex 0 "gl_Position"
+ OpName %_ ""
+ OpName %uv "uv"
+ OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+ OpDecorate %gl_PerVertex Block
+ OpDecorate %uv Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+ %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %uint = OpTypeInt 32 0
+ %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+ %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+ %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+ %int_7 = OpConstant %int 7
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
+ %21 = OpLoad %v4float %20
+ %23 = OpAccessChain %_ptr_Input_v4float %uv %int_7
+ %24 = OpLoad %v4float %23
+ %25 = OpFAdd %v4float %21 %24
+ %27 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+ OpStore %27 %25
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
+}
+
+TEST_F(ElimDeadInputComponentsTest, NoElimNonConstantIndex) {
+ // Should not reduce uv[8] because of non-constant index of ui
+ //
+ // #version 450
+ //
+ // layout(location = 0) in vec4 uv[8];
+ //
+ // out gl_PerVertex {
+ // vec4 gl_Position;
+ // };
+ //
+ // uniform ubname {
+ // int ui;
+ // } ubinst;
+ //
+ // void main()
+ // {
+ // gl_Position = uv[1] + uv[ubinst.ui];
+ // }
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %_ %uv %ubinst
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %gl_PerVertex "gl_PerVertex"
+ OpMemberName %gl_PerVertex 0 "gl_Position"
+ OpName %_ ""
+ OpName %uv "uv"
+ OpName %ubname "ubname"
+ OpMemberName %ubname 0 "ui"
+ OpName %ubinst "ubinst"
+ OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+ OpDecorate %gl_PerVertex Block
+ OpDecorate %uv Location 0
+ OpMemberDecorate %ubname 0 Offset 0
+ OpDecorate %ubname Block
+ OpDecorate %ubinst DescriptorSet 0
+ OpDecorate %ubinst Binding 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+ %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %uint = OpTypeInt 32 0
+ %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+ %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+ %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+ %ubname = OpTypeStruct %int
+%_ptr_Uniform_ubname = OpTypePointer Uniform %ubname
+ %ubinst = OpVariable %_ptr_Uniform_ubname Uniform
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
+ %21 = OpLoad %v4float %20
+ %26 = OpAccessChain %_ptr_Uniform_int %ubinst %int_0
+ %27 = OpLoad %int %26
+ %28 = OpAccessChain %_ptr_Input_v4float %uv %27
+ %29 = OpLoad %v4float %28
+ %30 = OpFAdd %v4float %21 %29
+ %32 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+ OpStore %32 %30
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
+}
+
+TEST_F(ElimDeadInputComponentsTest, NoElimNonIndexedAccessChain) {
+ // Should not change due to non-indexed access chain
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %_ %uv
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %gl_PerVertex "gl_PerVertex"
+ OpMemberName %gl_PerVertex 0 "gl_Position"
+ OpName %_ ""
+ OpName %uv "uv"
+ OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+ OpDecorate %gl_PerVertex Block
+ OpDecorate %uv Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+ %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %uint = OpTypeInt 32 0
+ %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+ %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+ %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %20 = OpAccessChain %_ptr_Input__arr_v4float_uint_8 %uv
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
+}
+
+TEST_F(ElimDeadInputComponentsTest, ElimStructMember) {
+ // Should eliminate uv
+ //
+ // #version 450
+ //
+ // in Vertex {
+ // vec4 Cd;
+ // vec2 uv;
+ // } iVert;
+ //
+ // out vec4 fragColor;
+ //
+ // void main()
+ // {
+ // vec4 color = vec4(iVert.Cd);
+ // fragColor = color;
+ // }
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %iVert %fragColor
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %Vertex "Vertex"
+ OpMemberName %Vertex 0 "Cd"
+ OpMemberName %Vertex 1 "uv"
+ OpName %iVert "iVert"
+ OpName %fragColor "fragColor"
+ OpDecorate %Vertex Block
+ OpDecorate %iVert Location 0
+ OpDecorate %fragColor Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+ %v2float = OpTypeVector %float 2
+ %Vertex = OpTypeStruct %v4float %v2float
+; CHECK: %Vertex = OpTypeStruct %v4float %v2float
+; CHECK: [[sty:%\w+]] = OpTypeStruct %v4float
+%_ptr_Input_Vertex = OpTypePointer Input %Vertex
+; CHECK: [[pty:%\w+]] = OpTypePointer Input [[sty]]
+ %iVert = OpVariable %_ptr_Input_Vertex Input
+; CHECK: %iVert = OpVariable [[pty]] Input
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %fragColor = OpVariable %_ptr_Output_v4float Output
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %17 = OpAccessChain %_ptr_Input_v4float %iVert %int_0
+ %18 = OpLoad %v4float %17
+ OpStore %fragColor %18
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
+}
+
+} // namespace
+} // namespace opt
+} // namespace spvtools
diff --git a/test/opt/fix_func_call_arguments_test.cpp b/test/opt/fix_func_call_arguments_test.cpp
new file mode 100644
index 00000000..ecd13a86
--- /dev/null
+++ b/test/opt/fix_func_call_arguments_test.cpp
@@ -0,0 +1,152 @@
+// Copyright (c) 2022 Advanced Micro Devices, Inc.
+//
+// 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 "gmock/gmock.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using FixFuncCallArgumentsTest = PassTest<::testing::Test>;
+TEST_F(FixFuncCallArgumentsTest, Simple) {
+ const std::string text = R"(
+;
+; CHECK: [[v0:%\w+]] = OpVariable %_ptr_Function_float Function
+; CHECK: [[v1:%\w+]] = OpVariable %_ptr_Function_float Function
+; CHECK: [[v2:%\w+]] = OpVariable %_ptr_Function_T Function
+; CHECK: [[ac0:%\w+]] = OpAccessChain %_ptr_Function_float %t %int_0
+; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_float %r1 %int_0 %uint_0
+; CHECK: [[ld0:%\w+]] = OpLoad %float [[ac0]]
+; CHECK: OpStore [[v1]] [[ld0]]
+; CHECK: [[ld1:%\w+]] = OpLoad %float [[ac1]]
+; CHECK: OpStore [[v0]] [[ld1]]
+; CHECK: [[func:%\w+]] = OpFunctionCall %void %fn [[v1]] [[v0]]
+; CHECK: [[ld2:%\w+]] = OpLoad %float [[v0]]
+; CHECK: OpStore [[ac1]] [[ld2]]
+; CHECK: [[ld3:%\w+]] = OpLoad %float [[v1]]
+; CHECK: OpStore [[ac0]] [[ld3]]
+;
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpSource HLSL 630
+OpName %type_RWStructuredBuffer_float "type.RWStructuredBuffer.float"
+OpName %r1 "r1"
+OpName %type_ACSBuffer_counter "type.ACSBuffer.counter"
+OpMemberName %type_ACSBuffer_counter 0 "counter"
+OpName %counter_var_r1 "counter.var.r1"
+OpName %main "main"
+OpName %bb_entry "bb.entry"
+OpName %T "T"
+OpMemberName %T 0 "t0"
+OpName %t "t"
+OpName %fn "fn"
+OpName %p0 "p0"
+OpName %p2 "p2"
+OpName %bb_entry_0 "bb.entry"
+OpDecorate %main LinkageAttributes "main" Export
+OpDecorate %r1 DescriptorSet 0
+OpDecorate %r1 Binding 0
+OpDecorate %counter_var_r1 DescriptorSet 0
+OpDecorate %counter_var_r1 Binding 1
+OpDecorate %_runtimearr_float ArrayStride 4
+OpMemberDecorate %type_RWStructuredBuffer_float 0 Offset 0
+OpDecorate %type_RWStructuredBuffer_float BufferBlock
+OpMemberDecorate %type_ACSBuffer_counter 0 Offset 0
+OpDecorate %type_ACSBuffer_counter BufferBlock
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%int_1 = OpConstant %int 1
+%float = OpTypeFloat 32
+%_runtimearr_float = OpTypeRuntimeArray %float
+%type_RWStructuredBuffer_float = OpTypeStruct %_runtimearr_float
+%_ptr_Uniform_type_RWStructuredBuffer_float = OpTypePointer Uniform %type_RWStructuredBuffer_float
+%type_ACSBuffer_counter = OpTypeStruct %int
+%_ptr_Uniform_type_ACSBuffer_counter = OpTypePointer Uniform %type_ACSBuffer_counter
+%15 = OpTypeFunction %int
+%T = OpTypeStruct %float
+%_ptr_Function_T = OpTypePointer Function %T
+%_ptr_Function_float = OpTypePointer Function %float
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%void = OpTypeVoid
+%27 = OpTypeFunction %void %_ptr_Function_float %_ptr_Function_float
+%r1 = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_float Uniform
+%counter_var_r1 = OpVariable %_ptr_Uniform_type_ACSBuffer_counter Uniform
+%main = OpFunction %int None %15
+%bb_entry = OpLabel
+%t = OpVariable %_ptr_Function_T Function
+%21 = OpAccessChain %_ptr_Function_float %t %int_0
+%23 = OpAccessChain %_ptr_Uniform_float %r1 %int_0 %uint_0
+%25 = OpFunctionCall %void %fn %21 %23
+OpReturnValue %int_1
+OpFunctionEnd
+%fn = OpFunction %void DontInline %27
+%p0 = OpFunctionParameter %_ptr_Function_float
+%p2 = OpFunctionParameter %_ptr_Function_float
+%bb_entry_0 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<FixFuncCallArgumentsPass>(text, true);
+}
+
+TEST_F(FixFuncCallArgumentsTest, NotAccessChainInput) {
+ const std::string text = R"(
+;
+; CHECK: [[o:%\w+]] = OpCopyObject %_ptr_Function_float %t
+; CHECK: [[func:%\w+]] = OpFunctionCall %void %fn [[o]]
+;
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpSource HLSL 630
+OpName %main "main"
+OpName %bb_entry "bb.entry"
+OpName %t "t"
+OpName %fn "fn"
+OpName %p0 "p0"
+OpName %bb_entry_0 "bb.entry"
+OpDecorate %main LinkageAttributes "main" Export
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%4 = OpTypeFunction %int
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%void = OpTypeVoid
+%12 = OpTypeFunction %void %_ptr_Function_float
+%main = OpFunction %int None %4
+%bb_entry = OpLabel
+%t = OpVariable %_ptr_Function_float Function
+%t1 = OpCopyObject %_ptr_Function_float %t
+%10 = OpFunctionCall %void %fn %t1
+OpReturnValue %int_1
+OpFunctionEnd
+%fn = OpFunction %void DontInline %12
+%p0 = OpFunctionParameter %_ptr_Function_float
+%bb_entry_0 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<FixFuncCallArgumentsPass>(text, false);
+}
+
+} // namespace
+} // namespace opt
+} // namespace spvtools \ No newline at end of file
diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp
index df8f3b12..a034e959 100644
--- a/test/opt/fold_test.cpp
+++ b/test/opt/fold_test.cpp
@@ -147,6 +147,9 @@ OpName %main "main"
%v2double = OpTypeVector %double 2
%v2half = OpTypeVector %half 2
%v2bool = OpTypeVector %bool 2
+%m2x2int = OpTypeMatrix %v2int 2
+%mat4v4float = OpTypeMatrix %v4float 4
+%mat4v4double = OpTypeMatrix %v4double 4
%struct_v2int_int_int = OpTypeStruct %v2int %int %int
%_ptr_int = OpTypePointer Function %int
%_ptr_uint = OpTypePointer Function %uint
@@ -218,7 +221,9 @@ OpName %main "main"
%struct_v2int_int_int_null = OpConstantNull %struct_v2int_int_int
%v2int_null = OpConstantNull %v2int
%102 = OpConstantComposite %v2int %103 %103
+%v4int_undef = OpUndef %v4int
%v4int_0_0_0_0 = OpConstantComposite %v4int %int_0 %int_0 %int_0 %int_0
+%m2x2int_undef = OpUndef %m2x2int
%struct_undef_0_0 = OpConstantComposite %struct_v2int_int_int %v2int_undef %int_0 %int_0
%float_n1 = OpConstant %float -1
%104 = OpConstant %float 0 ; Need a def with an numerical id to define id maps.
@@ -273,13 +278,20 @@ OpName %main "main"
%v4float_0_0_0_1 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1
%v4float_0_1_0_0 = OpConstantComposite %v4float %float_0 %float_1 %float_null %float_0
%v4float_1_1_1_1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%v4float_1_2_3_4 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
+%v4float_null = OpConstantNull %v4float
+%mat4v4float_null = OpConstantComposite %mat4v4float %v4float_null %v4float_null %v4float_null %v4float_null
+%mat4v4float_1_2_3_4 = OpConstantComposite %mat4v4float %v4float_1_2_3_4 %v4float_1_2_3_4 %v4float_1_2_3_4 %v4float_1_2_3_4
%107 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_0
%v4double_0_0_0_0 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_0
%v4double_0_0_0_1 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_1
%v4double_0_1_0_0 = OpConstantComposite %v4double %double_0 %double_1 %double_null %double_0
%v4double_1_1_1_1 = OpConstantComposite %v4double %double_1 %double_1 %double_1 %double_1
+%v4double_1_2_3_4 = OpConstantComposite %v4double %double_1 %double_2 %double_3 %double_4
%v4double_1_1_1_0p5 = OpConstantComposite %v4double %double_1 %double_1 %double_1 %double_0p5
%v4double_null = OpConstantNull %v4double
+%mat4v4double_null = OpConstantComposite %mat4v4double %v4double_null %v4double_null %v4double_null %v4double_null
+%mat4v4double_1_2_3_4 = OpConstantComposite %mat4v4double %v4double_1_2_3_4 %v4double_1_2_3_4 %v4double_1_2_3_4 %v4double_1_2_3_4
%v4float_n1_2_1_3 = OpConstantComposite %v4float %float_n1 %float_2 %float_1 %float_3
%uint_0x3f800000 = OpConstant %uint 0x3f800000
%uint_0xbf800000 = OpConstant %uint 0xbf800000
@@ -909,7 +921,61 @@ INSTANTIATE_TEST_SUITE_P(TestCase, DoubleVectorInstructionFoldingTest,
"%2 = OpBitcast %v2double %v4int_0x3FF00000_0x00000000_0xC05FD666_0x66666666\n" +
"OpReturn\n" +
"OpFunctionEnd",
- 2, {1.0,-127.35})
+ 2, {1.0,-127.35}),
+ // Test case 1: OpVectorTimesMatrix Non-Zero Zero {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {1.0, 2.0, 3.0, 4.0} {0.0, 0.0, 0.0, 0.0}
+ InstructionFoldingCase<std::vector<double>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpVectorTimesMatrix %v4double %v4double_1_2_3_4 %mat4v4double_null\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {0.0,0.0,0.0,0.0}),
+ // Test case 2: OpVectorTimesMatrix Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
+ InstructionFoldingCase<std::vector<double>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpVectorTimesMatrix %v4double %v4double_null %mat4v4double_1_2_3_4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {0.0,0.0,0.0,0.0}),
+ // Test case 3: OpVectorTimesMatrix Non-Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {1.0, 2.0, 3.0, 4.0} {30.0, 30.0, 30.0, 30.0}
+ InstructionFoldingCase<std::vector<double>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpVectorTimesMatrix %v4double %v4double_1_2_3_4 %mat4v4double_1_2_3_4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {30.0,30.0,30.0,30.0}),
+ // Test case 4: OpMatrixTimesVector Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {0.0, 0.0, 0.0, 0.0}
+ InstructionFoldingCase<std::vector<double>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpMatrixTimesVector %v4double %mat4v4double_null %v4double_1_2_3_4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {0.0,0.0,0.0,0.0}),
+ // Test case 5: OpMatrixTimesVector Non-Zero Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
+ InstructionFoldingCase<std::vector<double>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpMatrixTimesVector %v4double %mat4v4double_1_2_3_4 %v4double_null\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {0.0,0.0,0.0,0.0}),
+ // Test case 6: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {10.0, 20.0, 30.0, 40.0}
+ InstructionFoldingCase<std::vector<double>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpMatrixTimesVector %v4double %mat4v4double_1_2_3_4 %v4double_1_2_3_4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {10.0,20.0,30.0,40.0})
));
using FloatVectorInstructionFoldingTest =
@@ -978,7 +1044,61 @@ INSTANTIATE_TEST_SUITE_P(TestCase, FloatVectorInstructionFoldingTest,
"%2 = OpBitcast %v2float %long_0xbf8000003f800000\n" +
"OpReturn\n" +
"OpFunctionEnd",
- 2, {1.0f,-1.0f})
+ 2, {1.0f,-1.0f}),
+ // Test case 3: OpVectorTimesMatrix Non-Zero Zero {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {1.0, 2.0, 3.0, 4.0} {0.0, 0.0, 0.0, 0.0}
+ InstructionFoldingCase<std::vector<float>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpVectorTimesMatrix %v4float %v4float_1_2_3_4 %mat4v4float_null\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {0.0f,0.0f,0.0f,0.0f}),
+ // Test case 4: OpVectorTimesMatrix Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
+ InstructionFoldingCase<std::vector<float>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpVectorTimesMatrix %v4float %v4float_null %mat4v4float_1_2_3_4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {0.0f,0.0f,0.0f,0.0f}),
+ // Test case 5: OpVectorTimesMatrix Non-Zero Non-Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {1.0, 2.0, 3.0, 4.0} {30.0, 30.0, 30.0, 30.0}
+ InstructionFoldingCase<std::vector<float>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpVectorTimesMatrix %v4float %v4float_1_2_3_4 %mat4v4float_1_2_3_4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {30.0f,30.0f,30.0f,30.0f}),
+ // Test case 6: OpMatrixTimesVector Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0}} {0.0, 0.0, 0.0, 0.0}
+ InstructionFoldingCase<std::vector<float>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpMatrixTimesVector %v4float %mat4v4float_null %v4float_1_2_3_4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {0.0f,0.0f,0.0f,0.0f}),
+ // Test case 7: OpMatrixTimesVector Non-Zero Zero {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {0.0, 0.0, 0.0, 0.0} {0.0, 0.0, 0.0, 0.0}
+ InstructionFoldingCase<std::vector<float>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpMatrixTimesVector %v4float %mat4v4float_1_2_3_4 %v4float_null\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {0.0f,0.0f,0.0f,0.0f}),
+ // Test case 8: OpMatrixTimesVector Non-Zero Non-Zero {1.0, 2.0, 3.0, 4.0} {{1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0}} {10.0, 20.0, 30.0, 40.0}
+ InstructionFoldingCase<std::vector<float>>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpMatrixTimesVector %v4float %mat4v4float_1_2_3_4 %v4float_1_2_3_4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, {10.0f,20.0f,30.0f,40.0f})
));
// clang-format on
using BooleanInstructionFoldingTest =
@@ -6862,7 +6982,7 @@ INSTANTIATE_TEST_SUITE_P(SelectFoldingTest, MatchingInstructionFoldingTest,
4, true)
));
-INSTANTIATE_TEST_SUITE_P(CompositeExtractMatchingTest, MatchingInstructionFoldingTest,
+INSTANTIATE_TEST_SUITE_P(CompositeExtractOrInsertMatchingTest, MatchingInstructionFoldingTest,
::testing::Values(
// Test case 0: Extracting from result of consecutive shuffles of differing
// size.
@@ -7002,7 +7122,145 @@ INSTANTIATE_TEST_SUITE_P(CompositeExtractMatchingTest, MatchingInstructionFoldin
"%4 = OpCompositeExtract %int %3 1\n" +
"OpReturn\n" +
"OpFunctionEnd",
- 4, true)
+ 4, true),
+ // Test case 8: Inserting every element of a vector turns into a composite construct.
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
+ "; CHECK-DAG: [[v4:%\\w+]] = OpTypeVector [[int]] 4\n" +
+ "; CHECK-DAG: [[int1:%\\w+]] = OpConstant [[int]] 1\n" +
+ "; CHECK-DAG: [[int2:%\\w+]] = OpConstant [[int]] 2\n" +
+ "; CHECK-DAG: [[int3:%\\w+]] = OpConstant [[int]] 3\n" +
+ "; CHECK: [[construct:%\\w+]] = OpCompositeConstruct [[v4]] %100 [[int1]] [[int2]] [[int3]]\n" +
+ "; CHECK: %5 = OpCopyObject [[v4]] [[construct]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpCompositeInsert %v4int %100 %v4int_undef 0\n" +
+ "%3 = OpCompositeInsert %v4int %int_1 %2 1\n" +
+ "%4 = OpCompositeInsert %v4int %int_2 %3 2\n" +
+ "%5 = OpCompositeInsert %v4int %int_3 %4 3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 5, true),
+ // Test case 9: Inserting every element of a vector turns into a composite construct in a different order.
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
+ "; CHECK-DAG: [[v4:%\\w+]] = OpTypeVector [[int]] 4\n" +
+ "; CHECK-DAG: [[int1:%\\w+]] = OpConstant [[int]] 1\n" +
+ "; CHECK-DAG: [[int2:%\\w+]] = OpConstant [[int]] 2\n" +
+ "; CHECK-DAG: [[int3:%\\w+]] = OpConstant [[int]] 3\n" +
+ "; CHECK: [[construct:%\\w+]] = OpCompositeConstruct [[v4]] %100 [[int1]] [[int2]] [[int3]]\n" +
+ "; CHECK: %5 = OpCopyObject [[v4]] [[construct]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpCompositeInsert %v4int %100 %v4int_undef 0\n" +
+ "%4 = OpCompositeInsert %v4int %int_2 %2 2\n" +
+ "%3 = OpCompositeInsert %v4int %int_1 %4 1\n" +
+ "%5 = OpCompositeInsert %v4int %int_3 %3 3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 5, true),
+ // Test case 10: Check multiple inserts to the same position are handled correctly.
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
+ "; CHECK-DAG: [[v4:%\\w+]] = OpTypeVector [[int]] 4\n" +
+ "; CHECK-DAG: [[int1:%\\w+]] = OpConstant [[int]] 1\n" +
+ "; CHECK-DAG: [[int2:%\\w+]] = OpConstant [[int]] 2\n" +
+ "; CHECK-DAG: [[int3:%\\w+]] = OpConstant [[int]] 3\n" +
+ "; CHECK: [[construct:%\\w+]] = OpCompositeConstruct [[v4]] %100 [[int1]] [[int2]] [[int3]]\n" +
+ "; CHECK: %6 = OpCopyObject [[v4]] [[construct]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpCompositeInsert %v4int %100 %v4int_undef 0\n" +
+ "%3 = OpCompositeInsert %v4int %int_2 %2 2\n" +
+ "%4 = OpCompositeInsert %v4int %int_4 %3 1\n" +
+ "%5 = OpCompositeInsert %v4int %int_1 %4 1\n" +
+ "%6 = OpCompositeInsert %v4int %int_3 %5 3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 6, true),
+ // Test case 11: The last indexes are 0 and 1, but they have different first indexes. This should not be folded.
+ InstructionFoldingCase<bool>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpCompositeInsert %m2x2int %100 %m2x2int_undef 0 0\n" +
+ "%3 = OpCompositeInsert %m2x2int %int_1 %2 1 1\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, false),
+ // Test case 12: Don't fold when there is a partial insertion.
+ InstructionFoldingCase<bool>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpCompositeInsert %m2x2int %v2int_1_0 %m2x2int_undef 0\n" +
+ "%3 = OpCompositeInsert %m2x2int %int_4 %2 0 0\n" +
+ "%4 = OpCompositeInsert %m2x2int %v2int_2_3 %3 1\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 4, false),
+ // Test case 13: Insert into a column of a matrix
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
+ "; CHECK-DAG: [[v2:%\\w+]] = OpTypeVector [[int]] 2\n" +
+ "; CHECK: [[m2x2:%\\w+]] = OpTypeMatrix [[v2]] 2\n" +
+ "; CHECK-DAG: [[m2x2_undef:%\\w+]] = OpUndef [[m2x2]]\n" +
+ "; CHECK-DAG: [[int1:%\\w+]] = OpConstant [[int]] 1\n" +
+// We keep this insert in the chain. DeadInsertElimPass should remove it.
+ "; CHECK: [[insert:%\\w+]] = OpCompositeInsert [[m2x2]] %100 [[m2x2_undef]] 0 0\n" +
+ "; CHECK: [[construct:%\\w+]] = OpCompositeConstruct [[v2]] %100 [[int1]]\n" +
+ "; CHECK: %3 = OpCompositeInsert [[m2x2]] [[construct]] [[insert]] 0\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpCompositeInsert %m2x2int %100 %m2x2int_undef 0 0\n" +
+ "%3 = OpCompositeInsert %m2x2int %int_1 %2 0 1\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, true),
+ // Test case 14: Insert all elements of the matrix.
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
+ "; CHECK-DAG: [[v2:%\\w+]] = OpTypeVector [[int]] 2\n" +
+ "; CHECK: [[m2x2:%\\w+]] = OpTypeMatrix [[v2]] 2\n" +
+ "; CHECK-DAG: [[m2x2_undef:%\\w+]] = OpUndef [[m2x2]]\n" +
+ "; CHECK-DAG: [[int1:%\\w+]] = OpConstant [[int]] 1\n" +
+ "; CHECK-DAG: [[int2:%\\w+]] = OpConstant [[int]] 2\n" +
+ "; CHECK-DAG: [[int3:%\\w+]] = OpConstant [[int]] 3\n" +
+ "; CHECK: [[c0:%\\w+]] = OpCompositeConstruct [[v2]] %100 [[int1]]\n" +
+ "; CHECK: [[c1:%\\w+]] = OpCompositeConstruct [[v2]] [[int2]] [[int3]]\n" +
+ "; CHECK: [[matrix:%\\w+]] = OpCompositeConstruct [[m2x2]] [[c0]] [[c1]]\n" +
+ "; CHECK: %5 = OpCopyObject [[m2x2]] [[matrix]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpCompositeConstruct %v2int %100 %int_1\n" +
+ "%3 = OpCompositeInsert %m2x2int %2 %m2x2int_undef 0\n" +
+ "%4 = OpCompositeInsert %m2x2int %int_2 %3 1 0\n" +
+ "%5 = OpCompositeInsert %m2x2int %int_3 %4 1 1\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 5, true),
+ // Test case 15: Replace construct with extract when reconstructing a member
+ // of another object.
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
+ "; CHECK: [[v2:%\\w+]] = OpTypeVector [[int]] 2\n" +
+ "; CHECK: [[m2x2:%\\w+]] = OpTypeMatrix [[v2]] 2\n" +
+ "; CHECK: [[m2x2_undef:%\\w+]] = OpUndef [[m2x2]]\n" +
+ "; CHECK: %5 = OpCompositeExtract [[v2]] [[m2x2_undef]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%3 = OpCompositeExtract %int %m2x2int_undef 1 0\n" +
+ "%4 = OpCompositeExtract %int %m2x2int_undef 1 1\n" +
+ "%5 = OpCompositeConstruct %v2int %3 %4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 5, true)
));
INSTANTIATE_TEST_SUITE_P(DotProductMatchingTest, MatchingInstructionFoldingTest,
@@ -7087,6 +7345,291 @@ INSTANTIATE_TEST_SUITE_P(DotProductMatchingTest, MatchingInstructionFoldingTest,
3, true)
));
+INSTANTIATE_TEST_SUITE_P(VectorShuffleMatchingTest, MatchingInstructionFoldingTest,
+::testing::Values(
+ // Test case 0: Using OpDot to extract last element.
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
+ "; CHECK: [[v2int:%\\w+]] = OpTypeVector [[int]] 2{{[[:space:]]}}\n" +
+ "; CHECK: [[null:%\\w+]] = OpConstantNull [[v2int]]\n" +
+ "; CHECK: OpVectorShuffle\n" +
+ "; CHECK: %3 = OpVectorShuffle [[v2int]] [[null]] {{%\\w+}} 4294967295 2\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%n = OpVariable %_ptr_int Function\n" +
+ "%load = OpLoad %int %n\n" +
+ "%2 = OpVectorShuffle %v2int %v2int_null %v2int_2_3 3 0xFFFFFFFF \n" +
+ "%3 = OpVectorShuffle %v2int %2 %v2int_2_3 1 2 \n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, true)
+ ));
+
+INSTANTIATE_TEST_SUITE_P(FmaGenerationMatchingTest, MatchingInstructionFoldingTest,
+::testing::Values(
+ // Test case 0: (x * y) + a = Fma(x, y, a)
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" +
+ "; CHECK: OpFunction\n" +
+ "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" +
+ "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" +
+ "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" +
+ "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[la]]\n" +
+ "; CHECK: OpStore {{%\\w+}} [[fma]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%x = OpVariable %_ptr_float Function\n" +
+ "%y = OpVariable %_ptr_float Function\n" +
+ "%a = OpVariable %_ptr_float Function\n" +
+ "%lx = OpLoad %float %x\n" +
+ "%ly = OpLoad %float %y\n" +
+ "%mul = OpFMul %float %lx %ly\n" +
+ "%la = OpLoad %float %a\n" +
+ "%3 = OpFAdd %float %mul %la\n" +
+ "OpStore %a %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, true),
+ // Test case 1: a + (x * y) = Fma(x, y, a)
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" +
+ "; CHECK: OpFunction\n" +
+ "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" +
+ "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" +
+ "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" +
+ "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[la]]\n" +
+ "; CHECK: OpStore {{%\\w+}} [[fma]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%x = OpVariable %_ptr_float Function\n" +
+ "%y = OpVariable %_ptr_float Function\n" +
+ "%a = OpVariable %_ptr_float Function\n" +
+ "%lx = OpLoad %float %x\n" +
+ "%ly = OpLoad %float %y\n" +
+ "%mul = OpFMul %float %lx %ly\n" +
+ "%la = OpLoad %float %a\n" +
+ "%3 = OpFAdd %float %la %mul\n" +
+ "OpStore %a %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, true),
+ // Test case 2: (x * y) + a = Fma(x, y, a) with vectors
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" +
+ "; CHECK: OpFunction\n" +
+ "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" +
+ "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" +
+ "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" +
+ "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[la]]\n" +
+ "; CHECK: OpStore {{%\\w+}} [[fma]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%x = OpVariable %_ptr_v4float Function\n" +
+ "%y = OpVariable %_ptr_v4float Function\n" +
+ "%a = OpVariable %_ptr_v4float Function\n" +
+ "%lx = OpLoad %v4float %x\n" +
+ "%ly = OpLoad %v4float %y\n" +
+ "%mul = OpFMul %v4float %lx %ly\n" +
+ "%la = OpLoad %v4float %a\n" +
+ "%3 = OpFAdd %v4float %mul %la\n" +
+ "OpStore %a %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, true),
+ // Test case 3: a + (x * y) = Fma(x, y, a) with vectors
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" +
+ "; CHECK: OpFunction\n" +
+ "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" +
+ "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" +
+ "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" +
+ "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[la]]\n" +
+ "; CHECK: OpStore {{%\\w+}} [[fma]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%x = OpVariable %_ptr_float Function\n" +
+ "%y = OpVariable %_ptr_float Function\n" +
+ "%a = OpVariable %_ptr_float Function\n" +
+ "%lx = OpLoad %float %x\n" +
+ "%ly = OpLoad %float %y\n" +
+ "%mul = OpFMul %float %lx %ly\n" +
+ "%la = OpLoad %float %a\n" +
+ "%3 = OpFAdd %float %la %mul\n" +
+ "OpStore %a %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, true),
+ // Test 4: that the OpExtInstImport instruction is generated if it is missing.
+ InstructionFoldingCase<bool>(
+ std::string() +
+ "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" +
+ "; CHECK: OpFunction\n" +
+ "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" +
+ "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" +
+ "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" +
+ "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[la]]\n" +
+ "; CHECK: OpStore {{%\\w+}} [[fma]]\n" +
+ "OpCapability Shader\n" +
+ "OpMemoryModel Logical GLSL450\n" +
+ "OpEntryPoint Fragment %main \"main\"\n" +
+ "OpExecutionMode %main OriginUpperLeft\n" +
+ "OpSource GLSL 140\n" +
+ "OpName %main \"main\"\n" +
+ "%void = OpTypeVoid\n" +
+ "%void_func = OpTypeFunction %void\n" +
+ "%bool = OpTypeBool\n" +
+ "%float = OpTypeFloat 32\n" +
+ "%_ptr_float = OpTypePointer Function %float\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%x = OpVariable %_ptr_float Function\n" +
+ "%y = OpVariable %_ptr_float Function\n" +
+ "%a = OpVariable %_ptr_float Function\n" +
+ "%lx = OpLoad %float %x\n" +
+ "%ly = OpLoad %float %y\n" +
+ "%mul = OpFMul %float %lx %ly\n" +
+ "%la = OpLoad %float %a\n" +
+ "%3 = OpFAdd %float %mul %la\n" +
+ "OpStore %a %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, true),
+ // Test 5: Don't fold if the multiple is marked no contract.
+ InstructionFoldingCase<bool>(
+ std::string() +
+ "OpCapability Shader\n" +
+ "OpMemoryModel Logical GLSL450\n" +
+ "OpEntryPoint Fragment %main \"main\"\n" +
+ "OpExecutionMode %main OriginUpperLeft\n" +
+ "OpSource GLSL 140\n" +
+ "OpName %main \"main\"\n" +
+ "OpDecorate %mul NoContraction\n" +
+ "%void = OpTypeVoid\n" +
+ "%void_func = OpTypeFunction %void\n" +
+ "%bool = OpTypeBool\n" +
+ "%float = OpTypeFloat 32\n" +
+ "%_ptr_float = OpTypePointer Function %float\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%x = OpVariable %_ptr_float Function\n" +
+ "%y = OpVariable %_ptr_float Function\n" +
+ "%a = OpVariable %_ptr_float Function\n" +
+ "%lx = OpLoad %float %x\n" +
+ "%ly = OpLoad %float %y\n" +
+ "%mul = OpFMul %float %lx %ly\n" +
+ "%la = OpLoad %float %a\n" +
+ "%3 = OpFAdd %float %mul %la\n" +
+ "OpStore %a %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, false),
+ // Test 6: Don't fold if the add is marked no contract.
+ InstructionFoldingCase<bool>(
+ std::string() +
+ "OpCapability Shader\n" +
+ "OpMemoryModel Logical GLSL450\n" +
+ "OpEntryPoint Fragment %main \"main\"\n" +
+ "OpExecutionMode %main OriginUpperLeft\n" +
+ "OpSource GLSL 140\n" +
+ "OpName %main \"main\"\n" +
+ "OpDecorate %3 NoContraction\n" +
+ "%void = OpTypeVoid\n" +
+ "%void_func = OpTypeFunction %void\n" +
+ "%bool = OpTypeBool\n" +
+ "%float = OpTypeFloat 32\n" +
+ "%_ptr_float = OpTypePointer Function %float\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%x = OpVariable %_ptr_float Function\n" +
+ "%y = OpVariable %_ptr_float Function\n" +
+ "%a = OpVariable %_ptr_float Function\n" +
+ "%lx = OpLoad %float %x\n" +
+ "%ly = OpLoad %float %y\n" +
+ "%mul = OpFMul %float %lx %ly\n" +
+ "%la = OpLoad %float %a\n" +
+ "%3 = OpFAdd %float %mul %la\n" +
+ "OpStore %a %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, false),
+ // Test case 7: (x * y) - a = Fma(x, y, -a)
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" +
+ "; CHECK: OpFunction\n" +
+ "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" +
+ "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" +
+ "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" +
+ "; CHECK: [[na:%\\w+]] = OpFNegate {{%\\w+}} [[la]]\n" +
+ "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[lx]] [[ly]] [[na]]\n" +
+ "; CHECK: OpStore {{%\\w+}} [[fma]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%x = OpVariable %_ptr_float Function\n" +
+ "%y = OpVariable %_ptr_float Function\n" +
+ "%a = OpVariable %_ptr_float Function\n" +
+ "%lx = OpLoad %float %x\n" +
+ "%ly = OpLoad %float %y\n" +
+ "%mul = OpFMul %float %lx %ly\n" +
+ "%la = OpLoad %float %a\n" +
+ "%3 = OpFSub %float %mul %la\n" +
+ "OpStore %a %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, true),
+ // Test case 8: a - (x * y) = Fma(-x, y, a)
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[ext:%\\w+]] = OpExtInstImport \"GLSL.std.450\"\n" +
+ "; CHECK: OpFunction\n" +
+ "; CHECK: [[x:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[y:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[a:%\\w+]] = OpVariable {{%\\w+}} Function\n" +
+ "; CHECK: [[lx:%\\w+]] = OpLoad {{%\\w+}} [[x]]\n" +
+ "; CHECK: [[ly:%\\w+]] = OpLoad {{%\\w+}} [[y]]\n" +
+ "; CHECK: [[la:%\\w+]] = OpLoad {{%\\w+}} [[a]]\n" +
+ "; CHECK: [[nx:%\\w+]] = OpFNegate {{%\\w+}} [[lx]]\n" +
+ "; CHECK: [[fma:%\\w+]] = OpExtInst {{%\\w+}} [[ext]] Fma [[nx]] [[ly]] [[la]]\n" +
+ "; CHECK: OpStore {{%\\w+}} [[fma]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%x = OpVariable %_ptr_float Function\n" +
+ "%y = OpVariable %_ptr_float Function\n" +
+ "%a = OpVariable %_ptr_float Function\n" +
+ "%lx = OpLoad %float %x\n" +
+ "%ly = OpLoad %float %y\n" +
+ "%mul = OpFMul %float %lx %ly\n" +
+ "%la = OpLoad %float %a\n" +
+ "%3 = OpFSub %float %la %mul\n" +
+ "OpStore %a %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 3, true)
+));
+
using MatchingInstructionWithNoResultFoldingTest =
::testing::TestWithParam<InstructionFoldingCase<bool>>;
@@ -7420,6 +7963,27 @@ INSTANTIATE_TEST_SUITE_P(VectorShuffleMatchingTest, MatchingInstructionWithNoRes
"%9 = OpVectorShuffle %v4double %7 %8 2 0 1 4294967295\n" +
"OpReturn\n" +
"OpFunctionEnd",
+ 9, true),
+ // Test case 14: Shuffle with undef literal and change size of first input vector.
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" +
+ "; CHECK: [[v4double:%\\w+]] = OpTypeVector [[double]] 2\n" +
+ "; CHECK: OpVectorShuffle\n" +
+ "; CHECK: OpVectorShuffle {{%\\w+}} %5 %7 0 1 4 4294967295\n" +
+ "; CHECK: OpReturn\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpVariable %_ptr_v4double Function\n" +
+ "%3 = OpVariable %_ptr_v4double Function\n" +
+ "%4 = OpVariable %_ptr_v4double Function\n" +
+ "%5 = OpLoad %v4double %2\n" +
+ "%6 = OpLoad %v4double %3\n" +
+ "%7 = OpLoad %v4double %4\n" +
+ "%8 = OpVectorShuffle %v2double %5 %5 0 1\n" +
+ "%9 = OpVectorShuffle %v4double %8 %7 0 1 2 4294967295\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
9, true)
));
diff --git a/test/opt/freeze_spec_const_test.cpp b/test/opt/freeze_spec_const_test.cpp
index e5999cec..ad0fc32e 100644
--- a/test/opt/freeze_spec_const_test.cpp
+++ b/test/opt/freeze_spec_const_test.cpp
@@ -128,6 +128,51 @@ TEST_F(FreezeSpecConstantValueRemoveDecorationTest,
/* skip_nop = */ true);
}
+TEST_F(FreezeSpecConstantValueRemoveDecorationTest,
+ RemoveDecorationForLocalSizeIdWithSpecId) {
+ std::vector<const char*> text = {
+ // clang-format off
+ "OpCapability Shader",
+ "%1 = OpExtInstImport \"GLSL.std.450\"",
+ "OpMemoryModel Logical GLSL450",
+ "OpEntryPoint GLCompute %2 \"main\"",
+ "OpExecutionModeId %2 LocalSizeId %uint_32 %uint_1 %uint_1_0",
+ "OpSource GLSL 450",
+ "OpDecorate %3 SpecId 18",
+ "OpDecorate %5 SpecId 19",
+ "%void = OpTypeVoid",
+ "%9 = OpTypeFunction %void",
+ "%uint = OpTypeInt 32 0",
+ "%uint_32 = OpSpecConstant %uint 32",
+ "%uint_1 = OpConstant %uint 1",
+ "%uint_1_0 = OpSpecConstant %uint 1",
+ "%2 = OpFunction %void None %9",
+ "%11 = OpLabel",
+ "OpReturn",
+ "OpFunctionEnd",
+ // clang-format on
+ };
+ std::string expected_disassembly = SelectiveJoin(text, [](const char* line) {
+ return std::string(line).find("SpecId") != std::string::npos;
+ });
+ std::vector<std::pair<const char*, const char*>> replacement_pairs = {
+ {"%uint_32 = OpSpecConstant %uint 32", "%uint_32 = OpConstant %uint 32"},
+ {"%uint_1_0 = OpSpecConstant %uint 1", "%uint_1_0 = OpConstant %uint 1"},
+ };
+ for (auto& p : replacement_pairs) {
+ EXPECT_TRUE(FindAndReplace(&expected_disassembly, p.first, p.second))
+ << "text:\n"
+ << expected_disassembly << "\n"
+ << "find_str:\n"
+ << p.first << "\n"
+ << "replace_str:\n"
+ << p.second << "\n";
+ }
+ SinglePassRunAndCheck<FreezeSpecConstantValuePass>(JoinAllInsts(text),
+ expected_disassembly,
+ /* skip_nop = */ true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/function_test.cpp b/test/opt/function_test.cpp
index af25bacc..34a03871 100644
--- a/test/opt/function_test.cpp
+++ b/test/opt/function_test.cpp
@@ -296,6 +296,65 @@ OpFunctionEnd
EXPECT_EQ(1, non_semantic_ids.count(8));
}
+TEST(FunctionTest, ReorderBlocksinStructuredOrder) {
+ // The spir-v has the basic block in a random order. We want to reorder them
+ // in structured order.
+ const std::string text = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %100 "PSMain"
+ OpExecutionMode %PSMain OriginUpperLeft
+ OpSource HLSL 600
+ %int = OpTypeInt 32 1
+ %void = OpTypeVoid
+ %19 = OpTypeFunction %void
+ %bool = OpTypeBool
+%undef_bool = OpUndef %bool
+%undef_int = OpUndef %int
+ %100 = OpFunction %void None %19
+ %11 = OpLabel
+ OpSelectionMerge %10 None
+ OpSwitch %undef_int %3 0 %2 10 %1
+ %2 = OpLabel
+ OpReturn
+ %7 = OpLabel
+ OpBranch %8
+ %3 = OpLabel
+ OpBranch %4
+ %10 = OpLabel
+ OpReturn
+ %9 = OpLabel
+ OpBranch %10
+ %8 = OpLabel
+ OpBranch %4
+ %4 = OpLabel
+ OpLoopMerge %9 %8 None
+ OpBranchConditional %undef_bool %5 %9
+ %1 = OpLabel
+ OpReturn
+ %6 = OpLabel
+ OpBranch %7
+ %5 = OpLabel
+ OpSelectionMerge %7 None
+ OpBranchConditional %undef_bool %6 %7
+ OpFunctionEnd
+)";
+
+ std::unique_ptr<IRContext> ctx =
+ spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ASSERT_TRUE(ctx);
+ auto* func = spvtest::GetFunction(ctx->module(), 100);
+ ASSERT_TRUE(func);
+ func->ReorderBasicBlocksInStructuredOrder();
+
+ auto first_block = func->begin();
+ auto bb = first_block;
+ for (++bb; bb != func->end(); ++bb) {
+ EXPECT_EQ(bb->id(), (bb - first_block));
+ }
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/if_conversion_test.cpp b/test/opt/if_conversion_test.cpp
index dee15c31..dc7f8316 100644
--- a/test/opt/if_conversion_test.cpp
+++ b/test/opt/if_conversion_test.cpp
@@ -328,6 +328,40 @@ OpFunctionEnd
SinglePassRunAndCheck<IfConversion>(text, text, true, true);
}
+TEST_F(IfConversionTest, DontFlatten) {
+ const std::string text = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %1 "func" %2
+%void = OpTypeVoid
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%v2uint = OpTypeVector %uint 2
+%10 = OpConstantComposite %v2uint %uint_0 %uint_1
+%11 = OpConstantComposite %v2uint %uint_1 %uint_0
+%_ptr_Output_v2uint = OpTypePointer Output %v2uint
+%2 = OpVariable %_ptr_Output_v2uint Output
+%13 = OpTypeFunction %void
+%1 = OpFunction %void None %13
+%14 = OpLabel
+OpSelectionMerge %15 DontFlatten
+OpBranchConditional %true %16 %17
+%16 = OpLabel
+OpBranch %15
+%17 = OpLabel
+OpBranch %15
+%15 = OpLabel
+%18 = OpPhi %v2uint %10 %16 %11 %17
+OpStore %2 %18
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<IfConversion>(text, text, true, true);
+}
+
TEST_F(IfConversionTest, LoopUntouched) {
const std::string text = R"(OpCapability Shader
OpMemoryModel Logical GLSL450
@@ -557,6 +591,33 @@ OpFunctionEnd
SinglePassRunAndMatch<IfConversion>(text, true);
}
+TEST_F(IfConversionTest, MultipleEdgesFromSameBlock) {
+ // If a block has two out going edges that go to the same block, then there
+ // can be an OpPhi instruction with fewer entries than the number of incoming
+ // edges. This must be handled.
+ const std::string text = R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main"
+OpExecutionMode %2 OriginUpperLeft
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%true_0 = OpConstantTrue %bool
+%2 = OpFunction %void None %4
+%8 = OpLabel
+OpSelectionMerge %9 None
+OpBranchConditional %true_0 %9 %9
+%9 = OpLabel
+%10 = OpPhi %bool %true %8
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<IfConversion>(text, text, true, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp
index cefd8e54..7854965e 100644
--- a/test/opt/inline_test.cpp
+++ b/test/opt/inline_test.cpp
@@ -1,5 +1,5 @@
-// Copyright (c) 2017 Valve Corporation
-// Copyright (c) 2017 LunarG Inc.
+// Copyright (c) 2017-2022 Valve Corporation
+// Copyright (c) 2017-2022 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -1533,9 +1533,11 @@ OpFunctionEnd
%9 = OpLabel
OpBranch %10
%10 = OpLabel
-OpLoopMerge %12 %10 None
+OpLoopMerge %12 %15 None
OpBranch %14
%14 = OpLabel
+OpBranch %15
+%15 = OpLabel
OpBranchConditional %true %10 %12
%12 = OpLabel
OpReturn
@@ -1694,7 +1696,7 @@ OpFunctionEnd
OpBranch %13
%13 = OpLabel
%14 = OpCopyObject %bool %false
-OpLoopMerge %16 %13 None
+OpLoopMerge %16 %22 None
OpBranch %17
%17 = OpLabel
%19 = OpCopyObject %bool %true
@@ -1702,6 +1704,8 @@ OpSelectionMerge %20 None
OpBranchConditional %true %20 %20
%20 = OpLabel
%21 = OpPhi %bool %19 %17
+OpBranch %22
+%22 = OpLabel
OpBranchConditional %true %13 %16
%16 = OpLabel
OpReturn
@@ -4333,6 +4337,91 @@ OpFunctionEnd
SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
}
+TEST_F(InlineTest, CreateDebugInlinedAtFromDebugLine) {
+ const std::string text = R"(OpCapability Shader
+; CHECK: OpExtInst %void %1 DebugInlinedAt %uint_6_0
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+%3 = OpString "debuginlinedat.frag"
+%8 = OpString "int"
+%15 = OpString "int function1() {
+ return 1;
+}
+
+void main() {
+ function1();
+}
+"
+%20 = OpString "function1"
+%21 = OpString ""
+%26 = OpString "main"
+OpName %main "main"
+OpName %src_main "src.main"
+OpName %bb_entry "bb.entry"
+OpName %function1 "function1"
+OpName %bb_entry_0 "bb.entry"
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%void = OpTypeVoid
+%uint_4 = OpConstant %uint 4
+%uint_0 = OpConstant %uint 0
+%uint_3 = OpConstant %uint 3
+%uint_1 = OpConstant %uint 1
+%uint_5 = OpConstant %uint 5
+%uint_17 = OpConstant %uint 17
+%uint_13 = OpConstant %uint 13
+%30 = OpTypeFunction %void
+%uint_7 = OpConstant %uint 7
+%uint_6 = OpConstant %uint 6
+%uint_2 = OpConstant %uint 2
+%uint_12 = OpConstant %uint 12
+%48 = OpTypeFunction %int
+%uint_9 = OpConstant %uint 9
+%10 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 %uint_4 %uint_0
+%13 = OpExtInst %void %1 DebugTypeFunction %uint_3 %10
+%16 = OpExtInst %void %1 DebugSource %3 %15
+%17 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %16 %uint_5
+%22 = OpExtInst %void %1 DebugFunction %20 %13 %16 %uint_1 %uint_1 %17 %21 %uint_3 %uint_1
+%23 = OpExtInst %void %1 DebugLexicalBlock %16 %uint_1 %uint_17 %22
+%25 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void
+%27 = OpExtInst %void %1 DebugFunction %26 %25 %16 %uint_5 %uint_1 %17 %21 %uint_3 %uint_5
+%28 = OpExtInst %void %1 DebugLexicalBlock %16 %uint_5 %uint_13 %27
+%main = OpFunction %void None %30
+%31 = OpLabel
+%32 = OpFunctionCall %void %src_main
+%34 = OpExtInst %void %1 DebugLine %16 %uint_7 %uint_7 %uint_1 %uint_1
+OpReturn
+OpFunctionEnd
+%src_main = OpFunction %void None %30
+%bb_entry = OpLabel
+%37 = OpExtInst %void %1 DebugScope %27
+%38 = OpExtInst %void %1 DebugFunctionDefinition %27 %src_main
+%39 = OpExtInst %void %1 DebugScope %28
+%40 = OpExtInst %void %1 DebugLine %16 %uint_6 %uint_6 %uint_2 %uint_12
+%44 = OpFunctionCall %int %function1
+%46 = OpExtInst %void %1 DebugScope %27
+%47 = OpExtInst %void %1 DebugLine %16 %uint_7 %uint_7 %uint_1 %uint_1
+OpReturn
+OpFunctionEnd
+%function1 = OpFunction %int None %48
+%bb_entry_0 = OpLabel
+%50 = OpExtInst %void %1 DebugScope %22
+%51 = OpExtInst %void %1 DebugFunctionDefinition %22 %function1
+%52 = OpExtInst %void %1 DebugScope %23
+%53 = OpExtInst %void %1 DebugLine %16 %uint_2 %uint_2 %uint_2 %uint_9
+OpReturnValue %int_1
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_2);
+ SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Empty modules
diff --git a/test/opt/inst_bindless_check_test.cpp b/test/opt/inst_bindless_check_test.cpp
index 4c271dee..90f40bc6 100644
--- a/test/opt/inst_bindless_check_test.cpp
+++ b/test/opt/inst_bindless_check_test.cpp
@@ -1,5 +1,5 @@
-// Copyright (c) 2017 Valve Corporation
-// Copyright (c) 2017 LunarG Inc.
+// Copyright (c) 2017-2022 Valve Corporation
+// Copyright (c) 2017-2022 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -28,6 +28,297 @@ namespace {
using InstBindlessTest = PassTest<::testing::Test>;
+static const std::string kOutputDecorations = R"(
+; CHECK: OpDecorate [[output_buffer_type:%inst_bindless_OutputBuffer]] Block
+; CHECK: OpMemberDecorate [[output_buffer_type]] 0 Offset 0
+; CHECK: OpMemberDecorate [[output_buffer_type]] 1 Offset 4
+; CHECK: OpDecorate [[output_buffer_var:%\w+]] DescriptorSet 7
+; CHECK: OpDecorate [[output_buffer_var]] Binding 0
+)";
+
+static const std::string kOutputGlobals = R"(
+; CHECK: [[output_buffer_type]] = OpTypeStruct %uint %_runtimearr_uint
+; CHECK: [[output_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[output_buffer_type]]
+; CHECK: [[output_buffer_var]] = OpVariable [[output_ptr_type]] StorageBuffer
+)";
+
+static const std::string kStreamWrite4Begin = R"(
+; CHECK: %inst_bindless_stream_write_4 = OpFunction %void None {{%\w+}}
+; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_0
+; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_10
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10
+; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 1
+; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}}
+; CHECK: OpSelectionMerge {{%\w+}} None
+; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} %uint_10
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} %uint_23
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[param_1]]
+)";
+
+static const std::string kStreamWrite4End = R"(
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[param_2]]
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[param_3]]
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[param_4]]
+; CHECK: OpBranch {{%\w+}}
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: OpReturn
+; CHECK: OpFunctionEnd
+)";
+
+// clang-format off
+static const std::string kStreamWrite4Frag = kStreamWrite4Begin + R"(
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} %uint_4
+; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+)" + kStreamWrite4End;
+
+static const std::string kStreamWrite4Tese = kStreamWrite4Begin + R"(
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} %uint_2
+; CHECK: {{%\w+}} = OpLoad %uint %gl_PrimitiveID
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpLoad %v3float %gl_TessCoord
+; CHECK: {{%\w+}} = OpBitcast %v3uint {{%\w+}}
+; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+)" + kStreamWrite4End;
+
+static const std::string kStreamWrite4Vert = kStreamWrite4Begin + R"(
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} %uint_0
+; CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+)" + kStreamWrite4End;
+
+static const std::string kStreamWrite4Compute = kStreamWrite4Begin + R"(
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} %uint_5
+; CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
+; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+)" + kStreamWrite4End;
+
+static const std::string kStreamWrite4Ray = kStreamWrite4Begin + R"(
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpLoad %v3uint {{%\w+}}
+; CHECK: {{%\w+}} = OpCompositeExtract %uint %90 0
+; CHECK: {{%\w+}} = OpCompositeExtract %uint %90 1
+; CHECK: {{%\w+}} = OpCompositeExtract %uint %90 2
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+)" + kStreamWrite4End;
+// clang-format on
+
+static const std::string kStreamWrite5Begin = R"(
+; CHECK: %inst_bindless_stream_write_5 = OpFunction %void None {{%\w+}}
+; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[param_5:%\w+]] = OpFunctionParameter %uint
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_0
+; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_11
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_11
+; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 1
+; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}}
+; CHECK: OpSelectionMerge {{%\w+}} None
+; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} %uint_11
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} %uint_23
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[param_1]]
+)";
+
+static const std::string kStreamWrite5End = R"(
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[param_2]]
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[param_3]]
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[param_4]]
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[param_5]]
+; CHECK: OpBranch {{%\w+}}
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: OpReturn
+; CHECK: OpFunctionEnd
+)";
+
+// clang-format off
+static const std::string kStreamWrite5Frag = kStreamWrite5Begin + R"(
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} %uint_4
+; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+)" + kStreamWrite4End;
+
+static const std::string kStreamWrite5Vert = kStreamWrite5Begin + R"(
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} %uint_0
+; CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+)" + kStreamWrite5End;
+// clang-format on
+
+static const std::string kInputDecorations = R"(
+; CHECK: OpDecorate [[input_buffer_type:%inst_bindless_InputBuffer]] Block
+; CHECK: OpMemberDecorate [[input_buffer_type]] 0 Offset 0
+; CHECK: OpDecorate [[input_buffer_var:%\w+]] DescriptorSet 7
+; CHECK: OpDecorate [[input_buffer_var]] Binding 1
+)";
+
+static const std::string kInputGlobals = R"(
+; CHECK: [[input_buffer_type]] = OpTypeStruct %_runtimearr_uint
+; CHECK: [[input_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[input_buffer_type]]
+; CHECK: [[input_buffer_var]] = OpVariable [[input_ptr_type]] StorageBuffer
+)";
+
+static const std::string kDirectRead2 = R"(
+; CHECK: %inst_bindless_direct_read_2 = OpFunction %uint None {{%\w+}}
+; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 [[param_1]]
+; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_2]]
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
+; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
+; CHECK: OpReturnValue {{%\w+}}
+; CHECK: OpFunctionEnd
+)";
+
+static const std::string kDirectRead3 = R"(
+ ;CHECK: %inst_bindless_direct_read_3 = OpFunction %uint None {{%\w+}}
+; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint
+ ;CHECK: {{%\w+}} = OpLabel
+ ;CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 [[param_1]]
+ ;CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
+ ;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_2]]
+ ;CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
+ ;CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
+ ;CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_3]]
+ ;CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
+ ;CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
+ ;CHECK: OpReturnValue {{%\w+}}
+ ;CHECK: OpFunctionEnd
+)";
+
+static const std::string kDirectRead4 = R"(
+; CHECK: %inst_bindless_direct_read_4 = OpFunction %uint None {{%\w+}}
+; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 [[param_1]]
+; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_2]]
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
+; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_3]]
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
+; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} [[param_4]]
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[input_buffer_var]] %uint_0 {{%\w+}}
+; CHECK: {{%\w+}} = OpLoad %uint {{%\w+}}
+; CHECK: OpReturnValue {{%\w+}}
+; CHECK: OpFunctionEnd
+)";
+
TEST_F(InstBindlessTest, NoInstrumentConstIndexInbounds) {
// Texture2D g_tColor[128];
//
@@ -215,27 +506,20 @@ TEST_F(InstBindlessTest, Simple) {
// return ps_output;
// }
- const std::string entry_before =
- R"(OpCapability Shader
+ const std::string entry = R"(
+OpCapability Shader
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
+; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
OpExecutionMode %MainPs OriginUpperLeft
OpSource HLSL 500
)";
- const std::string entry_after =
- R"(OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-)";
-
- const std::string names_annots =
- R"(OpName %MainPs "MainPs"
+ // clang-format off
+ const std::string names_annots = R"(
+OpName %MainPs "MainPs"
OpName %g_tColor "g_tColor"
OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
@@ -250,20 +534,13 @@ OpDecorate %PerViewConstantBuffer_t Block
OpDecorate %g_sAniso DescriptorSet 0
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
)";
- const std::string new_annots =
- R"(OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_55 Block
-OpMemberDecorate %_struct_55 0 Offset 0
-OpMemberDecorate %_struct_55 1 Offset 4
-OpDecorate %57 DescriptorSet 7
-OpDecorate %57 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-)";
-
- const std::string consts_types_vars =
- R"(%void = OpTypeVoid
+ const std::string consts_types_vars = R"(
+%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
@@ -289,36 +566,32 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord
%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
+; CHECK: %uint_0 = OpConstant %uint 0
+; CHECK: %bool = OpTypeBool
+; CHECK: %48 = OpTypeFunction %void %uint %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kOutputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_56 = OpConstant %uint 56
+; CHECK: %103 = OpConstantNull %v4float
)";
+ // clang-format on
- const std::string new_consts_types_vars =
- R"(%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%48 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_55 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55
-%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_1 = OpConstant %uint 1
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_56 = OpConstant %uint 56
-%103 = OpConstantNull %v4float
-)";
-
- const std::string func_pt1 =
- R"(%MainPs = OpFunction %void None %10
+ const std::string main_func = R"(
+%MainPs = OpFunction %void None %10
%29 = OpLabel
%30 = OpLoad %v2float %i_vTextureCoords
%31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
@@ -327,93 +600,34 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord
%34 = OpLoad %16 %33
%35 = OpLoad %24 %g_sAniso
%36 = OpSampledImage %26 %34 %35
-)";
-
- const std::string func_pt2_before =
- R"(%37 = OpImageSampleImplicitLod %v4float %36 %30
+%37 = OpImageSampleImplicitLod %v4float %36 %30
OpStore %_entryPointOutput_vColor %37
+; CHECK-NOT: %37 = OpImageSampleImplicitLod %v4float %36 %30
+; CHECK-NOT: OpStore %_entryPointOutput_vColor %37
+; CHECK: %40 = OpULessThan %bool %32 %uint_128
+; CHECK: OpSelectionMerge %41 None
+; CHECK: OpBranchConditional %40 %42 %43
+; CHECK: %42 = OpLabel
+; CHECK: %44 = OpLoad %16 %33
+; CHECK: %45 = OpSampledImage %26 %44 %35
+; CHECK: %46 = OpImageSampleImplicitLod %v4float %45 %30
+; CHECK: OpBranch %41
+; CHECK: %43 = OpLabel
+; CHECK: %102 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_56 %uint_0 %32 %uint_128
+; CHECK: OpBranch %41
+; CHECK: %41 = OpLabel
+; CHECK: %104 = OpPhi %v4float %46 %42 %103 %43
+; CHECK: OpStore %_entryPointOutput_vColor %104
OpReturn
OpFunctionEnd
)";
- const std::string func_pt2_after =
- R"(%40 = OpULessThan %bool %32 %uint_128
-OpSelectionMerge %41 None
-OpBranchConditional %40 %42 %43
-%42 = OpLabel
-%44 = OpLoad %16 %33
-%45 = OpSampledImage %26 %44 %35
-%46 = OpImageSampleImplicitLod %v4float %45 %30
-OpBranch %41
-%43 = OpLabel
-%102 = OpFunctionCall %void %47 %uint_56 %uint_0 %32 %uint_128
-OpBranch %41
-%41 = OpLabel
-%104 = OpPhi %v4float %46 %42 %103 %43
-OpStore %_entryPointOutput_vColor %104
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string output_func =
- R"(%47 = OpFunction %void None %48
-%49 = OpFunctionParameter %uint
-%50 = OpFunctionParameter %uint
-%51 = OpFunctionParameter %uint
-%52 = OpFunctionParameter %uint
-%53 = OpLabel
-%59 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0
-%62 = OpAtomicIAdd %uint %59 %uint_4 %uint_0 %uint_10
-%63 = OpIAdd %uint %62 %uint_10
-%64 = OpArrayLength %uint %57 1
-%65 = OpULessThanEqual %bool %63 %64
-OpSelectionMerge %66 None
-OpBranchConditional %65 %67 %66
-%67 = OpLabel
-%68 = OpIAdd %uint %62 %uint_0
-%70 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %68
-OpStore %70 %uint_10
-%72 = OpIAdd %uint %62 %uint_1
-%73 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %72
-OpStore %73 %uint_23
-%75 = OpIAdd %uint %62 %uint_2
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75
-OpStore %76 %49
-%78 = OpIAdd %uint %62 %uint_3
-%79 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %78
-OpStore %79 %uint_4
-%82 = OpLoad %v4float %gl_FragCoord
-%84 = OpBitcast %v4uint %82
-%85 = OpCompositeExtract %uint %84 0
-%86 = OpIAdd %uint %62 %uint_4
-%87 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %86
-OpStore %87 %85
-%88 = OpCompositeExtract %uint %84 1
-%90 = OpIAdd %uint %62 %uint_5
-%91 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %90
-OpStore %91 %88
-%93 = OpIAdd %uint %62 %uint_7
-%94 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %93
-OpStore %94 %50
-%96 = OpIAdd %uint %62 %uint_8
-%97 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %96
-OpStore %97 %51
-%99 = OpIAdd %uint %62 %uint_9
-%100 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %99
-OpStore %100 %52
-OpBranch %66
-%66 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
+ const std::string output_func = kStreamWrite4Frag;
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass, uint32_t, uint32_t, bool, bool>(
- entry_before + names_annots + consts_types_vars + func_pt1 +
- func_pt2_before,
- entry_after + names_annots + new_annots + consts_types_vars +
- new_consts_types_vars + func_pt1 + func_pt2_after + output_func,
- true, true, 7u, 23u, false, false, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass, uint32_t, uint32_t, bool, bool>(
+ entry + names_annots + consts_types_vars + main_func + output_func, true,
+ 7u, 23u, false, false, false, false, false);
}
TEST_F(InstBindlessTest, InstrumentMultipleInstructions) {
@@ -447,11 +661,14 @@ TEST_F(InstBindlessTest, InstrumentMultipleInstructions) {
// return ps_output;
// }
- const std::string defs_before =
- R"(OpCapability Shader
+ // clang-format off
+ const std::string defs = R"(
+OpCapability Shader
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
+; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
OpExecutionMode %MainPs OriginUpperLeft
OpSource HLSL 500
OpName %MainPs "MainPs"
@@ -470,6 +687,9 @@ OpDecorate %PerViewConstantBuffer_t Block
OpDecorate %g_sAniso DescriptorSet 0
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -497,93 +717,32 @@ OpDecorate %_entryPointOutput_vColor Location 0
%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
+; CHECK: %uint_0 = OpConstant %uint 0
+; CHECK: %bool = OpTypeBool
+; CHECK: %56 = OpTypeFunction %void %uint %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kOutputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_58 = OpConstant %uint 58
+; CHECK: %111 = OpConstantNull %v4float
+; CHECK: %uint_64 = OpConstant %uint 64
)";
+ // clang-format on
- const std::string defs_after =
- R"(OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %g_sAniso "g_sAniso"
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 4
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_63 Block
-OpMemberDecorate %_struct_63 0 Offset 0
-OpMemberDecorate %_struct_63 1 Offset 4
-OpDecorate %65 DescriptorSet 7
-OpDecorate %65 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%int_1 = OpConstant %int 1
-%17 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%_arr_17_uint_128 = OpTypeArray %17 %uint_128
-%_ptr_UniformConstant__arr_17_uint_128 = OpTypePointer UniformConstant %_arr_17_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_17_uint_128 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17
-%25 = OpTypeSampler
-%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25
-%g_sAniso = OpVariable %_ptr_UniformConstant_25 UniformConstant
-%27 = OpTypeSampledImage %17
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%56 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_63 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63
-%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_1 = OpConstant %uint 1
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_58 = OpConstant %uint 58
-%111 = OpConstantNull %v4float
-%uint_64 = OpConstant %uint 64
-)";
-
- const std::string func_before =
+ const std::string main_func =
R"(%MainPs = OpFunction %void None %10
%30 = OpLabel
%31 = OpLoad %v2float %i_vTextureCoords
@@ -594,6 +753,20 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord
%36 = OpLoad %25 %g_sAniso
%37 = OpSampledImage %27 %35 %36
%38 = OpImageSampleImplicitLod %v4float %37 %31
+; CHECK-NOT: %38 = OpImageSampleImplicitLod %v4float %37 %31
+; CHECK: %48 = OpULessThan %bool %33 %uint_128
+; CHECK: OpSelectionMerge %49 None
+; CHECK: OpBranchConditional %48 %50 %51
+; CHECK: %50 = OpLabel
+; CHECK: %52 = OpLoad %17 %34
+; CHECK: %53 = OpSampledImage %27 %52 %36
+; CHECK: %54 = OpImageSampleImplicitLod %v4float %53 %31
+; CHECK: OpBranch %49
+; CHECK: %51 = OpLabel
+; CHECK: %110 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_58 %uint_0 %33 %uint_128
+; CHECK: OpBranch %49
+; CHECK: %49 = OpLabel
+; CHECK: %112 = OpPhi %v4float %54 %50 %111 %51
%39 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1
%40 = OpLoad %uint %39
%41 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %40
@@ -601,114 +774,33 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord
%43 = OpSampledImage %27 %42 %36
%44 = OpImageSampleImplicitLod %v4float %43 %31
%45 = OpFAdd %v4float %38 %44
+; CHECK-NOT: %44 = OpImageSampleImplicitLod %v4float %43 %31
+; CHECK-NOT: %45 = OpFAdd %v4float %38 %44
+; CHECK: %113 = OpULessThan %bool %40 %uint_128
+; CHECK: OpSelectionMerge %114 None
+; CHECK: OpBranchConditional %113 %115 %116
+; CHECK: %115 = OpLabel
+; CHECK: %117 = OpLoad %17 %41
+; CHECK: %118 = OpSampledImage %27 %117 %36
+; CHECK: %119 = OpImageSampleImplicitLod %v4float %118 %31
+; CHECK: OpBranch %114
+; CHECK: %116 = OpLabel
+; CHECK: %121 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_64 %uint_0 %40 %uint_128
+; CHECK: OpBranch %114
+; CHECK: %114 = OpLabel
+; CHECK: %122 = OpPhi %v4float %119 %115 %111 %116
+; CHECK: %45 = OpFAdd %v4float %112 %122
OpStore %_entryPointOutput_vColor %45
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%MainPs = OpFunction %void None %10
-%30 = OpLabel
-%31 = OpLoad %v2float %i_vTextureCoords
-%32 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%33 = OpLoad %uint %32
-%34 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %33
-%35 = OpLoad %17 %34
-%36 = OpLoad %25 %g_sAniso
-%37 = OpSampledImage %27 %35 %36
-%48 = OpULessThan %bool %33 %uint_128
-OpSelectionMerge %49 None
-OpBranchConditional %48 %50 %51
-%50 = OpLabel
-%52 = OpLoad %17 %34
-%53 = OpSampledImage %27 %52 %36
-%54 = OpImageSampleImplicitLod %v4float %53 %31
-OpBranch %49
-%51 = OpLabel
-%110 = OpFunctionCall %void %55 %uint_58 %uint_0 %33 %uint_128
-OpBranch %49
-%49 = OpLabel
-%112 = OpPhi %v4float %54 %50 %111 %51
-%39 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1
-%40 = OpLoad %uint %39
-%41 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %40
-%42 = OpLoad %17 %41
-%43 = OpSampledImage %27 %42 %36
-%113 = OpULessThan %bool %40 %uint_128
-OpSelectionMerge %114 None
-OpBranchConditional %113 %115 %116
-%115 = OpLabel
-%117 = OpLoad %17 %41
-%118 = OpSampledImage %27 %117 %36
-%119 = OpImageSampleImplicitLod %v4float %118 %31
-OpBranch %114
-%116 = OpLabel
-%121 = OpFunctionCall %void %55 %uint_64 %uint_0 %40 %uint_128
-OpBranch %114
-%114 = OpLabel
-%122 = OpPhi %v4float %119 %115 %111 %116
-%45 = OpFAdd %v4float %112 %122
-OpStore %_entryPointOutput_vColor %45
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string output_func =
- R"(%55 = OpFunction %void None %56
-%57 = OpFunctionParameter %uint
-%58 = OpFunctionParameter %uint
-%59 = OpFunctionParameter %uint
-%60 = OpFunctionParameter %uint
-%61 = OpLabel
-%67 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0
-%70 = OpAtomicIAdd %uint %67 %uint_4 %uint_0 %uint_10
-%71 = OpIAdd %uint %70 %uint_10
-%72 = OpArrayLength %uint %65 1
-%73 = OpULessThanEqual %bool %71 %72
-OpSelectionMerge %74 None
-OpBranchConditional %73 %75 %74
-%75 = OpLabel
-%76 = OpIAdd %uint %70 %uint_0
-%78 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %76
-OpStore %78 %uint_10
-%80 = OpIAdd %uint %70 %uint_1
-%81 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %80
-OpStore %81 %uint_23
-%83 = OpIAdd %uint %70 %uint_2
-%84 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %83
-OpStore %84 %57
-%86 = OpIAdd %uint %70 %uint_3
-%87 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %86
-OpStore %87 %uint_4
-%90 = OpLoad %v4float %gl_FragCoord
-%92 = OpBitcast %v4uint %90
-%93 = OpCompositeExtract %uint %92 0
-%94 = OpIAdd %uint %70 %uint_4
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94
-OpStore %95 %93
-%96 = OpCompositeExtract %uint %92 1
-%98 = OpIAdd %uint %70 %uint_5
-%99 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %98
-OpStore %99 %96
-%101 = OpIAdd %uint %70 %uint_7
-%102 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %101
-OpStore %102 %58
-%104 = OpIAdd %uint %70 %uint_8
-%105 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %104
-OpStore %105 %59
-%107 = OpIAdd %uint %70 %uint_9
-%108 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %107
-OpStore %108 %60
-OpBranch %74
-%74 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
+ const std::string output_func = kStreamWrite4Frag;
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
+ true, 7u, 23u, false, false,
+ false, false, false);
}
TEST_F(InstBindlessTest, InstrumentOpImage) {
@@ -716,12 +808,15 @@ TEST_F(InstBindlessTest, InstrumentOpImage) {
// using OpImage. This test was created by editing the SPIR-V
// from the Simple test.
- const std::string defs_before =
- R"(OpCapability Shader
+ // clang-format off
+ const std::string defs = R"(
+OpCapability Shader
OpCapability StorageImageReadWithoutFormat
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
+; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
OpExecutionMode %MainPs OriginUpperLeft
OpSource HLSL 500
OpName %MainPs "MainPs"
@@ -737,6 +832,9 @@ OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
OpDecorate %PerViewConstantBuffer_t Block
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -760,87 +858,32 @@ OpDecorate %_entryPointOutput_vColor Location 0
%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
+; CHECK: uint_0 = OpConstant %uint 0
+; CHECK: bool = OpTypeBool
+; CHECK: %86 = OpTypeFunction %void %uint %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kOutputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_51 = OpConstant %uint 51
+; CHECK: %141 = OpConstantNull %v4float
)";
+ // clang-format on
- const std::string defs_after =
- R"(OpCapability Shader
-OpCapability StorageImageReadWithoutFormat
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_51 Block
-OpMemberDecorate %_struct_51 0 Offset 0
-OpMemberDecorate %_struct_51 1 Offset 4
-OpDecorate %53 DescriptorSet 7
-OpDecorate %53 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%9 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%v2int = OpTypeVector %int 2
-%int_0 = OpConstant %int 0
-%15 = OpTypeImage %float 2D 0 0 0 0 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%18 = OpTypeSampledImage %15
-%_arr_18_uint_128 = OpTypeArray %18 %uint_128
-%_ptr_UniformConstant__arr_18_uint_128 = OpTypePointer UniformConstant %_arr_18_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_18_uint_128 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18
-%_ptr_Input_v2int = OpTypePointer Input %v2int
-%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%44 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_51 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_51 = OpTypePointer StorageBuffer %_struct_51
-%53 = OpVariable %_ptr_StorageBuffer__struct_51 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_1 = OpConstant %uint 1
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_51 = OpConstant %uint 51
-%99 = OpConstantNull %v4float
-)";
-
- const std::string func_before =
- R"(%MainPs = OpFunction %void None %3
+ const std::string main_func = R"(
+%MainPs = OpFunction %void None %3
%5 = OpLabel
%53 = OpLoad %v2int %i_vTextureCoords
%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
@@ -850,93 +893,32 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord
%75 = OpImage %20 %66
%71 = OpImageRead %v4float %75 %53
OpStore %_entryPointOutput_vColor %71
+; CHECK-NOT: %71 = OpImageRead %v4float %75 %53
+; CHECK-NOT: OpStore %_entryPointOutput_vColor %71
+; CHECK: %78 = OpULessThan %bool %64 %uint_128
+; CHECK: OpSelectionMerge %79 None
+; CHECK: OpBranchConditional %78 %80 %81
+; CHECK: %80 = OpLabel
+; CHECK: %82 = OpLoad %39 %65
+; CHECK: %83 = OpImage %20 %82
+; CHECK: %84 = OpImageRead %v4float %83 %53
+; CHECK: OpBranch %79
+; CHECK: %81 = OpLabel
+; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %64 %uint_128
+; CHECK: OpBranch %79
+; CHECK: %79 = OpLabel
+; CHECK: %142 = OpPhi %v4float %84 %80 %141 %81
+; CHECK: OpStore %_entryPointOutput_vColor %142
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%MainPs = OpFunction %void None %9
-%26 = OpLabel
-%27 = OpLoad %v2int %i_vTextureCoords
-%28 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%29 = OpLoad %uint %28
-%30 = OpAccessChain %_ptr_UniformConstant_18 %g_tColor %29
-%31 = OpLoad %18 %30
-%32 = OpImage %15 %31
-%36 = OpULessThan %bool %29 %uint_128
-OpSelectionMerge %37 None
-OpBranchConditional %36 %38 %39
-%38 = OpLabel
-%40 = OpLoad %18 %30
-%41 = OpImage %15 %40
-%42 = OpImageRead %v4float %41 %27
-OpBranch %37
-%39 = OpLabel
-%98 = OpFunctionCall %void %43 %uint_51 %uint_0 %29 %uint_128
-OpBranch %37
-%37 = OpLabel
-%100 = OpPhi %v4float %42 %38 %99 %39
-OpStore %_entryPointOutput_vColor %100
-OpReturn
-OpFunctionEnd
-)";
+ const std::string output_func = kStreamWrite4Frag;
- const std::string output_func =
- R"(%43 = OpFunction %void None %44
-%45 = OpFunctionParameter %uint
-%46 = OpFunctionParameter %uint
-%47 = OpFunctionParameter %uint
-%48 = OpFunctionParameter %uint
-%49 = OpLabel
-%55 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_0
-%58 = OpAtomicIAdd %uint %55 %uint_4 %uint_0 %uint_10
-%59 = OpIAdd %uint %58 %uint_10
-%60 = OpArrayLength %uint %53 1
-%61 = OpULessThanEqual %bool %59 %60
-OpSelectionMerge %62 None
-OpBranchConditional %61 %63 %62
-%63 = OpLabel
-%64 = OpIAdd %uint %58 %uint_0
-%66 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %64
-OpStore %66 %uint_10
-%68 = OpIAdd %uint %58 %uint_1
-%69 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %68
-OpStore %69 %uint_23
-%71 = OpIAdd %uint %58 %uint_2
-%72 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %71
-OpStore %72 %45
-%74 = OpIAdd %uint %58 %uint_3
-%75 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %74
-OpStore %75 %uint_4
-%78 = OpLoad %v4float %gl_FragCoord
-%80 = OpBitcast %v4uint %78
-%81 = OpCompositeExtract %uint %80 0
-%82 = OpIAdd %uint %58 %uint_4
-%83 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %82
-OpStore %83 %81
-%84 = OpCompositeExtract %uint %80 1
-%86 = OpIAdd %uint %58 %uint_5
-%87 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %86
-OpStore %87 %84
-%89 = OpIAdd %uint %58 %uint_7
-%90 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %89
-OpStore %90 %46
-%92 = OpIAdd %uint %58 %uint_8
-%93 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %92
-OpStore %93 %47
-%95 = OpIAdd %uint %58 %uint_9
-%96 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %95
-OpStore %96 %48
-OpBranch %62
-%62 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, false, false, false);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
+ true, 7u, 23u, false, false,
+ false, false, false);
}
TEST_F(InstBindlessTest, InstrumentSampledImage) {
@@ -944,11 +926,14 @@ TEST_F(InstBindlessTest, InstrumentSampledImage) {
// using sampled image. This test was created by editing the SPIR-V
// from the Simple test.
- const std::string defs_before =
- R"(OpCapability Shader
+ // clang-format off
+ const std::string defs = R"(
+OpCapability Shader
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
+; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
OpExecutionMode %MainPs OriginUpperLeft
OpSource HLSL 500
OpName %MainPs "MainPs"
@@ -964,6 +949,8 @@ OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
OpDecorate %PerViewConstantBuffer_t Block
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
+)" + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -987,86 +974,32 @@ OpDecorate %_entryPointOutput_vColor Location 0
%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
+; CHECK: uint_0 = OpConstant %uint 0
+; CHECK: bool = OpTypeBool
+; CHECK: %81 = OpTypeFunction %void %uint %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kOutputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_49 = OpConstant %uint 49
+; CHECK: %136 = OpConstantNull %v4float
)";
+ // clang-format on
- const std::string defs_after =
- R"(OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_49 Block
-OpMemberDecorate %_struct_49 0 Offset 0
-OpMemberDecorate %_struct_49 1 Offset 4
-OpDecorate %51 DescriptorSet 7
-OpDecorate %51 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%9 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%15 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%18 = OpTypeSampledImage %15
-%_arr_18_uint_128 = OpTypeArray %18 %uint_128
-%_ptr_UniformConstant__arr_18_uint_128 = OpTypePointer UniformConstant %_arr_18_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_18_uint_128 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%42 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_49 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_49 = OpTypePointer StorageBuffer %_struct_49
-%51 = OpVariable %_ptr_StorageBuffer__struct_49 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_1 = OpConstant %uint 1
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_49 = OpConstant %uint 49
-%97 = OpConstantNull %v4float
-)";
-
- const std::string func_before =
- R"(%MainPs = OpFunction %void None %3
+ const std::string main_func = R"(
+%MainPs = OpFunction %void None %3
%5 = OpLabel
%53 = OpLoad %v2float %i_vTextureCoords
%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
@@ -1075,91 +1008,31 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord
%66 = OpLoad %39 %65
%71 = OpImageSampleImplicitLod %v4float %66 %53
OpStore %_entryPointOutput_vColor %71
+; CHECK-NOT: %71 = OpImageSampleImplicitLod %v4float %66 %53
+; CHECK-NOT: OpStore %_entryPointOutput_vColor %71
+; CHECK: %74 = OpULessThan %bool %64 %uint_128
+; CHECK: OpSelectionMerge %75 None
+; CHECK: OpBranchConditional %74 %76 %77
+; CHECK: %76 = OpLabel
+; CHECK: %78 = OpLoad %39 %65
+; CHECK: %79 = OpImageSampleImplicitLod %v4float %78 %53
+; CHECK: OpBranch %75
+; CHECK: %77 = OpLabel
+; CHECK: %135 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_49 %uint_0 %64 %uint_128
+; CHECK: OpBranch %75
+; CHECK: %75 = OpLabel
+; CHECK: %137 = OpPhi %v4float %79 %76 %136 %77
+; CHECK: OpStore %_entryPointOutput_vColor %137
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%MainPs = OpFunction %void None %9
-%26 = OpLabel
-%27 = OpLoad %v2float %i_vTextureCoords
-%28 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%29 = OpLoad %uint %28
-%30 = OpAccessChain %_ptr_UniformConstant_18 %g_tColor %29
-%31 = OpLoad %18 %30
-%35 = OpULessThan %bool %29 %uint_128
-OpSelectionMerge %36 None
-OpBranchConditional %35 %37 %38
-%37 = OpLabel
-%39 = OpLoad %18 %30
-%40 = OpImageSampleImplicitLod %v4float %39 %27
-OpBranch %36
-%38 = OpLabel
-%96 = OpFunctionCall %void %41 %uint_49 %uint_0 %29 %uint_128
-OpBranch %36
-%36 = OpLabel
-%98 = OpPhi %v4float %40 %37 %97 %38
-OpStore %_entryPointOutput_vColor %98
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string output_func =
- R"(%41 = OpFunction %void None %42
-%43 = OpFunctionParameter %uint
-%44 = OpFunctionParameter %uint
-%45 = OpFunctionParameter %uint
-%46 = OpFunctionParameter %uint
-%47 = OpLabel
-%53 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_0
-%56 = OpAtomicIAdd %uint %53 %uint_4 %uint_0 %uint_10
-%57 = OpIAdd %uint %56 %uint_10
-%58 = OpArrayLength %uint %51 1
-%59 = OpULessThanEqual %bool %57 %58
-OpSelectionMerge %60 None
-OpBranchConditional %59 %61 %60
-%61 = OpLabel
-%62 = OpIAdd %uint %56 %uint_0
-%64 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %62
-OpStore %64 %uint_10
-%66 = OpIAdd %uint %56 %uint_1
-%67 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %66
-OpStore %67 %uint_23
-%69 = OpIAdd %uint %56 %uint_2
-%70 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %69
-OpStore %70 %43
-%72 = OpIAdd %uint %56 %uint_3
-%73 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %72
-OpStore %73 %uint_4
-%76 = OpLoad %v4float %gl_FragCoord
-%78 = OpBitcast %v4uint %76
-%79 = OpCompositeExtract %uint %78 0
-%80 = OpIAdd %uint %56 %uint_4
-%81 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %80
-OpStore %81 %79
-%82 = OpCompositeExtract %uint %78 1
-%84 = OpIAdd %uint %56 %uint_5
-%85 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %84
-OpStore %85 %82
-%87 = OpIAdd %uint %56 %uint_7
-%88 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %87
-OpStore %88 %44
-%90 = OpIAdd %uint %56 %uint_8
-%91 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %90
-OpStore %91 %45
-%93 = OpIAdd %uint %56 %uint_9
-%94 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %93
-OpStore %94 %46
-OpBranch %60
-%60 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
+ const std::string output_func = kStreamWrite4Frag;
- // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, false, false, false);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
+ true, 7u, 23u, false, false,
+ false, false, false);
}
TEST_F(InstBindlessTest, InstrumentImageWrite) {
@@ -1167,12 +1040,15 @@ TEST_F(InstBindlessTest, InstrumentImageWrite) {
// doing bindless image write. This test was created by editing the SPIR-V
// from the Simple test.
- const std::string defs_before =
- R"(OpCapability Shader
+ // clang-format off
+ const std::string defs = R"(
+OpCapability Shader
OpCapability StorageImageWriteWithoutFormat
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
+; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
OpExecutionMode %MainPs OriginUpperLeft
OpSource HLSL 500
OpName %MainPs "MainPs"
@@ -1188,6 +1064,9 @@ OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
OpDecorate %PerViewConstantBuffer_t Block
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -1212,87 +1091,31 @@ OpDecorate %_entryPointOutput_vColor Location 0
%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
+; CHECK: uint_0 = OpConstant %uint 0
+; CHECK: bool = OpTypeBool
+; CHECK: %41 = OpTypeFunction %void %uint %uint %uint %uint
+; CHECK: _runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kOutputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_51 = OpConstant %uint 51
)";
+ // clang-format on
- const std::string defs_after =
- R"(OpCapability Shader
-OpCapability StorageImageWriteWithoutFormat
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 3
-OpDecorate %g_tColor Binding 0
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_48 Block
-OpMemberDecorate %_struct_48 0 Offset 0
-OpMemberDecorate %_struct_48 1 Offset 4
-OpDecorate %50 DescriptorSet 7
-OpDecorate %50 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%9 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%v2int = OpTypeVector %int 2
-%int_0 = OpConstant %int 0
-%16 = OpTypeImage %float 2D 0 0 0 0 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%19 = OpConstantNull %v4float
-%_arr_16_uint_128 = OpTypeArray %16 %uint_128
-%_ptr_UniformConstant__arr_16_uint_128 = OpTypePointer UniformConstant %_arr_16_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_16_uint_128 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16
-%_ptr_Input_v2int = OpTypePointer Input %v2int
-%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%41 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_48 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_48 = OpTypePointer StorageBuffer %_struct_48
-%50 = OpVariable %_ptr_StorageBuffer__struct_48 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_1 = OpConstant %uint 1
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_51 = OpConstant %uint 51
-)";
-
- const std::string func_before =
- R"(%MainPs = OpFunction %void None %3
+ const std::string main_func = R"(
+%MainPs = OpFunction %void None %3
%5 = OpLabel
%53 = OpLoad %v2int %i_vTextureCoords
%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
@@ -1301,90 +1124,30 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord
%66 = OpLoad %20 %65
OpImageWrite %66 %53 %80
OpStore %_entryPointOutput_vColor %80
+; CHECK-NOT: OpImageWrite %66 %53 %80
+; CHECK-NOT: OpStore %_entryPointOutput_vColor %80
+; CHECK: %35 = OpULessThan %bool %30 %uint_128
+; CHECK: OpSelectionMerge %36 None
+; CHECK: OpBranchConditional %35 %37 %38
+; CHECK: %37 = OpLabel
+; CHECK: %39 = OpLoad %16 %31
+; CHECK: OpImageWrite %39 %28 %19
+; CHECK: OpBranch %36
+; CHECK: %38 = OpLabel
+; CHECK: %95 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %30 %uint_128
+; CHECK: OpBranch %36
+; CHECK: %36 = OpLabel
+; CHECK: OpStore %_entryPointOutput_vColor %19
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%MainPs = OpFunction %void None %9
-%27 = OpLabel
-%28 = OpLoad %v2int %i_vTextureCoords
-%29 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%30 = OpLoad %uint %29
-%31 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %30
-%32 = OpLoad %16 %31
-%35 = OpULessThan %bool %30 %uint_128
-OpSelectionMerge %36 None
-OpBranchConditional %35 %37 %38
-%37 = OpLabel
-%39 = OpLoad %16 %31
-OpImageWrite %39 %28 %19
-OpBranch %36
-%38 = OpLabel
-%95 = OpFunctionCall %void %40 %uint_51 %uint_0 %30 %uint_128
-OpBranch %36
-%36 = OpLabel
-OpStore %_entryPointOutput_vColor %19
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string output_func =
- R"(%40 = OpFunction %void None %41
-%42 = OpFunctionParameter %uint
-%43 = OpFunctionParameter %uint
-%44 = OpFunctionParameter %uint
-%45 = OpFunctionParameter %uint
-%46 = OpLabel
-%52 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_0
-%55 = OpAtomicIAdd %uint %52 %uint_4 %uint_0 %uint_10
-%56 = OpIAdd %uint %55 %uint_10
-%57 = OpArrayLength %uint %50 1
-%58 = OpULessThanEqual %bool %56 %57
-OpSelectionMerge %59 None
-OpBranchConditional %58 %60 %59
-%60 = OpLabel
-%61 = OpIAdd %uint %55 %uint_0
-%63 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %61
-OpStore %63 %uint_10
-%65 = OpIAdd %uint %55 %uint_1
-%66 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %65
-OpStore %66 %uint_23
-%68 = OpIAdd %uint %55 %uint_2
-%69 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %68
-OpStore %69 %42
-%71 = OpIAdd %uint %55 %uint_3
-%72 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %71
-OpStore %72 %uint_4
-%75 = OpLoad %v4float %gl_FragCoord
-%77 = OpBitcast %v4uint %75
-%78 = OpCompositeExtract %uint %77 0
-%79 = OpIAdd %uint %55 %uint_4
-%80 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %79
-OpStore %80 %78
-%81 = OpCompositeExtract %uint %77 1
-%83 = OpIAdd %uint %55 %uint_5
-%84 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %83
-OpStore %84 %81
-%86 = OpIAdd %uint %55 %uint_7
-%87 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %86
-OpStore %87 %43
-%89 = OpIAdd %uint %55 %uint_8
-%90 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %89
-OpStore %90 %44
-%92 = OpIAdd %uint %55 %uint_9
-%93 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %92
-OpStore %93 %45
-OpBranch %59
-%59 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
+ const std::string output_func = kStreamWrite4Frag;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
+ true, 7u, 23u, false, false,
+ false, false, false);
}
TEST_F(InstBindlessTest, InstrumentVertexSimple) {
@@ -1392,9 +1155,11 @@ TEST_F(InstBindlessTest, InstrumentVertexSimple) {
// doing bindless image write. This test was created by editing the SPIR-V
// from the Simple test.
- const std::string defs_before =
- R"(OpCapability Shader
+ // clang-format off
+ const std::string defs = R"(
+OpCapability Shader
OpCapability Sampled1D
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %_ %coords2D
@@ -1413,6 +1178,10 @@ OpName %foo "foo"
OpMemberName %foo 0 "g_idx"
OpName %__0 ""
OpName %coords2D "coords2D"
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
+; CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
@@ -1455,106 +1224,31 @@ OpDecorate %coords2D Location 0
%v2float = OpTypeVector %float 2
%_ptr_Input_v2float = OpTypePointer Input %v2float
%coords2D = OpVariable %_ptr_Input_v2float Input
+; CHECK: %uint_0 = OpConstant %uint 0
+; CHECK: %bool = OpTypeBool
+; CHECK: %54 = OpTypeFunction %void %uint %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kOutputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %_ptr_Input_uint = OpTypePointer Input %uint
+; CHECK: %gl_VertexIndex = OpVariable %_ptr_Input_uint Input
+; CHECK: %gl_InstanceIndex = OpVariable %_ptr_Input_uint Input
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_74 = OpConstant %uint 74
+; CHECK: %106 = OpConstantNull %v4float
)";
+ // clang-format on
- const std::string defs_after =
- R"(OpCapability Shader
-OpCapability Sampled1D
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Vertex %main "main" %_ %coords2D %gl_VertexIndex %gl_InstanceIndex
-OpSource GLSL 450
-OpName %main "main"
-OpName %lod "lod"
-OpName %coords1D "coords1D"
-OpName %gl_PerVertex "gl_PerVertex"
-OpMemberName %gl_PerVertex 0 "gl_Position"
-OpMemberName %gl_PerVertex 1 "gl_PointSize"
-OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
-OpMemberName %gl_PerVertex 3 "gl_CullDistance"
-OpName %_ ""
-OpName %texSampler1D "texSampler1D"
-OpName %foo "foo"
-OpMemberName %foo 0 "g_idx"
-OpName %__0 ""
-OpName %coords2D "coords2D"
-OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
-OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
-OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
-OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
-OpDecorate %gl_PerVertex Block
-OpDecorate %texSampler1D DescriptorSet 0
-OpDecorate %texSampler1D Binding 3
-OpMemberDecorate %foo 0 Offset 0
-OpDecorate %foo Block
-OpDecorate %__0 DescriptorSet 0
-OpDecorate %__0 Binding 5
-OpDecorate %coords2D Location 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_61 Block
-OpMemberDecorate %_struct_61 0 Offset 0
-OpMemberDecorate %_struct_61 1 Offset 4
-OpDecorate %63 DescriptorSet 7
-OpDecorate %63 Binding 0
-OpDecorate %gl_VertexIndex BuiltIn VertexIndex
-OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
-%void = OpTypeVoid
-%12 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Function_float = OpTypePointer Function %float
-%float_3 = OpConstant %float 3
-%float_1_78900003 = OpConstant %float 1.78900003
-%v4float = OpTypeVector %float 4
-%uint = OpTypeInt 32 0
-%uint_1 = OpConstant %uint 1
-%_arr_float_uint_1 = OpTypeArray %float %uint_1
-%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
-%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
-%_ = OpVariable %_ptr_Output_gl_PerVertex Output
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%24 = OpTypeImage %float 1D 0 0 0 1 Unknown
-%25 = OpTypeSampledImage %24
-%uint_128 = OpConstant %uint 128
-%_arr_25_uint_128 = OpTypeArray %25 %uint_128
-%_ptr_UniformConstant__arr_25_uint_128 = OpTypePointer UniformConstant %_arr_25_uint_128
-%texSampler1D = OpVariable %_ptr_UniformConstant__arr_25_uint_128 UniformConstant
-%foo = OpTypeStruct %int
-%_ptr_Uniform_foo = OpTypePointer Uniform %foo
-%__0 = OpVariable %_ptr_Uniform_foo Uniform
-%_ptr_Uniform_int = OpTypePointer Uniform %int
-%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%v2float = OpTypeVector %float 2
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%coords2D = OpVariable %_ptr_Input_v2float Input
-%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%54 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_61 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_61 = OpTypePointer StorageBuffer %_struct_61
-%63 = OpVariable %_ptr_StorageBuffer__struct_61 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_uint = OpTypePointer Input %uint
-%gl_VertexIndex = OpVariable %_ptr_Input_uint Input
-%gl_InstanceIndex = OpVariable %_ptr_Input_uint Input
-%uint_5 = OpConstant %uint 5
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_74 = OpConstant %uint 74
-%106 = OpConstantNull %v4float
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %3
+ const std::string main_func = R"(
+%main = OpFunction %void None %3
%5 = OpLabel
%lod = OpVariable %_ptr_Function_float Function
%coords1D = OpVariable %_ptr_Function_float Function
@@ -1569,96 +1263,34 @@ OpStore %coords1D %float_1_78900003
%38 = OpImageSampleExplicitLod %v4float %35 %36 Lod %37
%40 = OpAccessChain %_ptr_Output_v4float %_ %int_0
OpStore %40 %38
+; CHECK-NOT: %38 = OpImageSampleExplicitLod %v4float %35 %36 Lod %37
+; CHECK-NOT: %40 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+; CHECK-NOT: OpStore %40 %38
+; CHECK: %46 = OpULessThan %bool %37 %uint_128
+; CHECK: OpSelectionMerge %47 None
+; CHECK: OpBranchConditional %46 %48 %49
+; CHECK: %48 = OpLabel
+; CHECK: %50 = OpLoad %25 %38
+; CHECK: %51 = OpImageSampleExplicitLod %v4float %50 %40 Lod %41
+; CHECK: OpBranch %47
+; CHECK: %49 = OpLabel
+; CHECK: %52 = OpBitcast %uint %37
+; CHECK: %105 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_74 %uint_0 %52 %uint_128
+; CHECK: OpBranch %47
+; CHECK: %47 = OpLabel
+; CHECK: %107 = OpPhi %v4float %51 %48 %106 %49
+; CHECK: %43 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+; CHECK: OpStore %43 %107
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %12
-%35 = OpLabel
-%lod = OpVariable %_ptr_Function_float Function
-%coords1D = OpVariable %_ptr_Function_float Function
-OpStore %lod %float_3
-OpStore %coords1D %float_1_78900003
-%36 = OpAccessChain %_ptr_Uniform_int %__0 %int_0
-%37 = OpLoad %int %36
-%38 = OpAccessChain %_ptr_UniformConstant_25 %texSampler1D %37
-%39 = OpLoad %25 %38
-%40 = OpLoad %float %coords1D
-%41 = OpLoad %float %lod
-%46 = OpULessThan %bool %37 %uint_128
-OpSelectionMerge %47 None
-OpBranchConditional %46 %48 %49
-%48 = OpLabel
-%50 = OpLoad %25 %38
-%51 = OpImageSampleExplicitLod %v4float %50 %40 Lod %41
-OpBranch %47
-%49 = OpLabel
-%52 = OpBitcast %uint %37
-%105 = OpFunctionCall %void %53 %uint_74 %uint_0 %52 %uint_128
-OpBranch %47
-%47 = OpLabel
-%107 = OpPhi %v4float %51 %48 %106 %49
-%43 = OpAccessChain %_ptr_Output_v4float %_ %int_0
-OpStore %43 %107
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string output_func =
- R"(%53 = OpFunction %void None %54
-%55 = OpFunctionParameter %uint
-%56 = OpFunctionParameter %uint
-%57 = OpFunctionParameter %uint
-%58 = OpFunctionParameter %uint
-%59 = OpLabel
-%65 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_0
-%68 = OpAtomicIAdd %uint %65 %uint_4 %uint_0 %uint_10
-%69 = OpIAdd %uint %68 %uint_10
-%70 = OpArrayLength %uint %63 1
-%71 = OpULessThanEqual %bool %69 %70
-OpSelectionMerge %72 None
-OpBranchConditional %71 %73 %72
-%73 = OpLabel
-%74 = OpIAdd %uint %68 %uint_0
-%75 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %74
-OpStore %75 %uint_10
-%77 = OpIAdd %uint %68 %uint_1
-%78 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %77
-OpStore %78 %uint_23
-%80 = OpIAdd %uint %68 %uint_2
-%81 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %80
-OpStore %81 %55
-%83 = OpIAdd %uint %68 %uint_3
-%84 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %83
-OpStore %84 %uint_0
-%87 = OpLoad %uint %gl_VertexIndex
-%88 = OpIAdd %uint %68 %uint_4
-%89 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %88
-OpStore %89 %87
-%91 = OpLoad %uint %gl_InstanceIndex
-%93 = OpIAdd %uint %68 %uint_5
-%94 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %93
-OpStore %94 %91
-%96 = OpIAdd %uint %68 %uint_7
-%97 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %96
-OpStore %97 %56
-%99 = OpIAdd %uint %68 %uint_8
-%100 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %99
-OpStore %100 %57
-%102 = OpIAdd %uint %68 %uint_9
-%103 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %102
-OpStore %103 %58
-OpBranch %72
-%72 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
+ const std::string output_func = kStreamWrite4Vert;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
+ true, 7u, 23u, false, false,
+ false, false, false);
}
TEST_F(InstBindlessTest, InstrumentTeseSimple) {
@@ -1680,13 +1312,13 @@ TEST_F(InstBindlessTest, InstrumentTeseSimple) {
// gl_Position = adds[uniform_index_buffer.index].val;
// }
//
- // clang-format on
- const std::string defs_before =
- R"(OpCapability Tessellation
+ const std::string defs = R"(
+OpCapability Tessellation
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint TessellationEvaluation %main "main" %_
+; CHECK: OpEntryPoint TessellationEvaluation %main "main" %_ %gl_PrimitiveID %gl_TessCoord
OpExecutionMode %main Triangles
OpExecutionMode %main SpacingEqual
OpExecutionMode %main VertexOrderCw
@@ -1718,6 +1350,10 @@ OpMemberDecorate %ufoo 0 Offset 0
OpDecorate %ufoo Block
OpDecorate %uniform_index_buffer DescriptorSet 0
OpDecorate %uniform_index_buffer Binding 0
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId
+; CHECK: OpDecorate %gl_TessCoord BuiltIn TessCoord
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -1741,211 +1377,80 @@ OpDecorate %uniform_index_buffer Binding 0
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%_ptr_StorageBuffer_v4float = OpTypePointer StorageBuffer %v4float
%_ptr_Output_v4float = OpTypePointer Output %v4float
+; CHECK: %uint_0 = OpConstant %uint 0
+; CHECK: %bool = OpTypeBool
+; CHECK: %40 = OpTypeFunction %void %uint %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kOutputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %_ptr_Input_uint = OpTypePointer Input %uint
+; CHECK: %gl_PrimitiveID = OpVariable %_ptr_Input_uint Input
+; CHECK: %v3float = OpTypeVector %float 3
+; CHECK: %_ptr_Input_v3float = OpTypePointer Input %v3float
+; CHECK: %gl_TessCoord = OpVariable %_ptr_Input_v3float Input
+; CHECK: %v3uint = OpTypeVector %uint 3
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_6 = OpConstant %uint 6
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_63 = OpConstant %uint 63
+; CHECK: %101 = OpConstantNull %v4float
)";
+ // clang-format on
- const std::string defs_after =
- R"(OpCapability Tessellation
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint TessellationEvaluation %main "main" %_ %gl_PrimitiveID %gl_TessCoord
-OpExecutionMode %main Triangles
-OpExecutionMode %main SpacingEqual
-OpExecutionMode %main VertexOrderCw
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %gl_PerVertex "gl_PerVertex"
-OpMemberName %gl_PerVertex 0 "gl_Position"
-OpMemberName %gl_PerVertex 1 "gl_PointSize"
-OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
-OpMemberName %gl_PerVertex 3 "gl_CullDistance"
-OpName %_ ""
-OpName %bfoo "bfoo"
-OpMemberName %bfoo 0 "val"
-OpName %adds "adds"
-OpName %ufoo "ufoo"
-OpMemberName %ufoo 0 "index"
-OpName %uniform_index_buffer "uniform_index_buffer"
-OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
-OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
-OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
-OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
-OpDecorate %gl_PerVertex Block
-OpMemberDecorate %bfoo 0 Offset 0
-OpDecorate %bfoo Block
-OpDecorate %adds DescriptorSet 0
-OpDecorate %adds Binding 1
-OpMemberDecorate %ufoo 0 Offset 0
-OpDecorate %ufoo Block
-OpDecorate %uniform_index_buffer DescriptorSet 0
-OpDecorate %uniform_index_buffer Binding 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_47 Block
-OpMemberDecorate %_struct_47 0 Offset 0
-OpMemberDecorate %_struct_47 1 Offset 4
-OpDecorate %49 DescriptorSet 7
-OpDecorate %49 Binding 0
-OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId
-OpDecorate %gl_TessCoord BuiltIn TessCoord
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%uint = OpTypeInt 32 0
-%uint_1 = OpConstant %uint 1
-%_arr_float_uint_1 = OpTypeArray %float %uint_1
-%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
-%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
-%_ = OpVariable %_ptr_Output_gl_PerVertex Output
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%bfoo = OpTypeStruct %v4float
-%uint_11 = OpConstant %uint 11
-%_arr_bfoo_uint_11 = OpTypeArray %bfoo %uint_11
-%_ptr_StorageBuffer__arr_bfoo_uint_11 = OpTypePointer StorageBuffer %_arr_bfoo_uint_11
-%adds = OpVariable %_ptr_StorageBuffer__arr_bfoo_uint_11 StorageBuffer
-%ufoo = OpTypeStruct %uint
-%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
-%uniform_index_buffer = OpVariable %_ptr_Uniform_ufoo Uniform
-%_ptr_Uniform_uint = OpTypePointer Uniform %uint
-%_ptr_StorageBuffer_v4float = OpTypePointer StorageBuffer %v4float
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%40 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_47 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_47 = OpTypePointer StorageBuffer %_struct_47
-%49 = OpVariable %_ptr_StorageBuffer__struct_47 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_uint = OpTypePointer Input %uint
-%gl_PrimitiveID = OpVariable %_ptr_Input_uint Input
-%v3float = OpTypeVector %float 3
-%_ptr_Input_v3float = OpTypePointer Input %v3float
-%gl_TessCoord = OpVariable %_ptr_Input_v3float Input
-%v3uint = OpTypeVector %uint 3
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_63 = OpConstant %uint 63
-%101 = OpConstantNull %v4float
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %3
+ const std::string main_func = R"(
+%main = OpFunction %void None %3
%5 = OpLabel
%25 = OpAccessChain %_ptr_Uniform_uint %uniform_index_buffer %int_0
%26 = OpLoad %uint %25
%28 = OpAccessChain %_ptr_StorageBuffer_v4float %adds %26 %int_0
%29 = OpLoad %v4float %28
+; CHECK-NOT: %29 = OpLoad %v4float %28
+; CHECK: %34 = OpULessThan %bool %28 %uint_11
+; CHECK: OpSelectionMerge %35 None
+; CHECK: OpBranchConditional %34 %36 %37
+; CHECK: %36 = OpLabel
+; CHECK: %38 = OpLoad %v4float %29
+; CHECK: OpBranch %35
+; CHECK: %37 = OpLabel
+; CHECK: %100 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_63 %uint_0 %28 %uint_11
+; CHECK: OpBranch %35
+; CHECK: %35 = OpLabel
+; CHECK: %102 = OpPhi %v4float %38 %36 %101 %37
%31 = OpAccessChain %_ptr_Output_v4float %_ %int_0
OpStore %31 %29
+; CHECK-NOT: OpStore %31 %29
+; CHECK: OpStore %31 %102
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %10
-%26 = OpLabel
-%27 = OpAccessChain %_ptr_Uniform_uint %uniform_index_buffer %int_0
-%28 = OpLoad %uint %27
-%29 = OpAccessChain %_ptr_StorageBuffer_v4float %adds %28 %int_0
-%34 = OpULessThan %bool %28 %uint_11
-OpSelectionMerge %35 None
-OpBranchConditional %34 %36 %37
-%36 = OpLabel
-%38 = OpLoad %v4float %29
-OpBranch %35
-%37 = OpLabel
-%100 = OpFunctionCall %void %39 %uint_63 %uint_0 %28 %uint_11
-OpBranch %35
-%35 = OpLabel
-%102 = OpPhi %v4float %38 %36 %101 %37
-%31 = OpAccessChain %_ptr_Output_v4float %_ %int_0
-OpStore %31 %102
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string output_func =
- R"(%39 = OpFunction %void None %40
-%41 = OpFunctionParameter %uint
-%42 = OpFunctionParameter %uint
-%43 = OpFunctionParameter %uint
-%44 = OpFunctionParameter %uint
-%45 = OpLabel
-%51 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_0
-%54 = OpAtomicIAdd %uint %51 %uint_4 %uint_0 %uint_10
-%55 = OpIAdd %uint %54 %uint_10
-%56 = OpArrayLength %uint %49 1
-%57 = OpULessThanEqual %bool %55 %56
-OpSelectionMerge %58 None
-OpBranchConditional %57 %59 %58
-%59 = OpLabel
-%60 = OpIAdd %uint %54 %uint_0
-%61 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %60
-OpStore %61 %uint_10
-%63 = OpIAdd %uint %54 %uint_1
-%64 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %63
-OpStore %64 %uint_23
-%66 = OpIAdd %uint %54 %uint_2
-%67 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %66
-OpStore %67 %41
-%69 = OpIAdd %uint %54 %uint_3
-%70 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %69
-OpStore %70 %uint_2
-%73 = OpLoad %uint %gl_PrimitiveID
-%74 = OpIAdd %uint %54 %uint_4
-%75 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %74
-OpStore %75 %73
-%79 = OpLoad %v3float %gl_TessCoord
-%81 = OpBitcast %v3uint %79
-%82 = OpCompositeExtract %uint %81 0
-%83 = OpCompositeExtract %uint %81 1
-%85 = OpIAdd %uint %54 %uint_5
-%86 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %85
-OpStore %86 %82
-%88 = OpIAdd %uint %54 %uint_6
-%89 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %88
-OpStore %89 %83
-%91 = OpIAdd %uint %54 %uint_7
-%92 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %91
-OpStore %92 %42
-%94 = OpIAdd %uint %54 %uint_8
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %94
-OpStore %95 %43
-%97 = OpIAdd %uint %54 %uint_9
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %97
-OpStore %98 %44
-OpBranch %58
-%58 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
+ const std::string output_func = kStreamWrite4Tese;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + output_func, true,
- true, 7u, 23u, false, false, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + output_func,
+ true, 7u, 23u, false, false,
+ false, false, false);
}
TEST_F(InstBindlessTest, MultipleDebugFunctions) {
// Same source as Simple, but compiled -g and not optimized, especially not
// inlined. The OpSource has had the source extracted for the sake of brevity.
- const std::string defs_before =
- R"(OpCapability Shader
+ // clang-format off
+ const std::string defs = R"(
+OpCapability Shader
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%2 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
+; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
OpExecutionMode %MainPs OriginUpperLeft
%1 = OpString "foo5.frag"
OpSource HLSL 500 %1
@@ -1974,6 +1479,9 @@ OpDecorate %g_sAniso DescriptorSet 0
OpDecorate %g_sAniso Binding 1
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%4 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -2007,109 +1515,32 @@ OpDecorate %_entryPointOutput_vColor Location 0
%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
+; CHECK: %uint_0 = OpConstant %uint 0
+; CHECK: %bool = OpTypeBool
+; CHECK: %70 = OpTypeFunction %void %uint %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kOutputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_109 = OpConstant %uint 109
+; CHECK: %125 = OpConstantNull %v4float
)";
+ // clang-format on
- const std::string defs_after =
- R"(OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
-OpExecutionMode %MainPs OriginUpperLeft
-%5 = OpString "foo5.frag"
-OpSource HLSL 500 %5
-OpName %MainPs "MainPs"
-OpName %PS_INPUT "PS_INPUT"
-OpMemberName %PS_INPUT 0 "vTextureCoords"
-OpName %PS_OUTPUT "PS_OUTPUT"
-OpMemberName %PS_OUTPUT 0 "vColor"
-OpName %_MainPs_struct_PS_INPUT_vf21_ "@MainPs(struct-PS_INPUT-vf21;"
-OpName %i "i"
-OpName %ps_output "ps_output"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %g_sAniso "g_sAniso"
-OpName %i_0 "i"
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpName %param "param"
-OpDecorate %g_tColor DescriptorSet 0
-OpDecorate %g_tColor Binding 0
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %g_sAniso Binding 1
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_77 Block
-OpMemberDecorate %_struct_77 0 Offset 0
-OpMemberDecorate %_struct_77 1 Offset 4
-OpDecorate %79 DescriptorSet 7
-OpDecorate %79 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%18 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%PS_INPUT = OpTypeStruct %v2float
-%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
-%v4float = OpTypeVector %float 4
-%PS_OUTPUT = OpTypeStruct %v4float
-%23 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
-%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%27 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%_arr_27_uint_128 = OpTypeArray %27 %uint_128
-%_ptr_UniformConstant__arr_27_uint_128 = OpTypePointer UniformConstant %_arr_27_uint_128
-%g_tColor = OpVariable %_ptr_UniformConstant__arr_27_uint_128 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_27 = OpTypePointer UniformConstant %27
-%35 = OpTypeSampler
-%_ptr_UniformConstant_35 = OpTypePointer UniformConstant %35
-%g_sAniso = OpVariable %_ptr_UniformConstant_35 UniformConstant
-%37 = OpTypeSampledImage %27
-%_ptr_Function_v2float = OpTypePointer Function %v2float
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%70 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_77 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_77 = OpTypePointer StorageBuffer %_struct_77
-%79 = OpVariable %_ptr_StorageBuffer__struct_77 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_1 = OpConstant %uint 1
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_109 = OpConstant %uint 109
-%125 = OpConstantNull %v4float
-)";
-
- const std::string func1_before =
- R"(%MainPs = OpFunction %void None %4
+ const std::string func1 = R"(
+%MainPs = OpFunction %void None %4
%6 = OpLabel
%i_0 = OpVariable %_ptr_Function_PS_INPUT Function
%param = OpVariable %_ptr_Function_PS_INPUT Function
@@ -2126,26 +1557,8 @@ OpReturn
OpFunctionEnd
)";
- const std::string func1_after =
- R"(%MainPs = OpFunction %void None %18
-%42 = OpLabel
-%i_0 = OpVariable %_ptr_Function_PS_INPUT Function
-%param = OpVariable %_ptr_Function_PS_INPUT Function
-OpLine %5 21 0
-%43 = OpLoad %v2float %i_vTextureCoords
-%44 = OpAccessChain %_ptr_Function_v2float %i_0 %int_0
-OpStore %44 %43
-%45 = OpLoad %PS_INPUT %i_0
-OpStore %param %45
-%46 = OpFunctionCall %PS_OUTPUT %_MainPs_struct_PS_INPUT_vf21_ %param
-%47 = OpCompositeExtract %v4float %46 0
-OpStore %_entryPointOutput_vColor %47
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string func2_before =
- R"(%_MainPs_struct_PS_INPUT_vf21_ = OpFunction %PS_OUTPUT None %13
+ const std::string func2 = R"(
+%_MainPs_struct_PS_INPUT_vf21_ = OpFunction %PS_OUTPUT None %13
%i = OpFunctionParameter %_ptr_Function_PS_INPUT
%16 = OpLabel
%ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function
@@ -2159,6 +1572,24 @@ OpLine %1 24 0
%43 = OpAccessChain %_ptr_Function_v2float %i %int_0
%44 = OpLoad %v2float %43
%45 = OpImageSampleImplicitLod %v4float %41 %44
+; CHECK-NOT: %45 = OpImageSampleImplicitLod %v4float %41 %44
+; CHECK: OpNoLine
+; CHECK: %62 = OpULessThan %bool %50 %uint_128
+; CHECK: OpSelectionMerge %63 None
+; CHECK: OpBranchConditional %62 %64 %65
+; CHECK: %64 = OpLabel
+; CHECK: %66 = OpLoad %27 %51
+; CHECK: %67 = OpSampledImage %37 %66 %53
+; CHECK: OpLine %5 24 0
+; CHECK: %68 = OpImageSampleImplicitLod %v4float %67 %56
+; CHECK: OpNoLine
+; CHECK: OpBranch %63
+; CHECK: %65 = OpLabel
+; CHECK: %124 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_109 %uint_0 %50 %uint_128
+; CHECK: OpBranch %63
+; CHECK: %63 = OpLabel
+; CHECK: %126 = OpPhi %v4float %68 %64 %125 %65
+; CHECK: OpLine %5 24 0
%47 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0
OpStore %47 %45
OpLine %1 25 0
@@ -2167,102 +1598,12 @@ OpReturnValue %48
OpFunctionEnd
)";
- const std::string func2_after =
- R"(%_MainPs_struct_PS_INPUT_vf21_ = OpFunction %PS_OUTPUT None %23
-%i = OpFunctionParameter %_ptr_Function_PS_INPUT
-%48 = OpLabel
-%ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function
-OpLine %5 24 0
-%49 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%50 = OpLoad %uint %49
-%51 = OpAccessChain %_ptr_UniformConstant_27 %g_tColor %50
-%52 = OpLoad %27 %51
-%53 = OpLoad %35 %g_sAniso
-%54 = OpSampledImage %37 %52 %53
-%55 = OpAccessChain %_ptr_Function_v2float %i %int_0
-%56 = OpLoad %v2float %55
-OpNoLine
-%62 = OpULessThan %bool %50 %uint_128
-OpSelectionMerge %63 None
-OpBranchConditional %62 %64 %65
-%64 = OpLabel
-%66 = OpLoad %27 %51
-%67 = OpSampledImage %37 %66 %53
-OpLine %5 24 0
-%68 = OpImageSampleImplicitLod %v4float %67 %56
-OpNoLine
-OpBranch %63
-%65 = OpLabel
-%124 = OpFunctionCall %void %69 %uint_109 %uint_0 %50 %uint_128
-OpBranch %63
-%63 = OpLabel
-%126 = OpPhi %v4float %68 %64 %125 %65
-OpLine %5 24 0
-%58 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0
-OpStore %58 %126
-OpLine %5 25 0
-%59 = OpLoad %PS_OUTPUT %ps_output
-OpReturnValue %59
-OpFunctionEnd
-)";
-
- const std::string output_func =
- R"(%69 = OpFunction %void None %70
-%71 = OpFunctionParameter %uint
-%72 = OpFunctionParameter %uint
-%73 = OpFunctionParameter %uint
-%74 = OpFunctionParameter %uint
-%75 = OpLabel
-%81 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_0
-%84 = OpAtomicIAdd %uint %81 %uint_4 %uint_0 %uint_10
-%85 = OpIAdd %uint %84 %uint_10
-%86 = OpArrayLength %uint %79 1
-%87 = OpULessThanEqual %bool %85 %86
-OpSelectionMerge %88 None
-OpBranchConditional %87 %89 %88
-%89 = OpLabel
-%90 = OpIAdd %uint %84 %uint_0
-%92 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %90
-OpStore %92 %uint_10
-%94 = OpIAdd %uint %84 %uint_1
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %94
-OpStore %95 %uint_23
-%97 = OpIAdd %uint %84 %uint_2
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %97
-OpStore %98 %71
-%100 = OpIAdd %uint %84 %uint_3
-%101 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %100
-OpStore %101 %uint_4
-%104 = OpLoad %v4float %gl_FragCoord
-%106 = OpBitcast %v4uint %104
-%107 = OpCompositeExtract %uint %106 0
-%108 = OpIAdd %uint %84 %uint_4
-%109 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %108
-OpStore %109 %107
-%110 = OpCompositeExtract %uint %106 1
-%112 = OpIAdd %uint %84 %uint_5
-%113 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %112
-OpStore %113 %110
-%115 = OpIAdd %uint %84 %uint_7
-%116 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %115
-OpStore %116 %72
-%118 = OpIAdd %uint %84 %uint_8
-%119 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %118
-OpStore %119 %73
-%121 = OpIAdd %uint %84 %uint_9
-%122 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %121
-OpStore %122 %74
-OpBranch %88
-%88 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
+ const std::string output_func = kStreamWrite4Frag;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func1_before + func2_before,
- defs_after + func1_after + func2_after + output_func, true, true, 7u, 23u,
- false, false, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(
+ defs + func1 + func2 + output_func, true, 7u, 23u, false, false, false,
+ false, false);
}
TEST_F(InstBindlessTest, RuntimeArray) {
@@ -2270,13 +1611,16 @@ TEST_F(InstBindlessTest, RuntimeArray) {
// with runtime descriptor array. This test was created by editing the
// SPIR-V from the Simple test.
- const std::string defs_before =
- R"(OpCapability Shader
+ // clang-format off
+ const std::string defs = R"(
+OpCapability Shader
OpCapability RuntimeDescriptorArray
OpExtension "SPV_EXT_descriptor_indexing"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
+; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
OpExecutionMode %MainPs OriginUpperLeft
OpSource HLSL 500
OpName %MainPs "MainPs"
@@ -2295,6 +1639,9 @@ OpDecorate %g_sAniso DescriptorSet 1
OpDecorate %g_sAniso Binding 0
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kInputDecorations + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -2321,102 +1668,34 @@ OpDecorate %_entryPointOutput_vColor Location 0
%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
+; CHECK: %uint_0 = OpConstant %uint 0
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %41 = OpTypeFunction %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kInputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %bool = OpTypeBool
+; CHECK: %65 = OpTypeFunction %void %uint %uint %uint %uint
+)" + kOutputGlobals + R"(
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_59 = OpConstant %uint 59
+; CHECK: %116 = OpConstantNull %v4float
+; CHECK: %119 = OpTypeFunction %uint %uint %uint %uint %uint
)";
+ // clang-format on
- const std::string defs_after =
- R"(OpCapability Shader
-OpCapability RuntimeDescriptorArray
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
-OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
-OpName %_ ""
-OpName %g_sAniso "g_sAniso"
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 1
-OpDecorate %g_tColor Binding 2
-OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
-OpDecorate %PerViewConstantBuffer_t Block
-OpDecorate %g_sAniso DescriptorSet 1
-OpDecorate %g_sAniso Binding 0
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_46 Block
-OpMemberDecorate %_struct_46 0 Offset 0
-OpDecorate %48 DescriptorSet 7
-OpDecorate %48 Binding 1
-OpDecorate %_struct_71 Block
-OpMemberDecorate %_struct_71 0 Offset 0
-OpMemberDecorate %_struct_71 1 Offset 4
-OpDecorate %73 DescriptorSet 7
-OpDecorate %73 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%16 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_1 = OpConstant %uint 1
-%_runtimearr_16 = OpTypeRuntimeArray %16
-%_ptr_UniformConstant__runtimearr_16 = OpTypePointer UniformConstant %_runtimearr_16
-%g_tColor = OpVariable %_ptr_UniformConstant__runtimearr_16 UniformConstant
-%PerViewConstantBuffer_t = OpTypeStruct %uint
-%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
-%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
-%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
-%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16
-%24 = OpTypeSampler
-%_ptr_UniformConstant_24 = OpTypePointer UniformConstant %24
-%g_sAniso = OpVariable %_ptr_UniformConstant_24 UniformConstant
-%26 = OpTypeSampledImage %16
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-%uint_0 = OpConstant %uint 0
-%uint_2 = OpConstant %uint 2
-%41 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_46 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_46 = OpTypePointer StorageBuffer %_struct_46
-%48 = OpVariable %_ptr_StorageBuffer__struct_46 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%65 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_71 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_71 = OpTypePointer StorageBuffer %_struct_71
-%73 = OpVariable %_ptr_StorageBuffer__struct_71 StorageBuffer
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_59 = OpConstant %uint 59
-%116 = OpConstantNull %v4float
-%119 = OpTypeFunction %uint %uint %uint %uint %uint
-)";
-
- const std::string func_before =
- R"(%MainPs = OpFunction %void None %3
+ const std::string main_func = R"(
+%MainPs = OpFunction %void None %3
%5 = OpLabel
%53 = OpLoad %v2float %i_vTextureCoords
%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
@@ -2427,138 +1706,46 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord
%68 = OpSampledImage %39 %66 %67
%71 = OpImageSampleImplicitLod %v4float %68 %53
OpStore %_entryPointOutput_vColor %71
+; CHECK-NOT: %71 = OpImageSampleImplicitLod %v4float %68 %53
+; CHECK-NOT: OpStore %_entryPointOutput_vColor %71
+; CHECK: %55 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_2 %uint_2
+; CHECK: %57 = OpULessThan %bool %32 %55
+; CHECK: OpSelectionMerge %58 None
+; CHECK: OpBranchConditional %57 %59 %60
+; CHECK: %59 = OpLabel
+; CHECK: %61 = OpLoad %16 %33
+; CHECK: %62 = OpSampledImage %26 %61 %35
+; CHECK: %136 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_1 %uint_2 %32
+; CHECK: %137 = OpULessThan %bool %uint_0 %136
+; CHECK: OpSelectionMerge %138 None
+; CHECK: OpBranchConditional %137 %139 %140
+; CHECK: %139 = OpLabel
+; CHECK: %141 = OpLoad %16 %33
+; CHECK: %142 = OpSampledImage %26 %141 %35
+; CHECK: %143 = OpImageSampleImplicitLod %v4float %142 %30
+; CHECK: OpBranch %138
+; CHECK: %140 = OpLabel
+; CHECK: %144 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_59 %uint_1 %32 %uint_0
+; CHECK: OpBranch %138
+; CHECK: %138 = OpLabel
+; CHECK: %145 = OpPhi %v4float %143 %139 %116 %140
+; CHECK: OpBranch %58
+; CHECK: %60 = OpLabel
+; CHECK: %115 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_59 %uint_0 %32 %55
+; CHECK: OpBranch %58
+; CHECK: %58 = OpLabel
+; CHECK: %117 = OpPhi %v4float %145 %138 %116 %60
+; CHECK: OpStore %_entryPointOutput_vColor %117
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%MainPs = OpFunction %void None %10
-%29 = OpLabel
-%30 = OpLoad %v2float %i_vTextureCoords
-%31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
-%32 = OpLoad %uint %31
-%33 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %32
-%34 = OpLoad %16 %33
-%35 = OpLoad %24 %g_sAniso
-%36 = OpSampledImage %26 %34 %35
-%55 = OpFunctionCall %uint %40 %uint_2 %uint_2
-%57 = OpULessThan %bool %32 %55
-OpSelectionMerge %58 None
-OpBranchConditional %57 %59 %60
-%59 = OpLabel
-%61 = OpLoad %16 %33
-%62 = OpSampledImage %26 %61 %35
-%136 = OpFunctionCall %uint %118 %uint_0 %uint_1 %uint_2 %32
-%137 = OpULessThan %bool %uint_0 %136
-OpSelectionMerge %138 None
-OpBranchConditional %137 %139 %140
-%139 = OpLabel
-%141 = OpLoad %16 %33
-%142 = OpSampledImage %26 %141 %35
-%143 = OpImageSampleImplicitLod %v4float %142 %30
-OpBranch %138
-%140 = OpLabel
-%144 = OpFunctionCall %void %64 %uint_59 %uint_1 %32 %uint_0
-OpBranch %138
-%138 = OpLabel
-%145 = OpPhi %v4float %143 %139 %116 %140
-OpBranch %58
-%60 = OpLabel
-%115 = OpFunctionCall %void %64 %uint_59 %uint_0 %32 %55
-OpBranch %58
-%58 = OpLabel
-%117 = OpPhi %v4float %145 %138 %116 %60
-OpStore %_entryPointOutput_vColor %117
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%40 = OpFunction %uint None %41
-%42 = OpFunctionParameter %uint
-%43 = OpFunctionParameter %uint
-%44 = OpLabel
-%50 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %42
-%51 = OpLoad %uint %50
-%52 = OpIAdd %uint %51 %43
-%53 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %52
-%54 = OpLoad %uint %53
-OpReturnValue %54
-OpFunctionEnd
-%64 = OpFunction %void None %65
-%66 = OpFunctionParameter %uint
-%67 = OpFunctionParameter %uint
-%68 = OpFunctionParameter %uint
-%69 = OpFunctionParameter %uint
-%70 = OpLabel
-%74 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_0
-%77 = OpAtomicIAdd %uint %74 %uint_4 %uint_0 %uint_10
-%78 = OpIAdd %uint %77 %uint_10
-%79 = OpArrayLength %uint %73 1
-%80 = OpULessThanEqual %bool %78 %79
-OpSelectionMerge %81 None
-OpBranchConditional %80 %82 %81
-%82 = OpLabel
-%83 = OpIAdd %uint %77 %uint_0
-%84 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %83
-OpStore %84 %uint_10
-%86 = OpIAdd %uint %77 %uint_1
-%87 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %86
-OpStore %87 %uint_23
-%88 = OpIAdd %uint %77 %uint_2
-%89 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %88
-OpStore %89 %66
-%91 = OpIAdd %uint %77 %uint_3
-%92 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %91
-OpStore %92 %uint_4
-%95 = OpLoad %v4float %gl_FragCoord
-%97 = OpBitcast %v4uint %95
-%98 = OpCompositeExtract %uint %97 0
-%99 = OpIAdd %uint %77 %uint_4
-%100 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %99
-OpStore %100 %98
-%101 = OpCompositeExtract %uint %97 1
-%103 = OpIAdd %uint %77 %uint_5
-%104 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %103
-OpStore %104 %101
-%106 = OpIAdd %uint %77 %uint_7
-%107 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %106
-OpStore %107 %67
-%109 = OpIAdd %uint %77 %uint_8
-%110 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %109
-OpStore %110 %68
-%112 = OpIAdd %uint %77 %uint_9
-%113 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %112
-OpStore %113 %69
-OpBranch %81
-%81 = OpLabel
-OpReturn
-OpFunctionEnd
-%118 = OpFunction %uint None %119
-%120 = OpFunctionParameter %uint
-%121 = OpFunctionParameter %uint
-%122 = OpFunctionParameter %uint
-%123 = OpFunctionParameter %uint
-%124 = OpLabel
-%125 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %120
-%126 = OpLoad %uint %125
-%127 = OpIAdd %uint %126 %121
-%128 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %127
-%129 = OpLoad %uint %128
-%130 = OpIAdd %uint %129 %122
-%131 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %130
-%132 = OpLoad %uint %131
-%133 = OpIAdd %uint %132 %123
-%134 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %133
-%135 = OpLoad %uint %134
-OpReturnValue %135
-OpFunctionEnd
-)";
+ const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
+ true, 7u, 23u, true, true, false,
+ false, false);
}
TEST_F(InstBindlessTest, InstrumentInitCheckOnScalarDescriptor) {
@@ -2569,11 +1756,14 @@ TEST_F(InstBindlessTest, InstrumentInitCheckOnScalarDescriptor) {
// does not have the extension enabled because it does not contain a
// runtime array. This is the same shader as NoInstrumentNonBindless.
- const std::string defs_before =
- R"(OpCapability Shader
+ // clang-format off
+ const std::string defs = R"(
+OpCapability Shader
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
+; CHECK: OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
OpExecutionMode %MainPs OriginUpperLeft
OpSource HLSL 500
OpName %MainPs "MainPs"
@@ -2587,6 +1777,9 @@ OpDecorate %g_sAniso DescriptorSet 0
OpDecorate %g_sAniso Binding 0
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
+; check: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kInputDecorations + kOutputDecorations + R"(
+; check: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%8 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -2603,86 +1796,35 @@ OpDecorate %_entryPointOutput_vColor Location 0
%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
+; CHECK: %uint = OpTypeInt 32 0
+; CHECK: %uint_0 = OpConstant %uint 0
+; CHECK: %28 = OpTypeFunction %uint %uint %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kInputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %bool = OpTypeBool
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %61 = OpTypeFunction %void %uint %uint %uint %uint
+)" + kOutputGlobals + R"(
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_39 = OpConstant %uint 39
+; CHECK: %113 = OpConstantNull %v4float
)";
+ // clang-format on
- const std::string defs_after =
- R"(OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
-OpExecutionMode %MainPs OriginUpperLeft
-OpSource HLSL 500
-OpName %MainPs "MainPs"
-OpName %g_tColor "g_tColor"
-OpName %g_sAniso "g_sAniso"
-OpName %i_vTextureCoords "i.vTextureCoords"
-OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
-OpDecorate %g_tColor DescriptorSet 0
-OpDecorate %g_tColor Binding 0
-OpDecorate %g_sAniso DescriptorSet 0
-OpDecorate %g_sAniso Binding 0
-OpDecorate %i_vTextureCoords Location 0
-OpDecorate %_entryPointOutput_vColor Location 0
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_35 Block
-OpMemberDecorate %_struct_35 0 Offset 0
-OpDecorate %37 DescriptorSet 7
-OpDecorate %37 Binding 1
-OpDecorate %_struct_67 Block
-OpMemberDecorate %_struct_67 0 Offset 0
-OpMemberDecorate %_struct_67 1 Offset 4
-OpDecorate %69 DescriptorSet 7
-OpDecorate %69 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%8 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v2float = OpTypeVector %float 2
-%v4float = OpTypeVector %float 4
-%12 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
-%g_tColor = OpVariable %_ptr_UniformConstant_12 UniformConstant
-%14 = OpTypeSampler
-%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14
-%g_sAniso = OpVariable %_ptr_UniformConstant_14 UniformConstant
-%16 = OpTypeSampledImage %12
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
-%uint = OpTypeInt 32 0
-%uint_0 = OpConstant %uint 0
-%28 = OpTypeFunction %uint %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_35 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_35 = OpTypePointer StorageBuffer %_struct_35
-%37 = OpVariable %_ptr_StorageBuffer__struct_35 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%uint_1 = OpConstant %uint 1
-%61 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_67 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_67 = OpTypePointer StorageBuffer %_struct_67
-%69 = OpVariable %_ptr_StorageBuffer__struct_67 StorageBuffer
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_39 = OpConstant %uint 39
-%113 = OpConstantNull %v4float
-)";
-
- const std::string func_before =
- R"(%MainPs = OpFunction %void None %8
+ const std::string main_func = R"(
+%MainPs = OpFunction %void None %8
%19 = OpLabel
%20 = OpLoad %v2float %i_vTextureCoords
%21 = OpLoad %12 %g_tColor
@@ -2690,111 +1832,33 @@ OpDecorate %gl_FragCoord BuiltIn FragCoord
%23 = OpSampledImage %16 %21 %22
%24 = OpImageSampleImplicitLod %v4float %23 %20
OpStore %_entryPointOutput_vColor %24
+; CHECK-NOT: %24 = OpImageSampleImplicitLod %v4float %23 %20
+; CHECK-NOT: OpStore %_entryPointOutput_vColor %24
+; CHECK: %50 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
+; CHECK: %52 = OpULessThan %bool %uint_0 %50
+; CHECK: OpSelectionMerge %54 None
+; CHECK: OpBranchConditional %52 %55 %56
+; CHECK: %55 = OpLabel
+; CHECK: %57 = OpLoad %12 %g_tColor
+; CHECK: %58 = OpSampledImage %16 %57 %22
+; CHECK: %59 = OpImageSampleImplicitLod %v4float %58 %20
+; CHECK: OpBranch %54
+; CHECK: %56 = OpLabel
+; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_39 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %54
+; CHECK: %54 = OpLabel
+; CHECK: %114 = OpPhi %v4float %59 %55 %113 %56
+; CHECK: OpStore %_entryPointOutput_vColor %114
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%MainPs = OpFunction %void None %8
-%19 = OpLabel
-%20 = OpLoad %v2float %i_vTextureCoords
-%21 = OpLoad %12 %g_tColor
-%22 = OpLoad %14 %g_sAniso
-%23 = OpSampledImage %16 %21 %22
-%50 = OpFunctionCall %uint %27 %uint_0 %uint_0 %uint_0 %uint_0
-%52 = OpULessThan %bool %uint_0 %50
-OpSelectionMerge %54 None
-OpBranchConditional %52 %55 %56
-%55 = OpLabel
-%57 = OpLoad %12 %g_tColor
-%58 = OpSampledImage %16 %57 %22
-%59 = OpImageSampleImplicitLod %v4float %58 %20
-OpBranch %54
-%56 = OpLabel
-%112 = OpFunctionCall %void %60 %uint_39 %uint_1 %uint_0 %uint_0
-OpBranch %54
-%54 = OpLabel
-%114 = OpPhi %v4float %59 %55 %113 %56
-OpStore %_entryPointOutput_vColor %114
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%27 = OpFunction %uint None %28
-%29 = OpFunctionParameter %uint
-%30 = OpFunctionParameter %uint
-%31 = OpFunctionParameter %uint
-%32 = OpFunctionParameter %uint
-%33 = OpLabel
-%39 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %29
-%40 = OpLoad %uint %39
-%41 = OpIAdd %uint %40 %30
-%42 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %41
-%43 = OpLoad %uint %42
-%44 = OpIAdd %uint %43 %31
-%45 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %44
-%46 = OpLoad %uint %45
-%47 = OpIAdd %uint %46 %32
-%48 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %47
-%49 = OpLoad %uint %48
-OpReturnValue %49
-OpFunctionEnd
-%60 = OpFunction %void None %61
-%62 = OpFunctionParameter %uint
-%63 = OpFunctionParameter %uint
-%64 = OpFunctionParameter %uint
-%65 = OpFunctionParameter %uint
-%66 = OpLabel
-%70 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_0
-%73 = OpAtomicIAdd %uint %70 %uint_4 %uint_0 %uint_10
-%74 = OpIAdd %uint %73 %uint_10
-%75 = OpArrayLength %uint %69 1
-%76 = OpULessThanEqual %bool %74 %75
-OpSelectionMerge %77 None
-OpBranchConditional %76 %78 %77
-%78 = OpLabel
-%79 = OpIAdd %uint %73 %uint_0
-%80 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %79
-OpStore %80 %uint_10
-%82 = OpIAdd %uint %73 %uint_1
-%83 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %82
-OpStore %83 %uint_23
-%85 = OpIAdd %uint %73 %uint_2
-%86 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %85
-OpStore %86 %62
-%88 = OpIAdd %uint %73 %uint_3
-%89 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %88
-OpStore %89 %uint_4
-%92 = OpLoad %v4float %gl_FragCoord
-%94 = OpBitcast %v4uint %92
-%95 = OpCompositeExtract %uint %94 0
-%96 = OpIAdd %uint %73 %uint_4
-%97 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %96
-OpStore %97 %95
-%98 = OpCompositeExtract %uint %94 1
-%100 = OpIAdd %uint %73 %uint_5
-%101 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %100
-OpStore %101 %98
-%103 = OpIAdd %uint %73 %uint_7
-%104 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %103
-OpStore %104 %63
-%106 = OpIAdd %uint %73 %uint_8
-%107 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %106
-OpStore %107 %64
-%109 = OpIAdd %uint %73 %uint_9
-%110 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %109
-OpStore %110 %65
-OpBranch %77
-%77 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
+ const std::string new_funcs = kDirectRead4 + kStreamWrite4Frag;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
+ true, 7u, 23u, true, true, false,
+ false, false);
}
TEST_F(InstBindlessTest, SPV14AddToEntryPoint) {
@@ -2927,15 +1991,18 @@ TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedUBOArray) {
// b = uniformBuffer[nu_ii].a;
// }
- const std::string defs_before =
- R"(OpCapability Shader
+ // clang-format off
+ const std::string defs = R"(
+OpCapability Shader
OpCapability ShaderNonUniform
OpCapability RuntimeDescriptorArray
OpCapability UniformBufferArrayNonUniformIndexing
OpExtension "SPV_EXT_descriptor_indexing"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %b %nu_ii
+; CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpSourceExtension "GL_EXT_nonuniform_qualifier"
@@ -2955,6 +2022,12 @@ OpDecorate %nu_ii Location 0
OpDecorate %nu_ii NonUniform
OpDecorate %16 NonUniform
OpDecorate %20 NonUniform
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kInputDecorations + R"(
+; CHECK: OpDecorate %130 NonUniform
+)" + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+; CHECK: OpDecorate %127 NonUniform
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -2969,229 +2042,81 @@ OpDecorate %20 NonUniform
%nu_ii = OpVariable %_ptr_Input_int Input
%int_0 = OpConstant %int 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
+; CHECK: %uint = OpTypeInt 32 0
+; CHECK: %uint_0 = OpConstant %uint 0
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %26 = OpTypeFunction %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kInputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %bool = OpTypeBool
+; CHECK: %49 = OpTypeFunction %void %uint %uint %uint %uint
+)" + kOutputGlobals + R"(
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %v4float = OpTypeVector %float 4
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_45 = OpConstant %uint 45
+; CHECK: %101 = OpConstantNull %float
+; CHECK: %105 = OpTypeFunction %uint %uint %uint %uint %uint
)";
+ // clang-format on
- const std::string defs_after =
- R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability RuntimeDescriptorArray
-OpCapability UniformBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %b "b"
-OpName %uname "uname"
-OpMemberName %uname 0 "a"
-OpName %uniformBuffer "uniformBuffer"
-OpName %nu_ii "nu_ii"
-OpDecorate %b Location 0
-OpMemberDecorate %uname 0 Offset 0
-OpDecorate %uname Block
-OpDecorate %uniformBuffer DescriptorSet 0
-OpDecorate %uniformBuffer Binding 3
-OpDecorate %nu_ii Flat
-OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniform
-OpDecorate %7 NonUniform
-OpDecorate %102 NonUniform
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_31 Block
-OpMemberDecorate %_struct_31 0 Offset 0
-OpDecorate %33 DescriptorSet 7
-OpDecorate %33 Binding 1
-OpDecorate %130 NonUniform
-OpDecorate %_struct_55 Block
-OpMemberDecorate %_struct_55 0 Offset 0
-OpMemberDecorate %_struct_55 1 Offset 4
-OpDecorate %57 DescriptorSet 7
-OpDecorate %57 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-OpDecorate %127 NonUniform
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Output_float = OpTypePointer Output %float
-%b = OpVariable %_ptr_Output_float Output
-%uname = OpTypeStruct %float
-%_runtimearr_uname = OpTypeRuntimeArray %uname
-%_ptr_Uniform__runtimearr_uname = OpTypePointer Uniform %_runtimearr_uname
-%uniformBuffer = OpVariable %_ptr_Uniform__runtimearr_uname Uniform
-%int = OpTypeInt 32 1
-%_ptr_Input_int = OpTypePointer Input %int
-%nu_ii = OpVariable %_ptr_Input_int Input
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%uint = OpTypeInt 32 0
-%uint_0 = OpConstant %uint 0
-%uint_1 = OpConstant %uint 1
-%uint_3 = OpConstant %uint 3
-%26 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_31 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31
-%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%49 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_55 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55
-%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_45 = OpConstant %uint 45
-%101 = OpConstantNull %float
-%105 = OpTypeFunction %uint %uint %uint %uint %uint
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %3
+ const std::string main_func = R"(
+%main = OpFunction %void None %3
%5 = OpLabel
%16 = OpLoad %int %nu_ii
%19 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %16 %int_0
%20 = OpLoad %float %19
OpStore %b %20
+; CHECK-NOT: %20 = OpLoad %float %19
+; CHECK-NOT: OpStore %b %20
+; CHECK: %40 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_3
+; CHECK: %42 = OpULessThan %bool %7 %40
+; CHECK: OpSelectionMerge %43 None
+; CHECK: OpBranchConditional %42 %44 %45
+; CHECK: %44 = OpLabel
+; CHECK: %103 = OpBitcast %uint %7
+; CHECK: %122 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %103
+; CHECK: %123 = OpULessThan %bool %uint_0 %122
+; CHECK: OpSelectionMerge %124 None
+; CHECK: OpBranchConditional %123 %125 %126
+; CHECK: %125 = OpLabel
+; CHECK: %127 = OpLoad %float %20
+; CHECK: OpBranch %124
+; CHECK: %126 = OpLabel
+; CHECK: %128 = OpBitcast %uint %7
+; CHECK: %129 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_1 %128 %uint_0
+; CHECK: OpBranch %124
+; CHECK: %124 = OpLabel
+; CHECK: %130 = OpPhi %float %127 %125 %101 %126
+; CHECK: OpBranch %43
+; CHECK: %45 = OpLabel
+; CHECK: %47 = OpBitcast %uint %7
+; CHECK: %100 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_0 %47 %40
+; CHECK: OpBranch %43
+; CHECK: %43 = OpLabel
+; CHECK: %102 = OpPhi %float %130 %124 %101 %45
+; CHECK: OpStore %b %102
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %10
-%19 = OpLabel
-%7 = OpLoad %int %nu_ii
-%20 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %7 %int_0
-%40 = OpFunctionCall %uint %25 %uint_1 %uint_3
-%42 = OpULessThan %bool %7 %40
-OpSelectionMerge %43 None
-OpBranchConditional %42 %44 %45
-%44 = OpLabel
-%103 = OpBitcast %uint %7
-%122 = OpFunctionCall %uint %104 %uint_0 %uint_0 %uint_3 %103
-%123 = OpULessThan %bool %uint_0 %122
-OpSelectionMerge %124 None
-OpBranchConditional %123 %125 %126
-%125 = OpLabel
-%127 = OpLoad %float %20
-OpBranch %124
-%126 = OpLabel
-%128 = OpBitcast %uint %7
-%129 = OpFunctionCall %void %48 %uint_45 %uint_1 %128 %uint_0
-OpBranch %124
-%124 = OpLabel
-%130 = OpPhi %float %127 %125 %101 %126
-OpBranch %43
-%45 = OpLabel
-%47 = OpBitcast %uint %7
-%100 = OpFunctionCall %void %48 %uint_45 %uint_0 %47 %40
-OpBranch %43
-%43 = OpLabel
-%102 = OpPhi %float %130 %124 %101 %45
-OpStore %b %102
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%25 = OpFunction %uint None %26
-%27 = OpFunctionParameter %uint
-%28 = OpFunctionParameter %uint
-%29 = OpLabel
-%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27
-%36 = OpLoad %uint %35
-%37 = OpIAdd %uint %36 %28
-%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37
-%39 = OpLoad %uint %38
-OpReturnValue %39
-OpFunctionEnd
-%48 = OpFunction %void None %49
-%50 = OpFunctionParameter %uint
-%51 = OpFunctionParameter %uint
-%52 = OpFunctionParameter %uint
-%53 = OpFunctionParameter %uint
-%54 = OpLabel
-%58 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0
-%61 = OpAtomicIAdd %uint %58 %uint_4 %uint_0 %uint_10
-%62 = OpIAdd %uint %61 %uint_10
-%63 = OpArrayLength %uint %57 1
-%64 = OpULessThanEqual %bool %62 %63
-OpSelectionMerge %65 None
-OpBranchConditional %64 %66 %65
-%66 = OpLabel
-%67 = OpIAdd %uint %61 %uint_0
-%68 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %67
-OpStore %68 %uint_10
-%70 = OpIAdd %uint %61 %uint_1
-%71 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %70
-OpStore %71 %uint_23
-%73 = OpIAdd %uint %61 %uint_2
-%74 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %73
-OpStore %74 %50
-%75 = OpIAdd %uint %61 %uint_3
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75
-OpStore %76 %uint_4
-%80 = OpLoad %v4float %gl_FragCoord
-%82 = OpBitcast %v4uint %80
-%83 = OpCompositeExtract %uint %82 0
-%84 = OpIAdd %uint %61 %uint_4
-%85 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %84
-OpStore %85 %83
-%86 = OpCompositeExtract %uint %82 1
-%88 = OpIAdd %uint %61 %uint_5
-%89 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %88
-OpStore %89 %86
-%91 = OpIAdd %uint %61 %uint_7
-%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91
-OpStore %92 %51
-%94 = OpIAdd %uint %61 %uint_8
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94
-OpStore %95 %52
-%97 = OpIAdd %uint %61 %uint_9
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %97
-OpStore %98 %53
-OpBranch %65
-%65 = OpLabel
-OpReturn
-OpFunctionEnd
-%104 = OpFunction %uint None %105
-%106 = OpFunctionParameter %uint
-%107 = OpFunctionParameter %uint
-%108 = OpFunctionParameter %uint
-%109 = OpFunctionParameter %uint
-%110 = OpLabel
-%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %106
-%112 = OpLoad %uint %111
-%113 = OpIAdd %uint %112 %107
-%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113
-%115 = OpLoad %uint %114
-%116 = OpIAdd %uint %115 %108
-%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116
-%118 = OpLoad %uint %117
-%119 = OpIAdd %uint %118 %109
-%120 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %119
-%121 = OpLoad %uint %120
-OpReturnValue %121
-OpFunctionEnd
-)";
+ const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
+ true, 7u, 23u, true, true, false,
+ false, false);
}
TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayDeprecated) {
@@ -3208,15 +2133,18 @@ TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayDeprecated) {
// b = storageBuffer[nu_ii].b;
// }
- const std::string defs_before =
- R"(OpCapability Shader
+ // clang-format off
+ const std::string defs = R"(
+OpCapability Shader
OpCapability ShaderNonUniform
OpCapability RuntimeDescriptorArray
OpCapability StorageBufferArrayNonUniformIndexing
OpExtension "SPV_EXT_descriptor_indexing"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %b %nu_ii
+; CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpSourceExtension "GL_EXT_nonuniform_qualifier"
@@ -3236,6 +2164,12 @@ OpDecorate %nu_ii Location 0
OpDecorate %nu_ii NonUniform
OpDecorate %16 NonUniform
OpDecorate %20 NonUniform
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kInputDecorations + R"(
+; CHECK: OpDecorate %130 NonUniform
+)" + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+; CHECK: OpDecorate %127 NonUniform
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -3250,243 +2184,98 @@ OpDecorate %20 NonUniform
%nu_ii = OpVariable %_ptr_Input_int Input
%int_0 = OpConstant %int 0
%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
+; CHECK: %uint = OpTypeInt 32 0
+; CHECK: %uint_0 = OpConstant %uint 0
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %26 = OpTypeFunction %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kInputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %bool = OpTypeBool
+; CHECK: %49 = OpTypeFunction %void %uint %uint %uint %uint
+)" + kOutputGlobals + R"(
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %v4float = OpTypeVector %float 4
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_45 = OpConstant %uint 45
+; CHECK: %101 = OpConstantNull %float
+; CHECK: %105 = OpTypeFunction %uint %uint %uint %uint %uint
)";
+ // clang-format on
- const std::string defs_after =
- R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability RuntimeDescriptorArray
-OpCapability StorageBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %b "b"
-OpName %bname "bname"
-OpMemberName %bname 0 "a"
-OpName %storageBuffer "storageBuffer"
-OpName %nu_ii "nu_ii"
-OpDecorate %b Location 0
-OpMemberDecorate %bname 0 Offset 0
-OpDecorate %bname Block
-OpDecorate %storageBuffer DescriptorSet 0
-OpDecorate %storageBuffer Binding 3
-OpDecorate %nu_ii Flat
-OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniform
-OpDecorate %7 NonUniform
-OpDecorate %102 NonUniform
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_31 Block
-OpMemberDecorate %_struct_31 0 Offset 0
-OpDecorate %33 DescriptorSet 7
-OpDecorate %33 Binding 1
-OpDecorate %130 NonUniform
-OpDecorate %_struct_55 Block
-OpMemberDecorate %_struct_55 0 Offset 0
-OpMemberDecorate %_struct_55 1 Offset 4
-OpDecorate %57 DescriptorSet 7
-OpDecorate %57 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-OpDecorate %127 NonUniform
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Output_float = OpTypePointer Output %float
-%b = OpVariable %_ptr_Output_float Output
-%bname = OpTypeStruct %float
-%_runtimearr_bname = OpTypeRuntimeArray %bname
-%_ptr_StorageBuffer__runtimearr_bname = OpTypePointer StorageBuffer %_runtimearr_bname
-%storageBuffer = OpVariable %_ptr_StorageBuffer__runtimearr_bname StorageBuffer
-%int = OpTypeInt 32 1
-%_ptr_Input_int = OpTypePointer Input %int
-%nu_ii = OpVariable %_ptr_Input_int Input
-%int_0 = OpConstant %int 0
-%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
-%uint = OpTypeInt 32 0
-%uint_0 = OpConstant %uint 0
-%uint_1 = OpConstant %uint 1
-%uint_3 = OpConstant %uint 3
-%26 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_31 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31
-%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%49 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_55 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55
-%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_45 = OpConstant %uint 45
-%101 = OpConstantNull %float
-%105 = OpTypeFunction %uint %uint %uint %uint %uint
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %3
+ const std::string main_func = R"(
+%main = OpFunction %void None %3
%5 = OpLabel
%16 = OpLoad %int %nu_ii
%19 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %16 %int_0
%20 = OpLoad %float %19
OpStore %b %20
+; CHECK-NOT: %20 = OpLoad %float %19
+; CHECK-NOT: OpStore %b %20
+; CHECK: %40 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_3
+; CHECK: %42 = OpULessThan %bool %7 %40
+; CHECK: OpSelectionMerge %43 None
+; CHECK: OpBranchConditional %42 %44 %45
+; CHECK: %44 = OpLabel
+; CHECK: %103 = OpBitcast %uint %7
+; CHECK: %122 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %103
+; CHECK: %123 = OpULessThan %bool %uint_0 %122
+; CHECK: OpSelectionMerge %124 None
+; CHECK: OpBranchConditional %123 %125 %126
+; CHECK: %125 = OpLabel
+; CHECK: %127 = OpLoad %float %20
+; CHECK: OpBranch %124
+; CHECK: %126 = OpLabel
+; CHECK: %128 = OpBitcast %uint %7
+; CHECK: %129 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_1 %128 %uint_0
+; CHECK: OpBranch %124
+; CHECK: %124 = OpLabel
+; CHECK: %130 = OpPhi %float %127 %125 %101 %126
+; CHECK: OpBranch %43
+; CHECK: %45 = OpLabel
+; CHECK: %47 = OpBitcast %uint %7
+; CHECK: %100 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_0 %47 %40
+; CHECK: OpBranch %43
+; CHECK: %43 = OpLabel
+; CHECK: %102 = OpPhi %float %130 %124 %101 %45
+; CHECK: OpStore %b %102
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %10
-%19 = OpLabel
-%7 = OpLoad %int %nu_ii
-%20 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %7 %int_0
-%40 = OpFunctionCall %uint %25 %uint_1 %uint_3
-%42 = OpULessThan %bool %7 %40
-OpSelectionMerge %43 None
-OpBranchConditional %42 %44 %45
-%44 = OpLabel
-%103 = OpBitcast %uint %7
-%122 = OpFunctionCall %uint %104 %uint_0 %uint_0 %uint_3 %103
-%123 = OpULessThan %bool %uint_0 %122
-OpSelectionMerge %124 None
-OpBranchConditional %123 %125 %126
-%125 = OpLabel
-%127 = OpLoad %float %20
-OpBranch %124
-%126 = OpLabel
-%128 = OpBitcast %uint %7
-%129 = OpFunctionCall %void %48 %uint_45 %uint_1 %128 %uint_0
-OpBranch %124
-%124 = OpLabel
-%130 = OpPhi %float %127 %125 %101 %126
-OpBranch %43
-%45 = OpLabel
-%47 = OpBitcast %uint %7
-%100 = OpFunctionCall %void %48 %uint_45 %uint_0 %47 %40
-OpBranch %43
-%43 = OpLabel
-%102 = OpPhi %float %130 %124 %101 %45
-OpStore %b %102
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%25 = OpFunction %uint None %26
-%27 = OpFunctionParameter %uint
-%28 = OpFunctionParameter %uint
-%29 = OpLabel
-%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27
-%36 = OpLoad %uint %35
-%37 = OpIAdd %uint %36 %28
-%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37
-%39 = OpLoad %uint %38
-OpReturnValue %39
-OpFunctionEnd
-%48 = OpFunction %void None %49
-%50 = OpFunctionParameter %uint
-%51 = OpFunctionParameter %uint
-%52 = OpFunctionParameter %uint
-%53 = OpFunctionParameter %uint
-%54 = OpLabel
-%58 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0
-%61 = OpAtomicIAdd %uint %58 %uint_4 %uint_0 %uint_10
-%62 = OpIAdd %uint %61 %uint_10
-%63 = OpArrayLength %uint %57 1
-%64 = OpULessThanEqual %bool %62 %63
-OpSelectionMerge %65 None
-OpBranchConditional %64 %66 %65
-%66 = OpLabel
-%67 = OpIAdd %uint %61 %uint_0
-%68 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %67
-OpStore %68 %uint_10
-%70 = OpIAdd %uint %61 %uint_1
-%71 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %70
-OpStore %71 %uint_23
-%73 = OpIAdd %uint %61 %uint_2
-%74 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %73
-OpStore %74 %50
-%75 = OpIAdd %uint %61 %uint_3
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75
-OpStore %76 %uint_4
-%80 = OpLoad %v4float %gl_FragCoord
-%82 = OpBitcast %v4uint %80
-%83 = OpCompositeExtract %uint %82 0
-%84 = OpIAdd %uint %61 %uint_4
-%85 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %84
-OpStore %85 %83
-%86 = OpCompositeExtract %uint %82 1
-%88 = OpIAdd %uint %61 %uint_5
-%89 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %88
-OpStore %89 %86
-%91 = OpIAdd %uint %61 %uint_7
-%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91
-OpStore %92 %51
-%94 = OpIAdd %uint %61 %uint_8
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94
-OpStore %95 %52
-%97 = OpIAdd %uint %61 %uint_9
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %97
-OpStore %98 %53
-OpBranch %65
-%65 = OpLabel
-OpReturn
-OpFunctionEnd
-%104 = OpFunction %uint None %105
-%106 = OpFunctionParameter %uint
-%107 = OpFunctionParameter %uint
-%108 = OpFunctionParameter %uint
-%109 = OpFunctionParameter %uint
-%110 = OpLabel
-%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %106
-%112 = OpLoad %uint %111
-%113 = OpIAdd %uint %112 %107
-%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113
-%115 = OpLoad %uint %114
-%116 = OpIAdd %uint %115 %108
-%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116
-%118 = OpLoad %uint %117
-%119 = OpIAdd %uint %118 %109
-%120 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %119
-%121 = OpLoad %uint %120
-OpReturnValue %121
-OpFunctionEnd
-)";
+ const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
+ true, 7u, 23u, true, true, false,
+ false, false);
}
TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArray) {
// Same as Deprecated but declaring as StorageBuffer Block
- const std::string defs_before =
- R"(OpCapability Shader
+ // clang-format off
+ const std::string defs = R"(
+OpCapability Shader
OpCapability ShaderNonUniform
OpCapability RuntimeDescriptorArray
OpCapability StorageBufferArrayNonUniformIndexing
OpExtension "SPV_EXT_descriptor_indexing"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %b %nu_ii
+; CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpSourceExtension "GL_EXT_nonuniform_qualifier"
@@ -3506,6 +2295,12 @@ OpDecorate %nu_ii Location 0
OpDecorate %nu_ii NonUniform
OpDecorate %16 NonUniform
OpDecorate %20 NonUniform
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kInputDecorations + R"(
+; CHECK: OpDecorate %130 NonUniform
+)" + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+; CHECK: OpDecorate %127 NonUniform
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -3520,229 +2315,81 @@ OpDecorate %20 NonUniform
%nu_ii = OpVariable %_ptr_Input_int Input
%int_0 = OpConstant %int 0
%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
+; CHECK: %uint = OpTypeInt 32 0
+; CHECK: %uint_0 = OpConstant %uint 0
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %26 = OpTypeFunction %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kInputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %bool = OpTypeBool
+; CHECK: %49 = OpTypeFunction %void %uint %uint %uint %uint
+)" + kOutputGlobals + R"(
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %v4float = OpTypeVector %float 4
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_45 = OpConstant %uint 45
+; CHECK: %101 = OpConstantNull %float
+; CHECK: %105 = OpTypeFunction %uint %uint %uint %uint %uint
)";
+ // clang-format on
- const std::string defs_after =
- R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability RuntimeDescriptorArray
-OpCapability StorageBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %b "b"
-OpName %bname "bname"
-OpMemberName %bname 0 "a"
-OpName %storageBuffer "storageBuffer"
-OpName %nu_ii "nu_ii"
-OpDecorate %b Location 0
-OpMemberDecorate %bname 0 Offset 0
-OpDecorate %bname Block
-OpDecorate %storageBuffer DescriptorSet 0
-OpDecorate %storageBuffer Binding 3
-OpDecorate %nu_ii Flat
-OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniform
-OpDecorate %7 NonUniform
-OpDecorate %102 NonUniform
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_31 Block
-OpMemberDecorate %_struct_31 0 Offset 0
-OpDecorate %33 DescriptorSet 7
-OpDecorate %33 Binding 1
-OpDecorate %130 NonUniform
-OpDecorate %_struct_55 Block
-OpMemberDecorate %_struct_55 0 Offset 0
-OpMemberDecorate %_struct_55 1 Offset 4
-OpDecorate %57 DescriptorSet 7
-OpDecorate %57 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-OpDecorate %127 NonUniform
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Output_float = OpTypePointer Output %float
-%b = OpVariable %_ptr_Output_float Output
-%bname = OpTypeStruct %float
-%_runtimearr_bname = OpTypeRuntimeArray %bname
-%_ptr_StorageBuffer__runtimearr_bname = OpTypePointer StorageBuffer %_runtimearr_bname
-%storageBuffer = OpVariable %_ptr_StorageBuffer__runtimearr_bname StorageBuffer
-%int = OpTypeInt 32 1
-%_ptr_Input_int = OpTypePointer Input %int
-%nu_ii = OpVariable %_ptr_Input_int Input
-%int_0 = OpConstant %int 0
-%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
-%uint = OpTypeInt 32 0
-%uint_0 = OpConstant %uint 0
-%uint_1 = OpConstant %uint 1
-%uint_3 = OpConstant %uint 3
-%26 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_31 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31
-%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%49 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_55 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55
-%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_45 = OpConstant %uint 45
-%101 = OpConstantNull %float
-%105 = OpTypeFunction %uint %uint %uint %uint %uint
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %3
+ const std::string main_func = R"(
+%main = OpFunction %void None %3
%5 = OpLabel
%16 = OpLoad %int %nu_ii
%19 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %16 %int_0
%20 = OpLoad %float %19
OpStore %b %20
+; CHECK-NOT: %20 = OpLoad %float %19
+; CHECK-NOT: OpStore %b %20
+; CHECK: %40 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_3
+; CHECK: %42 = OpULessThan %bool %7 %40
+; CHECK: OpSelectionMerge %43 None
+; CHECK: OpBranchConditional %42 %44 %45
+; CHECK: %44 = OpLabel
+; CHECK: %103 = OpBitcast %uint %7
+; CHECK: %122 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %103
+; CHECK: %123 = OpULessThan %bool %uint_0 %122
+; CHECK: OpSelectionMerge %124 None
+; CHECK: OpBranchConditional %123 %125 %126
+; CHECK: %125 = OpLabel
+; CHECK: %127 = OpLoad %float %20
+; CHECK: OpBranch %124
+; CHECK: %126 = OpLabel
+; CHECK: %128 = OpBitcast %uint %7
+; CHECK: %129 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_1 %128 %uint_0
+; CHECK: OpBranch %124
+; CHECK: %124 = OpLabel
+; CHECK: %130 = OpPhi %float %127 %125 %101 %126
+; CHECK: OpBranch %43
+; CHECK: %45 = OpLabel
+; CHECK: %47 = OpBitcast %uint %7
+; CHECK: %100 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_0 %47 %40
+; CHECK: OpBranch %43
+; CHECK: %43 = OpLabel
+; CHECK: %102 = OpPhi %float %130 %124 %101 %45
+; CHECK: OpStore %b %102
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %10
-%19 = OpLabel
-%7 = OpLoad %int %nu_ii
-%20 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %7 %int_0
-%40 = OpFunctionCall %uint %25 %uint_1 %uint_3
-%42 = OpULessThan %bool %7 %40
-OpSelectionMerge %43 None
-OpBranchConditional %42 %44 %45
-%44 = OpLabel
-%103 = OpBitcast %uint %7
-%122 = OpFunctionCall %uint %104 %uint_0 %uint_0 %uint_3 %103
-%123 = OpULessThan %bool %uint_0 %122
-OpSelectionMerge %124 None
-OpBranchConditional %123 %125 %126
-%125 = OpLabel
-%127 = OpLoad %float %20
-OpBranch %124
-%126 = OpLabel
-%128 = OpBitcast %uint %7
-%129 = OpFunctionCall %void %48 %uint_45 %uint_1 %128 %uint_0
-OpBranch %124
-%124 = OpLabel
-%130 = OpPhi %float %127 %125 %101 %126
-OpBranch %43
-%45 = OpLabel
-%47 = OpBitcast %uint %7
-%100 = OpFunctionCall %void %48 %uint_45 %uint_0 %47 %40
-OpBranch %43
-%43 = OpLabel
-%102 = OpPhi %float %130 %124 %101 %45
-OpStore %b %102
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%25 = OpFunction %uint None %26
-%27 = OpFunctionParameter %uint
-%28 = OpFunctionParameter %uint
-%29 = OpLabel
-%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27
-%36 = OpLoad %uint %35
-%37 = OpIAdd %uint %36 %28
-%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37
-%39 = OpLoad %uint %38
-OpReturnValue %39
-OpFunctionEnd
-%48 = OpFunction %void None %49
-%50 = OpFunctionParameter %uint
-%51 = OpFunctionParameter %uint
-%52 = OpFunctionParameter %uint
-%53 = OpFunctionParameter %uint
-%54 = OpLabel
-%58 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0
-%61 = OpAtomicIAdd %uint %58 %uint_4 %uint_0 %uint_10
-%62 = OpIAdd %uint %61 %uint_10
-%63 = OpArrayLength %uint %57 1
-%64 = OpULessThanEqual %bool %62 %63
-OpSelectionMerge %65 None
-OpBranchConditional %64 %66 %65
-%66 = OpLabel
-%67 = OpIAdd %uint %61 %uint_0
-%68 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %67
-OpStore %68 %uint_10
-%70 = OpIAdd %uint %61 %uint_1
-%71 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %70
-OpStore %71 %uint_23
-%73 = OpIAdd %uint %61 %uint_2
-%74 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %73
-OpStore %74 %50
-%75 = OpIAdd %uint %61 %uint_3
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75
-OpStore %76 %uint_4
-%80 = OpLoad %v4float %gl_FragCoord
-%82 = OpBitcast %v4uint %80
-%83 = OpCompositeExtract %uint %82 0
-%84 = OpIAdd %uint %61 %uint_4
-%85 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %84
-OpStore %85 %83
-%86 = OpCompositeExtract %uint %82 1
-%88 = OpIAdd %uint %61 %uint_5
-%89 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %88
-OpStore %89 %86
-%91 = OpIAdd %uint %61 %uint_7
-%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91
-OpStore %92 %51
-%94 = OpIAdd %uint %61 %uint_8
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94
-OpStore %95 %52
-%97 = OpIAdd %uint %61 %uint_9
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %97
-OpStore %98 %53
-OpBranch %65
-%65 = OpLabel
-OpReturn
-OpFunctionEnd
-%104 = OpFunction %uint None %105
-%106 = OpFunctionParameter %uint
-%107 = OpFunctionParameter %uint
-%108 = OpFunctionParameter %uint
-%109 = OpFunctionParameter %uint
-%110 = OpLabel
-%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %106
-%112 = OpLoad %uint %111
-%113 = OpIAdd %uint %112 %107
-%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113
-%115 = OpLoad %uint %114
-%116 = OpIAdd %uint %115 %108
-%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116
-%118 = OpLoad %uint %117
-%119 = OpIAdd %uint %118 %109
-%120 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %119
-%121 = OpLoad %uint %120
-OpReturnValue %121
-OpFunctionEnd
-)";
+ const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
+ true, 7u, 23u, true, true, false,
+ false, false);
}
TEST_F(InstBindlessTest, InstInitLoadUBOScalar) {
@@ -3757,12 +2404,15 @@ TEST_F(InstBindlessTest, InstInitLoadUBOScalar) {
// b = uniformBuffer.a;
// }
- const std::string defs_before =
- R"(OpCapability Shader
+ // clang-format off
+ const std::string defs = R"(
+OpCapability Shader
OpExtension "SPV_EXT_descriptor_indexing"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %b
+; CHECK: OpEntryPoint Fragment %main "main" %b %gl_FragCoord
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpSourceExtension "GL_EXT_nonuniform_qualifier"
@@ -3776,6 +2426,9 @@ OpMemberDecorate %uname 0 Offset 0
OpDecorate %uname Block
OpDecorate %uniformBuffer DescriptorSet 0
OpDecorate %uniformBuffer Binding 3
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kInputDecorations + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -3787,187 +2440,68 @@ OpDecorate %uniformBuffer Binding 3
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
+; CHECK: %int = OpTypeInt 32 1
+; CHECK: %int_0 = OpConstant %int 0
+; CHECK: %_ptr_Uniform_float = OpTypePointer Uniform %float
+; CHECK: %uint = OpTypeInt 32 0
+; CHECK: %uint_0 = OpConstant %uint 0
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %21 = OpTypeFunction %uint %uint %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kInputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %bool = OpTypeBool
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %52 = OpTypeFunction %void %uint %uint %uint %uint
+)" + kOutputGlobals + R"(
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %v4float = OpTypeVector %float 4
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_32 = OpConstant %uint 32
+; CHECK: %104 = OpConstantNull %float
)";
+ // clang-format on
- const std::string defs_after =
- R"(OpCapability Shader
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %gl_FragCoord
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %b "b"
-OpName %uname "uname"
-OpMemberName %uname 0 "a"
-OpName %uniformBuffer "uniformBuffer"
-OpDecorate %b Location 0
-OpMemberDecorate %uname 0 Offset 0
-OpDecorate %uname Block
-OpDecorate %uniformBuffer DescriptorSet 0
-OpDecorate %uniformBuffer Binding 3
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_28 Block
-OpMemberDecorate %_struct_28 0 Offset 0
-OpDecorate %30 DescriptorSet 7
-OpDecorate %30 Binding 1
-OpDecorate %_struct_58 Block
-OpMemberDecorate %_struct_58 0 Offset 0
-OpMemberDecorate %_struct_58 1 Offset 4
-OpDecorate %60 DescriptorSet 7
-OpDecorate %60 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%7 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Output_float = OpTypePointer Output %float
-%b = OpVariable %_ptr_Output_float Output
-%uname = OpTypeStruct %float
-%_ptr_Uniform_uname = OpTypePointer Uniform %uname
-%uniformBuffer = OpVariable %_ptr_Uniform_uname Uniform
-%int = OpTypeInt 32 1
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%uint = OpTypeInt 32 0
-%uint_0 = OpConstant %uint 0
-%uint_3 = OpConstant %uint 3
-%21 = OpTypeFunction %uint %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_28 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_28 = OpTypePointer StorageBuffer %_struct_28
-%30 = OpVariable %_ptr_StorageBuffer__struct_28 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%uint_1 = OpConstant %uint 1
-%52 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_58 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_58 = OpTypePointer StorageBuffer %_struct_58
-%60 = OpVariable %_ptr_StorageBuffer__struct_58 StorageBuffer
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_32 = OpConstant %uint 32
-%104 = OpConstantNull %float
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %3
+ const std::string main_func = R"(
+%main = OpFunction %void None %3
%5 = OpLabel
%15 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %int_0
%16 = OpLoad %float %15
OpStore %b %16
+; CHECK-NOT: %16 = OpLoad %float %15
+; CHECK-NOT: OpStore %b %16
+; CHECK: %43 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %uint_0
+; CHECK: %45 = OpULessThan %bool %uint_0 %43
+; CHECK: OpSelectionMerge %47 None
+; CHECK: OpBranchConditional %45 %48 %49
+; CHECK: %48 = OpLabel
+; CHECK: %50 = OpLoad %float %15
+; CHECK: OpBranch %47
+; CHECK: %49 = OpLabel
+; CHECK: %103 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_32 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %47
+; CHECK: %47 = OpLabel
+; CHECK: %105 = OpPhi %float %50 %48 %104 %49
+; CHECK: OpStore %b %105
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %7
-%14 = OpLabel
-%15 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %int_0
-%43 = OpFunctionCall %uint %20 %uint_0 %uint_0 %uint_3 %uint_0
-%45 = OpULessThan %bool %uint_0 %43
-OpSelectionMerge %47 None
-OpBranchConditional %45 %48 %49
-%48 = OpLabel
-%50 = OpLoad %float %15
-OpBranch %47
-%49 = OpLabel
-%103 = OpFunctionCall %void %51 %uint_32 %uint_1 %uint_0 %uint_0
-OpBranch %47
-%47 = OpLabel
-%105 = OpPhi %float %50 %48 %104 %49
-OpStore %b %105
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%20 = OpFunction %uint None %21
-%22 = OpFunctionParameter %uint
-%23 = OpFunctionParameter %uint
-%24 = OpFunctionParameter %uint
-%25 = OpFunctionParameter %uint
-%26 = OpLabel
-%32 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %22
-%33 = OpLoad %uint %32
-%34 = OpIAdd %uint %33 %23
-%35 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %34
-%36 = OpLoad %uint %35
-%37 = OpIAdd %uint %36 %24
-%38 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %37
-%39 = OpLoad %uint %38
-%40 = OpIAdd %uint %39 %25
-%41 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %40
-%42 = OpLoad %uint %41
-OpReturnValue %42
-OpFunctionEnd
-%51 = OpFunction %void None %52
-%53 = OpFunctionParameter %uint
-%54 = OpFunctionParameter %uint
-%55 = OpFunctionParameter %uint
-%56 = OpFunctionParameter %uint
-%57 = OpLabel
-%61 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_0
-%64 = OpAtomicIAdd %uint %61 %uint_4 %uint_0 %uint_10
-%65 = OpIAdd %uint %64 %uint_10
-%66 = OpArrayLength %uint %60 1
-%67 = OpULessThanEqual %bool %65 %66
-OpSelectionMerge %68 None
-OpBranchConditional %67 %69 %68
-%69 = OpLabel
-%70 = OpIAdd %uint %64 %uint_0
-%71 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %70
-OpStore %71 %uint_10
-%73 = OpIAdd %uint %64 %uint_1
-%74 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %73
-OpStore %74 %uint_23
-%76 = OpIAdd %uint %64 %uint_2
-%77 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %76
-OpStore %77 %53
-%78 = OpIAdd %uint %64 %uint_3
-%79 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %78
-OpStore %79 %uint_4
-%83 = OpLoad %v4float %gl_FragCoord
-%85 = OpBitcast %v4uint %83
-%86 = OpCompositeExtract %uint %85 0
-%87 = OpIAdd %uint %64 %uint_4
-%88 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %87
-OpStore %88 %86
-%89 = OpCompositeExtract %uint %85 1
-%91 = OpIAdd %uint %64 %uint_5
-%92 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %91
-OpStore %92 %89
-%94 = OpIAdd %uint %64 %uint_7
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %94
-OpStore %95 %54
-%97 = OpIAdd %uint %64 %uint_8
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %97
-OpStore %98 %55
-%100 = OpIAdd %uint %64 %uint_9
-%101 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %100
-OpStore %101 %56
-OpBranch %68
-%68 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
+ const std::string new_funcs = kDirectRead4 + kStreamWrite4Frag;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
+ true, 7u, 23u, true, true, false,
+ false, false);
}
TEST_F(InstBindlessTest, InstBoundsInitStoreUnsizedSSBOArray) {
@@ -3984,15 +2518,17 @@ TEST_F(InstBindlessTest, InstBoundsInitStoreUnsizedSSBOArray) {
// storageBuffer[nu_ii].b = b;
// }
- const std::string defs_before =
- R"(OpCapability Shader
+ // clang-format off
+ const std::string defs = R"(OpCapability Shader
OpCapability ShaderNonUniform
OpCapability RuntimeDescriptorArray
OpCapability StorageBufferArrayNonUniformIndexing
OpExtension "SPV_EXT_descriptor_indexing"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %nu_ii %b
+; CHECK: OpEntryPoint Fragment %main "main" %nu_ii %b %gl_FragCoord
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpSourceExtension "GL_EXT_nonuniform_qualifier"
@@ -4011,6 +2547,9 @@ OpDecorate %nu_ii Location 0
OpDecorate %nu_ii NonUniform
OpDecorate %14 NonUniform
OpDecorate %b Location 1
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kInputDecorations + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -4025,223 +2564,76 @@ OpDecorate %b Location 1
%_ptr_Input_float = OpTypePointer Input %float
%b = OpVariable %_ptr_Input_float Input
%_ptr_Uniform_float = OpTypePointer Uniform %float
+; CHECK: %uint = OpTypeInt 32 0
+; CHECK: %uint_0 = OpConstant %uint 0
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %26 = OpTypeFunction %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kInputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %bool = OpTypeBool
+; CHECK: %48 = OpTypeFunction %void %uint %uint %uint %uint
+)" + kOutputGlobals + R"(
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %v4float = OpTypeVector %float 4
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_45 = OpConstant %uint 45
+; CHECK: %102 = OpTypeFunction %uint %uint %uint %uint %uint
)";
+ // clang-format on
- const std::string defs_after =
- R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability RuntimeDescriptorArray
-OpCapability StorageBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %nu_ii %b %gl_FragCoord
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %bname "bname"
-OpMemberName %bname 0 "b"
-OpName %storageBuffer "storageBuffer"
-OpName %nu_ii "nu_ii"
-OpName %b "b"
-OpMemberDecorate %bname 0 Offset 0
-OpDecorate %bname BufferBlock
-OpDecorate %storageBuffer DescriptorSet 0
-OpDecorate %storageBuffer Binding 4
-OpDecorate %nu_ii Flat
-OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniform
-OpDecorate %7 NonUniform
-OpDecorate %b Location 1
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_31 Block
-OpMemberDecorate %_struct_31 0 Offset 0
-OpDecorate %33 DescriptorSet 7
-OpDecorate %33 Binding 1
-OpDecorate %_struct_54 Block
-OpMemberDecorate %_struct_54 0 Offset 0
-OpMemberDecorate %_struct_54 1 Offset 4
-OpDecorate %56 DescriptorSet 7
-OpDecorate %56 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-%void = OpTypeVoid
-%9 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%bname = OpTypeStruct %float
-%_runtimearr_bname = OpTypeRuntimeArray %bname
-%_ptr_Uniform__runtimearr_bname = OpTypePointer Uniform %_runtimearr_bname
-%storageBuffer = OpVariable %_ptr_Uniform__runtimearr_bname Uniform
-%int = OpTypeInt 32 1
-%_ptr_Input_int = OpTypePointer Input %int
-%nu_ii = OpVariable %_ptr_Input_int Input
-%int_0 = OpConstant %int 0
-%_ptr_Input_float = OpTypePointer Input %float
-%b = OpVariable %_ptr_Input_float Input
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%uint = OpTypeInt 32 0
-%uint_0 = OpConstant %uint 0
-%uint_1 = OpConstant %uint 1
-%uint_4 = OpConstant %uint 4
-%26 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_31 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31
-%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%48 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_54 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_54 = OpTypePointer StorageBuffer %_struct_54
-%56 = OpVariable %_ptr_StorageBuffer__struct_54 StorageBuffer
-%uint_10 = OpConstant %uint 10
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_45 = OpConstant %uint 45
-%102 = OpTypeFunction %uint %uint %uint %uint %uint
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %3
+ const std::string main_func = R"(
+%main = OpFunction %void None %3
%5 = OpLabel
%14 = OpLoad %int %nu_ii
%18 = OpLoad %float %b
%20 = OpAccessChain %_ptr_Uniform_float %storageBuffer %14 %int_0
OpStore %20 %18
+; CHECK-NOT: OpStore %20 %18
+; CHECK: %40 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_4
+; CHECK: %42 = OpULessThan %bool %7 %40
+; CHECK: OpSelectionMerge %43 None
+; CHECK: OpBranchConditional %42 %44 %45
+; CHECK: %44 = OpLabel
+; CHECK: %100 = OpBitcast %uint %7
+; CHECK: %119 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_4 %100
+; CHECK: %120 = OpULessThan %bool %uint_0 %119
+; CHECK: OpSelectionMerge %121 None
+; CHECK: OpBranchConditional %120 %122 %123
+; CHECK: %122 = OpLabel
+; CHECK: OpStore %20 %19
+; CHECK: OpBranch %121
+; CHECK: %123 = OpLabel
+; CHECK: %124 = OpBitcast %uint %7
+; CHECK: %125 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_1 %124 %uint_0
+; CHECK: OpBranch %121
+; CHECK: %121 = OpLabel
+; CHECK: OpBranch %43
+; CHECK: %45 = OpLabel
+; CHECK: %46 = OpBitcast %uint %7
+; CHECK: %99 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_45 %uint_0 %46 %40
+; CHECK: OpBranch %43
+; CHECK: %43 = OpLabel
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %9
-%18 = OpLabel
-%7 = OpLoad %int %nu_ii
-%19 = OpLoad %float %b
-%20 = OpAccessChain %_ptr_Uniform_float %storageBuffer %7 %int_0
-%40 = OpFunctionCall %uint %25 %uint_1 %uint_4
-%42 = OpULessThan %bool %7 %40
-OpSelectionMerge %43 None
-OpBranchConditional %42 %44 %45
-%44 = OpLabel
-%100 = OpBitcast %uint %7
-%119 = OpFunctionCall %uint %101 %uint_0 %uint_0 %uint_4 %100
-%120 = OpULessThan %bool %uint_0 %119
-OpSelectionMerge %121 None
-OpBranchConditional %120 %122 %123
-%122 = OpLabel
-OpStore %20 %19
-OpBranch %121
-%123 = OpLabel
-%124 = OpBitcast %uint %7
-%125 = OpFunctionCall %void %47 %uint_45 %uint_1 %124 %uint_0
-OpBranch %121
-%121 = OpLabel
-OpBranch %43
-%45 = OpLabel
-%46 = OpBitcast %uint %7
-%99 = OpFunctionCall %void %47 %uint_45 %uint_0 %46 %40
-OpBranch %43
-%43 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%25 = OpFunction %uint None %26
-%27 = OpFunctionParameter %uint
-%28 = OpFunctionParameter %uint
-%29 = OpLabel
-%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27
-%36 = OpLoad %uint %35
-%37 = OpIAdd %uint %36 %28
-%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37
-%39 = OpLoad %uint %38
-OpReturnValue %39
-OpFunctionEnd
-%47 = OpFunction %void None %48
-%49 = OpFunctionParameter %uint
-%50 = OpFunctionParameter %uint
-%51 = OpFunctionParameter %uint
-%52 = OpFunctionParameter %uint
-%53 = OpLabel
-%57 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_0
-%59 = OpAtomicIAdd %uint %57 %uint_4 %uint_0 %uint_10
-%60 = OpIAdd %uint %59 %uint_10
-%61 = OpArrayLength %uint %56 1
-%62 = OpULessThanEqual %bool %60 %61
-OpSelectionMerge %63 None
-OpBranchConditional %62 %64 %63
-%64 = OpLabel
-%65 = OpIAdd %uint %59 %uint_0
-%66 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %65
-OpStore %66 %uint_10
-%68 = OpIAdd %uint %59 %uint_1
-%69 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %68
-OpStore %69 %uint_23
-%71 = OpIAdd %uint %59 %uint_2
-%72 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %71
-OpStore %72 %49
-%74 = OpIAdd %uint %59 %uint_3
-%75 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %74
-OpStore %75 %uint_4
-%79 = OpLoad %v4float %gl_FragCoord
-%81 = OpBitcast %v4uint %79
-%82 = OpCompositeExtract %uint %81 0
-%83 = OpIAdd %uint %59 %uint_4
-%84 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %83
-OpStore %84 %82
-%85 = OpCompositeExtract %uint %81 1
-%87 = OpIAdd %uint %59 %uint_5
-%88 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %87
-OpStore %88 %85
-%90 = OpIAdd %uint %59 %uint_7
-%91 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %90
-OpStore %91 %50
-%93 = OpIAdd %uint %59 %uint_8
-%94 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %93
-OpStore %94 %51
-%96 = OpIAdd %uint %59 %uint_9
-%97 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %96
-OpStore %97 %52
-OpBranch %63
-%63 = OpLabel
-OpReturn
-OpFunctionEnd
-%101 = OpFunction %uint None %102
-%103 = OpFunctionParameter %uint
-%104 = OpFunctionParameter %uint
-%105 = OpFunctionParameter %uint
-%106 = OpFunctionParameter %uint
-%107 = OpLabel
-%108 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %103
-%109 = OpLoad %uint %108
-%110 = OpIAdd %uint %109 %104
-%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %110
-%112 = OpLoad %uint %111
-%113 = OpIAdd %uint %112 %105
-%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113
-%115 = OpLoad %uint %114
-%116 = OpIAdd %uint %115 %106
-%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116
-%118 = OpLoad %uint %117
-OpReturnValue %118
-OpFunctionEnd
-)";
+ const std::string new_funcs = kDirectRead2 + kStreamWrite4Frag + kDirectRead4;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
+ true, 7u, 23u, true, true, false,
+ false, false);
}
TEST_F(InstBindlessTest, InstBoundsInitLoadSizedUBOArray) {
@@ -4258,14 +2650,17 @@ TEST_F(InstBindlessTest, InstBoundsInitLoadSizedUBOArray) {
// b = uniformBuffer[nu_ii].a;
// }
- const std::string defs_before =
- R"(OpCapability Shader
+ // clang-format off
+ const std::string defs = R"(
+OpCapability Shader
OpCapability ShaderNonUniform
OpCapability UniformBufferArrayNonUniformIndexing
OpExtension "SPV_EXT_descriptor_indexing"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %b %nu_ii
+; CHECK: OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpSourceExtension "GL_EXT_nonuniform_qualifier"
@@ -4285,6 +2680,11 @@ OpDecorate %nu_ii Location 0
OpDecorate %nu_ii NonUniform
OpDecorate %18 NonUniform
OpDecorate %22 NonUniform
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+)" + kInputDecorations + R"(
+; CHECK: OpDecorate %117 NonUniform
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -4301,216 +2701,77 @@ OpDecorate %22 NonUniform
%nu_ii = OpVariable %_ptr_Input_int Input
%int_0 = OpConstant %int 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
-)";
-
- const std::string defs_after =
- R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability UniformBufferArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %b "b"
-OpName %uname "uname"
-OpMemberName %uname 0 "a"
-OpName %uniformBuffer "uniformBuffer"
-OpName %nu_ii "nu_ii"
-OpDecorate %b Location 0
-OpMemberDecorate %uname 0 Offset 0
-OpDecorate %uname Block
-OpDecorate %uniformBuffer DescriptorSet 0
-OpDecorate %uniformBuffer Binding 3
-OpDecorate %nu_ii Flat
-OpDecorate %nu_ii Location 0
-OpDecorate %nu_ii NonUniform
-OpDecorate %7 NonUniform
-OpDecorate %89 NonUniform
-OpDecorate %120 NonUniform
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_39 Block
-OpMemberDecorate %_struct_39 0 Offset 0
-OpMemberDecorate %_struct_39 1 Offset 4
-OpDecorate %41 DescriptorSet 7
-OpDecorate %41 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-OpDecorate %_struct_98 Block
-OpMemberDecorate %_struct_98 0 Offset 0
-OpDecorate %100 DescriptorSet 7
-OpDecorate %100 Binding 1
-OpDecorate %117 NonUniform
-%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%_ptr_Output_float = OpTypePointer Output %float
-%b = OpVariable %_ptr_Output_float Output
-%uname = OpTypeStruct %float
-%uint = OpTypeInt 32 0
-%uint_128 = OpConstant %uint 128
-%_arr_uname_uint_128 = OpTypeArray %uname %uint_128
-%_ptr_Uniform__arr_uname_uint_128 = OpTypePointer Uniform %_arr_uname_uint_128
-%uniformBuffer = OpVariable %_ptr_Uniform__arr_uname_uint_128 Uniform
-%int = OpTypeInt 32 1
-%_ptr_Input_int = OpTypePointer Input %int
-%nu_ii = OpVariable %_ptr_Input_int Input
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%uint_0 = OpConstant %uint 0
-%bool = OpTypeBool
-%32 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_39 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
-%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_1 = OpConstant %uint 1
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_46 = OpConstant %uint 46
-%88 = OpConstantNull %float
-%92 = OpTypeFunction %uint %uint %uint %uint %uint
-%_struct_98 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_98 = OpTypePointer StorageBuffer %_struct_98
-%100 = OpVariable %_ptr_StorageBuffer__struct_98 StorageBuffer
-)";
+; CHECK: %uint_0 = OpConstant %uint 0
+; CHECK: %bool = OpTypeBool
+; CHECK: %32 = OpTypeFunction %void %uint %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kOutputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %v4float = OpTypeVector %float 4
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_46 = OpConstant %uint 46
+; CHECK: %88 = OpConstantNull %float
+; CHECK: %92 = OpTypeFunction %uint %uint %uint %uint %uint
+)" + kInputGlobals;
+ // clang-format on
- const std::string func_before =
- R"(%main = OpFunction %void None %3
+ const std::string main_func = R"(
+%main = OpFunction %void None %3
%5 = OpLabel
%18 = OpLoad %int %nu_ii
%21 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %18 %int_0
%22 = OpLoad %float %21
OpStore %b %22
+; CHECK-NOT: %22 = OpLoad %float %21
+; CHECK-NOT: OpStore %b %22
+; CHECK: %25 = OpULessThan %bool %7 %uint_128
+; CHECK: OpSelectionMerge %26 None
+; CHECK: OpBranchConditional %25 %27 %28
+; CHECK: %27 = OpLabel
+; CHECK: %90 = OpBitcast %uint %7
+; CHECK: %112 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_3 %90
+; CHECK: %113 = OpULessThan %bool %uint_0 %112
+; CHECK: OpSelectionMerge %114 None
+; CHECK: OpBranchConditional %113 %115 %116
+; CHECK: %115 = OpLabel
+; CHECK: %117 = OpLoad %float %22
+; CHECK: OpBranch %114
+; CHECK: %116 = OpLabel
+; CHECK: %118 = OpBitcast %uint %7
+; CHECK: %119 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_46 %uint_1 %118 %uint_0
+; CHECK: OpBranch %114
+; CHECK: %114 = OpLabel
+; CHECK: %120 = OpPhi %float %117 %115 %88 %116
+; CHECK: OpBranch %26
+; CHECK: %28 = OpLabel
+; CHECK: %30 = OpBitcast %uint %7
+; CHECK: %87 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_46 %uint_0 %30 %uint_128
+; CHECK: OpBranch %26
+; CHECK: %26 = OpLabel
+; CHECK: %89 = OpPhi %float %120 %114 %88 %28
+; CHECK: OpStore %b %89
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %10
-%21 = OpLabel
-%7 = OpLoad %int %nu_ii
-%22 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %7 %int_0
-%25 = OpULessThan %bool %7 %uint_128
-OpSelectionMerge %26 None
-OpBranchConditional %25 %27 %28
-%27 = OpLabel
-%90 = OpBitcast %uint %7
-%112 = OpFunctionCall %uint %91 %uint_0 %uint_0 %uint_3 %90
-%113 = OpULessThan %bool %uint_0 %112
-OpSelectionMerge %114 None
-OpBranchConditional %113 %115 %116
-%115 = OpLabel
-%117 = OpLoad %float %22
-OpBranch %114
-%116 = OpLabel
-%118 = OpBitcast %uint %7
-%119 = OpFunctionCall %void %31 %uint_46 %uint_1 %118 %uint_0
-OpBranch %114
-%114 = OpLabel
-%120 = OpPhi %float %117 %115 %88 %116
-OpBranch %26
-%28 = OpLabel
-%30 = OpBitcast %uint %7
-%87 = OpFunctionCall %void %31 %uint_46 %uint_0 %30 %uint_128
-OpBranch %26
-%26 = OpLabel
-%89 = OpPhi %float %120 %114 %88 %28
-OpStore %b %89
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%31 = OpFunction %void None %32
-%33 = OpFunctionParameter %uint
-%34 = OpFunctionParameter %uint
-%35 = OpFunctionParameter %uint
-%36 = OpFunctionParameter %uint
-%37 = OpLabel
-%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0
-%46 = OpAtomicIAdd %uint %43 %uint_4 %uint_0 %uint_10
-%47 = OpIAdd %uint %46 %uint_10
-%48 = OpArrayLength %uint %41 1
-%49 = OpULessThanEqual %bool %47 %48
-OpSelectionMerge %50 None
-OpBranchConditional %49 %51 %50
-%51 = OpLabel
-%52 = OpIAdd %uint %46 %uint_0
-%54 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %52
-OpStore %54 %uint_10
-%56 = OpIAdd %uint %46 %uint_1
-%57 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %56
-OpStore %57 %uint_23
-%59 = OpIAdd %uint %46 %uint_2
-%60 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %59
-OpStore %60 %33
-%62 = OpIAdd %uint %46 %uint_3
-%63 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %62
-OpStore %63 %uint_4
-%67 = OpLoad %v4float %gl_FragCoord
-%69 = OpBitcast %v4uint %67
-%70 = OpCompositeExtract %uint %69 0
-%71 = OpIAdd %uint %46 %uint_4
-%72 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %71
-OpStore %72 %70
-%73 = OpCompositeExtract %uint %69 1
-%75 = OpIAdd %uint %46 %uint_5
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %75
-OpStore %76 %73
-%78 = OpIAdd %uint %46 %uint_7
-%79 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %78
-OpStore %79 %34
-%81 = OpIAdd %uint %46 %uint_8
-%82 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %81
-OpStore %82 %35
-%84 = OpIAdd %uint %46 %uint_9
-%85 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %84
-OpStore %85 %36
-OpBranch %50
-%50 = OpLabel
-OpReturn
-OpFunctionEnd
-%91 = OpFunction %uint None %92
-%93 = OpFunctionParameter %uint
-%94 = OpFunctionParameter %uint
-%95 = OpFunctionParameter %uint
-%96 = OpFunctionParameter %uint
-%97 = OpLabel
-%101 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %93
-%102 = OpLoad %uint %101
-%103 = OpIAdd %uint %102 %94
-%104 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %103
-%105 = OpLoad %uint %104
-%106 = OpIAdd %uint %105 %95
-%107 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %106
-%108 = OpLoad %uint %107
-%109 = OpIAdd %uint %108 %96
-%110 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %109
-%111 = OpLoad %uint %110
-OpReturnValue %111
-OpFunctionEnd
-)";
+ const std::string new_funcs = kStreamWrite4Frag + kDirectRead4;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
+ true, 7u, 23u, true, true, false,
+ false, false);
}
TEST_F(InstBindlessTest,
@@ -4532,13 +2793,16 @@ TEST_F(InstBindlessTest,
// sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r;
// }
- const std::string defs_before =
- R"(OpCapability Shader
+ // clang-format off
+ const std::string defs = R"(
+OpCapability Shader
OpCapability RuntimeDescriptorArray
OpExtension "SPV_EXT_descriptor_indexing"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
+; CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpSourceExtension "GL_EXT_nonuniform_qualifier"
@@ -4556,6 +2820,9 @@ OpDecorate %sbo Binding 0
OpDecorate %images DescriptorSet 0
OpDecorate %images Binding 1
OpDecorate %images NonWritable
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kInputDecorations + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
@@ -4577,100 +2844,38 @@ OpDecorate %images NonWritable
%v4float = OpTypeVector %float 4
%uint_0 = OpConstant %uint 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %34 = OpTypeFunction %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kInputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %bool = OpTypeBool
+; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint
+)" + kOutputGlobals + R"(
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %v3uint = OpTypeVector %uint 3
+; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
+; CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
+; CHECK: %uint_6 = OpConstant %uint 6
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_50 = OpConstant %uint 50
+; CHECK: %112 = OpConstantNull %v4float
+; CHECK: %115 = OpTypeFunction %uint %uint %uint %uint %uint
+; CHECK: %uint_47 = OpConstant %uint 47
+; CHECK: %140 = OpConstantNull %uint
+; CHECK: %uint_53 = OpConstant %uint 53
)";
+ // clang-format on
- const std::string defs_after =
- R"(OpCapability Shader
-OpCapability RuntimeDescriptorArray
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
-OpExecutionMode %main LocalSize 1 1 1
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %Input "Input"
-OpMemberName %Input 0 "index"
-OpMemberName %Input 1 "red"
-OpName %sbo "sbo"
-OpName %images "images"
-OpMemberDecorate %Input 0 Offset 0
-OpMemberDecorate %Input 1 Offset 4
-OpDecorate %Input BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
-OpDecorate %images NonWritable
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_39 Block
-OpMemberDecorate %_struct_39 0 Offset 0
-OpDecorate %41 DescriptorSet 7
-OpDecorate %41 Binding 1
-OpDecorate %_struct_63 Block
-OpMemberDecorate %_struct_63 0 Offset 0
-OpMemberDecorate %_struct_63 1 Offset 4
-OpDecorate %65 DescriptorSet 7
-OpDecorate %65 Binding 0
-OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
-%void = OpTypeVoid
-%7 = OpTypeFunction %void
-%uint = OpTypeInt 32 0
-%float = OpTypeFloat 32
-%Input = OpTypeStruct %uint %float
-%_ptr_Uniform_Input = OpTypePointer Uniform %Input
-%sbo = OpVariable %_ptr_Uniform_Input Uniform
-%int = OpTypeInt 32 1
-%int_1 = OpConstant %int 1
-%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
-%_runtimearr_13 = OpTypeRuntimeArray %13
-%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
-%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_uint = OpTypePointer Uniform %uint
-%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
-%v2int = OpTypeVector %int 2
-%20 = OpConstantComposite %v2int %int_0 %int_0
-%v4float = OpTypeVector %float 4
-%uint_0 = OpConstant %uint 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%uint_1 = OpConstant %uint 1
-%34 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_39 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
-%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%57 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_63 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63
-%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_5 = OpConstant %uint 5
-%uint_3 = OpConstant %uint 3
-%v3uint = OpTypeVector %uint 3
-%_ptr_Input_v3uint = OpTypePointer Input %v3uint
-%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_50 = OpConstant %uint 50
-%112 = OpConstantNull %v4float
-%115 = OpTypeFunction %uint %uint %uint %uint %uint
-%uint_47 = OpConstant %uint 47
-%140 = OpConstantNull %uint
-%uint_53 = OpConstant %uint 53
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %3
+ const std::string main_func = R"(
+%main = OpFunction %void None %3
%5 = OpLabel
%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
%20 = OpLoad %uint %19
@@ -4680,159 +2885,70 @@ OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
%29 = OpCompositeExtract %float %27 0
%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
OpStore %31 %29
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string func_after =
- R"(%main = OpFunction %void None %7
-%24 = OpLabel
-%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
-%132 = OpFunctionCall %uint %114 %uint_0 %uint_0 %uint_0 %uint_0
-%133 = OpULessThan %bool %uint_0 %132
-OpSelectionMerge %134 None
-OpBranchConditional %133 %135 %136
-%135 = OpLabel
-%137 = OpLoad %uint %25
-OpBranch %134
-%136 = OpLabel
-%139 = OpFunctionCall %void %56 %uint_47 %uint_1 %uint_0 %uint_0
-OpBranch %134
-%134 = OpLabel
-%141 = OpPhi %uint %137 %135 %140 %136
-%27 = OpAccessChain %_ptr_UniformConstant_13 %images %141
-%28 = OpLoad %13 %27
-%48 = OpFunctionCall %uint %33 %uint_1 %uint_1
-%50 = OpULessThan %bool %141 %48
-OpSelectionMerge %51 None
-OpBranchConditional %50 %52 %53
-%52 = OpLabel
-%54 = OpLoad %13 %27
-%142 = OpFunctionCall %uint %114 %uint_0 %uint_0 %uint_1 %141
-%143 = OpULessThan %bool %uint_0 %142
-OpSelectionMerge %144 None
-OpBranchConditional %143 %145 %146
-%145 = OpLabel
-%147 = OpLoad %13 %27
-%148 = OpImageRead %v4float %147 %20
-OpBranch %144
-%146 = OpLabel
-%149 = OpFunctionCall %void %56 %uint_50 %uint_1 %141 %uint_0
-OpBranch %144
-%144 = OpLabel
-%150 = OpPhi %v4float %148 %145 %112 %146
-OpBranch %51
-%53 = OpLabel
-%111 = OpFunctionCall %void %56 %uint_50 %uint_0 %141 %48
-OpBranch %51
-%51 = OpLabel
-%113 = OpPhi %v4float %150 %144 %112 %53
-%30 = OpCompositeExtract %float %113 0
-%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
-%151 = OpFunctionCall %uint %114 %uint_0 %uint_0 %uint_0 %uint_0
-%152 = OpULessThan %bool %uint_0 %151
-OpSelectionMerge %153 None
-OpBranchConditional %152 %154 %155
-%154 = OpLabel
-OpStore %31 %30
-OpBranch %153
-%155 = OpLabel
-%157 = OpFunctionCall %void %56 %uint_53 %uint_1 %uint_0 %uint_0
-OpBranch %153
-%153 = OpLabel
+; CHECK-NOT: OpStore %31 %29
+; CHECK: %132 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
+; CHECK: %133 = OpULessThan %bool %uint_0 %132
+; CHECK: OpSelectionMerge %134 None
+; CHECK: OpBranchConditional %133 %135 %136
+; CHECK: %135 = OpLabel
+; CHECK: %137 = OpLoad %uint %25
+; CHECK: OpBranch %134
+; CHECK: %136 = OpLabel
+; CHECK: %139 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_47 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %134
+; CHECK: %134 = OpLabel
+; CHECK: %141 = OpPhi %uint %137 %135 %140 %136
+; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %141
+; CHECK: %28 = OpLoad %13 %27
+; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
+; CHECK: %50 = OpULessThan %bool %141 %48
+; CHECK: OpSelectionMerge %51 None
+; CHECK: OpBranchConditional %50 %52 %53
+; CHECK: %52 = OpLabel
+; CHECK: %54 = OpLoad %13 %27
+; CHECK: %142 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %141
+; CHECK: %143 = OpULessThan %bool %uint_0 %142
+; CHECK: OpSelectionMerge %144 None
+; CHECK: OpBranchConditional %143 %145 %146
+; CHECK: %145 = OpLabel
+; CHECK: %147 = OpLoad %13 %27
+; CHECK: %148 = OpImageRead %v4float %147 %20
+; CHECK: OpBranch %144
+; CHECK: %146 = OpLabel
+; CHECK: %149 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_50 %uint_1 %141 %uint_0
+; CHECK: OpBranch %144
+; CHECK: %144 = OpLabel
+; CHECK: %150 = OpPhi %v4float %148 %145 %112 %146
+; CHECK: OpBranch %51
+; CHECK: %53 = OpLabel
+; CHECK: %111 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_50 %uint_0 %141 %48
+; CHECK: OpBranch %51
+; CHECK: %51 = OpLabel
+; CHECK: %113 = OpPhi %v4float %150 %144 %112 %53
+; CHECK: %30 = OpCompositeExtract %float %113 0
+; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+; CHECK: %151 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
+; CHECK: %152 = OpULessThan %bool %uint_0 %151
+; CHECK: OpSelectionMerge %153 None
+; CHECK: OpBranchConditional %152 %154 %155
+; CHECK: %154 = OpLabel
+; CHECK: OpStore %31 %30
+; CHECK: OpBranch %153
+; CHECK: %155 = OpLabel
+; CHECK: %157 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_53 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %153
+; CHECK: %153 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string new_funcs =
- R"(%33 = OpFunction %uint None %34
-%35 = OpFunctionParameter %uint
-%36 = OpFunctionParameter %uint
-%37 = OpLabel
-%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35
-%44 = OpLoad %uint %43
-%45 = OpIAdd %uint %44 %36
-%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45
-%47 = OpLoad %uint %46
-OpReturnValue %47
-OpFunctionEnd
-%56 = OpFunction %void None %57
-%58 = OpFunctionParameter %uint
-%59 = OpFunctionParameter %uint
-%60 = OpFunctionParameter %uint
-%61 = OpFunctionParameter %uint
-%62 = OpLabel
-%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0
-%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10
-%70 = OpIAdd %uint %69 %uint_10
-%71 = OpArrayLength %uint %65 1
-%72 = OpULessThanEqual %bool %70 %71
-OpSelectionMerge %73 None
-OpBranchConditional %72 %74 %73
-%74 = OpLabel
-%75 = OpIAdd %uint %69 %uint_0
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75
-OpStore %76 %uint_10
-%78 = OpIAdd %uint %69 %uint_1
-%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78
-OpStore %79 %uint_23
-%81 = OpIAdd %uint %69 %uint_2
-%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81
-OpStore %82 %58
-%85 = OpIAdd %uint %69 %uint_3
-%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85
-OpStore %86 %uint_5
-%90 = OpLoad %v3uint %gl_GlobalInvocationID
-%91 = OpCompositeExtract %uint %90 0
-%92 = OpCompositeExtract %uint %90 1
-%93 = OpCompositeExtract %uint %90 2
-%94 = OpIAdd %uint %69 %uint_4
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94
-OpStore %95 %91
-%96 = OpIAdd %uint %69 %uint_5
-%97 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %96
-OpStore %97 %92
-%99 = OpIAdd %uint %69 %uint_6
-%100 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %99
-OpStore %100 %93
-%102 = OpIAdd %uint %69 %uint_7
-%103 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %102
-OpStore %103 %59
-%105 = OpIAdd %uint %69 %uint_8
-%106 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %105
-OpStore %106 %60
-%108 = OpIAdd %uint %69 %uint_9
-%109 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %108
-OpStore %109 %61
-OpBranch %73
-%73 = OpLabel
-OpReturn
-OpFunctionEnd
-%114 = OpFunction %uint None %115
-%116 = OpFunctionParameter %uint
-%117 = OpFunctionParameter %uint
-%118 = OpFunctionParameter %uint
-%119 = OpFunctionParameter %uint
-%120 = OpLabel
-%121 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %116
-%122 = OpLoad %uint %121
-%123 = OpIAdd %uint %122 %117
-%124 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %123
-%125 = OpLoad %uint %124
-%126 = OpIAdd %uint %125 %118
-%127 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %126
-%128 = OpLoad %uint %127
-%129 = OpIAdd %uint %128 %119
-%130 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %129
-%131 = OpLoad %uint %130
-OpReturnValue %131
-OpFunctionEnd
-)";
+ kDirectRead2 + kStreamWrite4Compute + kDirectRead4;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
+ true, 7u, 23u, true, true, false,
+ false, false);
}
TEST_F(InstBindlessTest,
@@ -4853,14 +2969,17 @@ TEST_F(InstBindlessTest,
// sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r;
// }
- const std::string defs_before =
- R"(OpCapability RuntimeDescriptorArray
+ // clang-format off
+ const std::string defs = R"(
+OpCapability RuntimeDescriptorArray
OpCapability RayTracingNV
OpExtension "SPV_EXT_descriptor_indexing"
OpExtension "SPV_NV_ray_tracing"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint RayGenerationNV %main "main"
+; CHECK: OpEntryPoint RayGenerationNV %main "main" %89
OpSource GLSL 460
OpSourceExtension "GL_EXT_nonuniform_qualifier"
OpSourceExtension "GL_NV_ray_tracing"
@@ -4878,51 +2997,11 @@ OpDecorate %sbo Binding 0
OpDecorate %images DescriptorSet 0
OpDecorate %images Binding 1
OpDecorate %images NonWritable
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kInputDecorations + kOutputDecorations + R"(
+; CHECK: OpDecorate %89 BuiltIn LaunchIdNV
%void = OpTypeVoid
-)";
-
- const std::string defs_after =
- R"(OpCapability RuntimeDescriptorArray
-OpCapability RayTracingNV
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_NV_ray_tracing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint RayGenerationNV %main "main" %89
-OpSource GLSL 460
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpSourceExtension "GL_NV_ray_tracing"
-OpName %main "main"
-OpName %StorageBuffer "StorageBuffer"
-OpMemberName %StorageBuffer 0 "index"
-OpMemberName %StorageBuffer 1 "red"
-OpName %sbo "sbo"
-OpName %images "images"
-OpMemberDecorate %StorageBuffer 0 Offset 0
-OpMemberDecorate %StorageBuffer 1 Offset 4
-OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
-OpDecorate %images NonWritable
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_39 Block
-OpMemberDecorate %_struct_39 0 Offset 0
-OpDecorate %41 DescriptorSet 7
-OpDecorate %41 Binding 1
-OpDecorate %_struct_63 Block
-OpMemberDecorate %_struct_63 0 Offset 0
-OpMemberDecorate %_struct_63 1 Offset 4
-OpDecorate %65 DescriptorSet 7
-OpDecorate %65 Binding 0
-OpDecorate %89 BuiltIn LaunchIdNV
-%void = OpTypeVoid
-)";
-
- const std::string func_before =
- R"(%3 = OpTypeFunction %void
+%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%StorageBuffer = OpTypeStruct %uint %float
@@ -4942,6 +3021,38 @@ OpDecorate %89 BuiltIn LaunchIdNV
%v4float = OpTypeVector %float 4
%uint_0 = OpConstant %uint 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %34 = OpTypeFunction %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kInputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %bool = OpTypeBool
+; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint
+)" + kOutputGlobals + R"(
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_5313 = OpConstant %uint 5313
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %v3uint = OpTypeVector %uint 3
+; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
+; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_6 = OpConstant %uint 6
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_51 = OpConstant %uint 51
+; CHECK: %113 = OpConstantNull %v4float
+; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint
+; CHECK: %uint_48 = OpConstant %uint 48
+; CHECK: %141 = OpConstantNull %uint
+; CHECK: %uint_54 = OpConstant %uint 54
+)";
+ // clang-format on
+
+ const std::string main_func = R"(
%main = OpFunction %void None %3
%5 = OpLabel
%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
@@ -4952,211 +3063,69 @@ OpDecorate %89 BuiltIn LaunchIdNV
%29 = OpCompositeExtract %float %27 0
%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
OpStore %31 %29
+; CHECK-NOT: OpStore %31 %29
+; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
+; CHECK: %134 = OpULessThan %bool %uint_0 %133
+; CHECK: OpSelectionMerge %135 None
+; CHECK: OpBranchConditional %134 %136 %137
+; CHECK: %136 = OpLabel
+; CHECK: %138 = OpLoad %uint %25
+; CHECK: OpBranch %135
+; CHECK: %137 = OpLabel
+; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %135
+; CHECK: %135 = OpLabel
+; CHECK: %142 = OpPhi %uint %138 %136 %141 %137
+; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+; CHECK: %28 = OpLoad %13 %27
+; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
+; CHECK: %50 = OpULessThan %bool %142 %48
+; CHECK: OpSelectionMerge %51 None
+; CHECK: OpBranchConditional %50 %52 %53
+; CHECK: %52 = OpLabel
+; CHECK: %54 = OpLoad %13 %27
+; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
+; CHECK: %144 = OpULessThan %bool %uint_0 %143
+; CHECK: OpSelectionMerge %145 None
+; CHECK: OpBranchConditional %144 %146 %147
+; CHECK: %146 = OpLabel
+; CHECK: %148 = OpLoad %13 %27
+; CHECK: %149 = OpImageRead %v4float %148 %20
+; CHECK: OpBranch %145
+; CHECK: %147 = OpLabel
+; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
+; CHECK: OpBranch %145
+; CHECK: %145 = OpLabel
+; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147
+; CHECK: OpBranch %51
+; CHECK: %53 = OpLabel
+; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
+; CHECK: OpBranch %51
+; CHECK: %51 = OpLabel
+; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53
+; CHECK: %30 = OpCompositeExtract %float %114 0
+; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
+; CHECK: %153 = OpULessThan %bool %uint_0 %152
+; CHECK: OpSelectionMerge %154 None
+; CHECK: OpBranchConditional %153 %155 %156
+; CHECK: %155 = OpLabel
+; CHECK: OpStore %31 %30
+; CHECK: OpBranch %154
+; CHECK: %156 = OpLabel
+; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %154
+; CHECK: %154 = OpLabel
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%7 = OpTypeFunction %void
-%uint = OpTypeInt 32 0
-%float = OpTypeFloat 32
-%StorageBuffer = OpTypeStruct %uint %float
-%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
-%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
-%int = OpTypeInt 32 1
-%int_1 = OpConstant %int 1
-%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
-%_runtimearr_13 = OpTypeRuntimeArray %13
-%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
-%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_uint = OpTypePointer Uniform %uint
-%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
-%v2int = OpTypeVector %int 2
-%20 = OpConstantComposite %v2int %int_0 %int_0
-%v4float = OpTypeVector %float 4
-%uint_0 = OpConstant %uint 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%uint_1 = OpConstant %uint 1
-%34 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_39 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
-%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%57 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_63 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63
-%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_5313 = OpConstant %uint 5313
-%uint_3 = OpConstant %uint 3
-%v3uint = OpTypeVector %uint 3
-%_ptr_Input_v3uint = OpTypePointer Input %v3uint
-%89 = OpVariable %_ptr_Input_v3uint Input
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_51 = OpConstant %uint 51
-%113 = OpConstantNull %v4float
-%116 = OpTypeFunction %uint %uint %uint %uint %uint
-%uint_48 = OpConstant %uint 48
-%141 = OpConstantNull %uint
-%uint_54 = OpConstant %uint 54
-%main = OpFunction %void None %7
-%24 = OpLabel
-%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
-%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
-%134 = OpULessThan %bool %uint_0 %133
-OpSelectionMerge %135 None
-OpBranchConditional %134 %136 %137
-%136 = OpLabel
-%138 = OpLoad %uint %25
-OpBranch %135
-%137 = OpLabel
-%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0
-OpBranch %135
-%135 = OpLabel
-%142 = OpPhi %uint %138 %136 %141 %137
-%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
-%28 = OpLoad %13 %27
-%48 = OpFunctionCall %uint %33 %uint_1 %uint_1
-%50 = OpULessThan %bool %142 %48
-OpSelectionMerge %51 None
-OpBranchConditional %50 %52 %53
-%52 = OpLabel
-%54 = OpLoad %13 %27
-%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142
-%144 = OpULessThan %bool %uint_0 %143
-OpSelectionMerge %145 None
-OpBranchConditional %144 %146 %147
-%146 = OpLabel
-%148 = OpLoad %13 %27
-%149 = OpImageRead %v4float %148 %20
-OpBranch %145
-%147 = OpLabel
-%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0
-OpBranch %145
-%145 = OpLabel
-%151 = OpPhi %v4float %149 %146 %113 %147
-OpBranch %51
-%53 = OpLabel
-%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48
-OpBranch %51
-%51 = OpLabel
-%114 = OpPhi %v4float %151 %145 %113 %53
-%30 = OpCompositeExtract %float %114 0
-%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
-%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
-%153 = OpULessThan %bool %uint_0 %152
-OpSelectionMerge %154 None
-OpBranchConditional %153 %155 %156
-%155 = OpLabel
-OpStore %31 %30
-OpBranch %154
-%156 = OpLabel
-%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0
-OpBranch %154
-%154 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%33 = OpFunction %uint None %34
-%35 = OpFunctionParameter %uint
-%36 = OpFunctionParameter %uint
-%37 = OpLabel
-%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35
-%44 = OpLoad %uint %43
-%45 = OpIAdd %uint %44 %36
-%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45
-%47 = OpLoad %uint %46
-OpReturnValue %47
-OpFunctionEnd
-%56 = OpFunction %void None %57
-%58 = OpFunctionParameter %uint
-%59 = OpFunctionParameter %uint
-%60 = OpFunctionParameter %uint
-%61 = OpFunctionParameter %uint
-%62 = OpLabel
-%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0
-%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10
-%70 = OpIAdd %uint %69 %uint_10
-%71 = OpArrayLength %uint %65 1
-%72 = OpULessThanEqual %bool %70 %71
-OpSelectionMerge %73 None
-OpBranchConditional %72 %74 %73
-%74 = OpLabel
-%75 = OpIAdd %uint %69 %uint_0
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75
-OpStore %76 %uint_10
-%78 = OpIAdd %uint %69 %uint_1
-%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78
-OpStore %79 %uint_23
-%81 = OpIAdd %uint %69 %uint_2
-%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81
-OpStore %82 %58
-%85 = OpIAdd %uint %69 %uint_3
-%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85
-OpStore %86 %uint_5313
-%90 = OpLoad %v3uint %89
-%91 = OpCompositeExtract %uint %90 0
-%92 = OpCompositeExtract %uint %90 1
-%93 = OpCompositeExtract %uint %90 2
-%94 = OpIAdd %uint %69 %uint_4
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94
-OpStore %95 %91
-%97 = OpIAdd %uint %69 %uint_5
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97
-OpStore %98 %92
-%100 = OpIAdd %uint %69 %uint_6
-%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100
-OpStore %101 %93
-%103 = OpIAdd %uint %69 %uint_7
-%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103
-OpStore %104 %59
-%106 = OpIAdd %uint %69 %uint_8
-%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106
-OpStore %107 %60
-%109 = OpIAdd %uint %69 %uint_9
-%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109
-OpStore %110 %61
-OpBranch %73
-%73 = OpLabel
-OpReturn
-OpFunctionEnd
-%115 = OpFunction %uint None %116
-%117 = OpFunctionParameter %uint
-%118 = OpFunctionParameter %uint
-%119 = OpFunctionParameter %uint
-%120 = OpFunctionParameter %uint
-%121 = OpLabel
-%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117
-%123 = OpLoad %uint %122
-%124 = OpIAdd %uint %123 %118
-%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124
-%126 = OpLoad %uint %125
-%127 = OpIAdd %uint %126 %119
-%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127
-%129 = OpLoad %uint %128
-%130 = OpIAdd %uint %129 %120
-%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130
-%132 = OpLoad %uint %131
-OpReturnValue %132
-OpFunctionEnd
-)";
+ const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
+ true, 7u, 23u, true, true, false,
+ false, false);
}
TEST_F(InstBindlessTest,
@@ -5177,14 +3146,17 @@ TEST_F(InstBindlessTest,
// sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r;
// }
- const std::string defs_before =
- R"(OpCapability RuntimeDescriptorArray
+ // clang-format off
+ const std::string defs = R"(
+OpCapability RuntimeDescriptorArray
OpCapability RayTracingNV
OpExtension "SPV_EXT_descriptor_indexing"
OpExtension "SPV_NV_ray_tracing"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint IntersectionNV %main "main"
+; CHECK: OpEntryPoint IntersectionNV %main "main" %89
OpSource GLSL 460
OpSourceExtension "GL_EXT_nonuniform_qualifier"
OpSourceExtension "GL_NV_ray_tracing"
@@ -5202,51 +3174,11 @@ OpDecorate %sbo Binding 0
OpDecorate %images DescriptorSet 0
OpDecorate %images Binding 1
OpDecorate %images NonWritable
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kInputDecorations + kOutputDecorations + R"(
+; CHECK: OpDecorate %89 BuiltIn LaunchIdNV
%void = OpTypeVoid
-)";
-
- const std::string defs_after =
- R"(OpCapability RuntimeDescriptorArray
-OpCapability RayTracingNV
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_NV_ray_tracing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint IntersectionNV %main "main" %89
-OpSource GLSL 460
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpSourceExtension "GL_NV_ray_tracing"
-OpName %main "main"
-OpName %StorageBuffer "StorageBuffer"
-OpMemberName %StorageBuffer 0 "index"
-OpMemberName %StorageBuffer 1 "red"
-OpName %sbo "sbo"
-OpName %images "images"
-OpMemberDecorate %StorageBuffer 0 Offset 0
-OpMemberDecorate %StorageBuffer 1 Offset 4
-OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
-OpDecorate %images NonWritable
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_39 Block
-OpMemberDecorate %_struct_39 0 Offset 0
-OpDecorate %41 DescriptorSet 7
-OpDecorate %41 Binding 1
-OpDecorate %_struct_63 Block
-OpMemberDecorate %_struct_63 0 Offset 0
-OpMemberDecorate %_struct_63 1 Offset 4
-OpDecorate %65 DescriptorSet 7
-OpDecorate %65 Binding 0
-OpDecorate %89 BuiltIn LaunchIdNV
-%void = OpTypeVoid
-)";
-
- const std::string func_before =
- R"(%3 = OpTypeFunction %void
+%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%StorageBuffer = OpTypeStruct %uint %float
@@ -5266,6 +3198,37 @@ OpDecorate %89 BuiltIn LaunchIdNV
%v4float = OpTypeVector %float 4
%uint_0 = OpConstant %uint 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %34 = OpTypeFunction %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kInputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %bool = OpTypeBool
+)" + kOutputGlobals + R"(
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_5314 = OpConstant %uint 5314
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %v3uint = OpTypeVector %uint 3
+; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
+; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_6 = OpConstant %uint 6
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_51 = OpConstant %uint 51
+; CHECK: %113 = OpConstantNull %v4float
+; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint
+; CHECK: %uint_48 = OpConstant %uint 48
+; CHECK: %141 = OpConstantNull %uint
+; CHECK: %uint_54 = OpConstant %uint 54
+)";
+ // clang-format on
+
+ const std::string main_func = R"(
%main = OpFunction %void None %3
%5 = OpLabel
%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
@@ -5276,211 +3239,69 @@ OpDecorate %89 BuiltIn LaunchIdNV
%29 = OpCompositeExtract %float %27 0
%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
OpStore %31 %29
+; CHECK-NOT: OpStore %31 %29
+; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
+; CHECK: %134 = OpULessThan %bool %uint_0 %133
+; CHECK: OpSelectionMerge %135 None
+; CHECK: OpBranchConditional %134 %136 %137
+; CHECK: %136 = OpLabel
+; CHECK: %138 = OpLoad %uint %25
+; CHECK: OpBranch %135
+; CHECK: %137 = OpLabel
+; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %135
+; CHECK: %135 = OpLabel
+; CHECK: %142 = OpPhi %uint %138 %136 %141 %137
+; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+; CHECK: %28 = OpLoad %13 %27
+; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
+; CHECK: %50 = OpULessThan %bool %142 %48
+; CHECK: OpSelectionMerge %51 None
+; CHECK: OpBranchConditional %50 %52 %53
+; CHECK: %52 = OpLabel
+; CHECK: %54 = OpLoad %13 %27
+; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
+; CHECK: %144 = OpULessThan %bool %uint_0 %143
+; CHECK: OpSelectionMerge %145 None
+; CHECK: OpBranchConditional %144 %146 %147
+; CHECK: %146 = OpLabel
+; CHECK: %148 = OpLoad %13 %27
+; CHECK: %149 = OpImageRead %v4float %148 %20
+; CHECK: OpBranch %145
+; CHECK: %147 = OpLabel
+; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
+; CHECK: OpBranch %145
+; CHECK: %145 = OpLabel
+; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147
+; CHECK: OpBranch %51
+; CHECK: %53 = OpLabel
+; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
+; CHECK: OpBranch %51
+; CHECK: %51 = OpLabel
+; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53
+; CHECK: %30 = OpCompositeExtract %float %114 0
+; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
+; CHECK: %153 = OpULessThan %bool %uint_0 %152
+; CHECK: OpSelectionMerge %154 None
+; CHECK: OpBranchConditional %153 %155 %156
+; CHECK: %155 = OpLabel
+; CHECK: OpStore %31 %30
+; CHECK: OpBranch %154
+; CHECK: %156 = OpLabel
+; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %154
+; CHECK: %154 = OpLabel
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%7 = OpTypeFunction %void
-%uint = OpTypeInt 32 0
-%float = OpTypeFloat 32
-%StorageBuffer = OpTypeStruct %uint %float
-%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
-%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
-%int = OpTypeInt 32 1
-%int_1 = OpConstant %int 1
-%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
-%_runtimearr_13 = OpTypeRuntimeArray %13
-%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
-%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_uint = OpTypePointer Uniform %uint
-%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
-%v2int = OpTypeVector %int 2
-%20 = OpConstantComposite %v2int %int_0 %int_0
-%v4float = OpTypeVector %float 4
-%uint_0 = OpConstant %uint 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%uint_1 = OpConstant %uint 1
-%34 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_39 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
-%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%57 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_63 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63
-%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_5314 = OpConstant %uint 5314
-%uint_3 = OpConstant %uint 3
-%v3uint = OpTypeVector %uint 3
-%_ptr_Input_v3uint = OpTypePointer Input %v3uint
-%89 = OpVariable %_ptr_Input_v3uint Input
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_51 = OpConstant %uint 51
-%113 = OpConstantNull %v4float
-%116 = OpTypeFunction %uint %uint %uint %uint %uint
-%uint_48 = OpConstant %uint 48
-%141 = OpConstantNull %uint
-%uint_54 = OpConstant %uint 54
-%main = OpFunction %void None %7
-%24 = OpLabel
-%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
-%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
-%134 = OpULessThan %bool %uint_0 %133
-OpSelectionMerge %135 None
-OpBranchConditional %134 %136 %137
-%136 = OpLabel
-%138 = OpLoad %uint %25
-OpBranch %135
-%137 = OpLabel
-%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0
-OpBranch %135
-%135 = OpLabel
-%142 = OpPhi %uint %138 %136 %141 %137
-%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
-%28 = OpLoad %13 %27
-%48 = OpFunctionCall %uint %33 %uint_1 %uint_1
-%50 = OpULessThan %bool %142 %48
-OpSelectionMerge %51 None
-OpBranchConditional %50 %52 %53
-%52 = OpLabel
-%54 = OpLoad %13 %27
-%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142
-%144 = OpULessThan %bool %uint_0 %143
-OpSelectionMerge %145 None
-OpBranchConditional %144 %146 %147
-%146 = OpLabel
-%148 = OpLoad %13 %27
-%149 = OpImageRead %v4float %148 %20
-OpBranch %145
-%147 = OpLabel
-%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0
-OpBranch %145
-%145 = OpLabel
-%151 = OpPhi %v4float %149 %146 %113 %147
-OpBranch %51
-%53 = OpLabel
-%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48
-OpBranch %51
-%51 = OpLabel
-%114 = OpPhi %v4float %151 %145 %113 %53
-%30 = OpCompositeExtract %float %114 0
-%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
-%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
-%153 = OpULessThan %bool %uint_0 %152
-OpSelectionMerge %154 None
-OpBranchConditional %153 %155 %156
-%155 = OpLabel
-OpStore %31 %30
-OpBranch %154
-%156 = OpLabel
-%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0
-OpBranch %154
-%154 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%33 = OpFunction %uint None %34
-%35 = OpFunctionParameter %uint
-%36 = OpFunctionParameter %uint
-%37 = OpLabel
-%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35
-%44 = OpLoad %uint %43
-%45 = OpIAdd %uint %44 %36
-%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45
-%47 = OpLoad %uint %46
-OpReturnValue %47
-OpFunctionEnd
-%56 = OpFunction %void None %57
-%58 = OpFunctionParameter %uint
-%59 = OpFunctionParameter %uint
-%60 = OpFunctionParameter %uint
-%61 = OpFunctionParameter %uint
-%62 = OpLabel
-%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0
-%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10
-%70 = OpIAdd %uint %69 %uint_10
-%71 = OpArrayLength %uint %65 1
-%72 = OpULessThanEqual %bool %70 %71
-OpSelectionMerge %73 None
-OpBranchConditional %72 %74 %73
-%74 = OpLabel
-%75 = OpIAdd %uint %69 %uint_0
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75
-OpStore %76 %uint_10
-%78 = OpIAdd %uint %69 %uint_1
-%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78
-OpStore %79 %uint_23
-%81 = OpIAdd %uint %69 %uint_2
-%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81
-OpStore %82 %58
-%85 = OpIAdd %uint %69 %uint_3
-%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85
-OpStore %86 %uint_5314
-%90 = OpLoad %v3uint %89
-%91 = OpCompositeExtract %uint %90 0
-%92 = OpCompositeExtract %uint %90 1
-%93 = OpCompositeExtract %uint %90 2
-%94 = OpIAdd %uint %69 %uint_4
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94
-OpStore %95 %91
-%97 = OpIAdd %uint %69 %uint_5
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97
-OpStore %98 %92
-%100 = OpIAdd %uint %69 %uint_6
-%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100
-OpStore %101 %93
-%103 = OpIAdd %uint %69 %uint_7
-%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103
-OpStore %104 %59
-%106 = OpIAdd %uint %69 %uint_8
-%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106
-OpStore %107 %60
-%109 = OpIAdd %uint %69 %uint_9
-%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109
-OpStore %110 %61
-OpBranch %73
-%73 = OpLabel
-OpReturn
-OpFunctionEnd
-%115 = OpFunction %uint None %116
-%117 = OpFunctionParameter %uint
-%118 = OpFunctionParameter %uint
-%119 = OpFunctionParameter %uint
-%120 = OpFunctionParameter %uint
-%121 = OpLabel
-%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117
-%123 = OpLoad %uint %122
-%124 = OpIAdd %uint %123 %118
-%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124
-%126 = OpLoad %uint %125
-%127 = OpIAdd %uint %126 %119
-%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127
-%129 = OpLoad %uint %128
-%130 = OpIAdd %uint %129 %120
-%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130
-%132 = OpLoad %uint %131
-OpReturnValue %132
-OpFunctionEnd
-)";
+ const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
+ true, 7u, 23u, true, true, false,
+ false, false);
}
TEST_F(InstBindlessTest,
@@ -5501,14 +3322,17 @@ TEST_F(InstBindlessTest,
// sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r;
// }
- const std::string defs_before =
- R"(OpCapability RuntimeDescriptorArray
+ // clang-format off
+ const std::string defs = R"(
+OpCapability RuntimeDescriptorArray
OpCapability RayTracingNV
OpExtension "SPV_EXT_descriptor_indexing"
OpExtension "SPV_NV_ray_tracing"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint AnyHitNV %main "main"
+; CHECK: OpEntryPoint AnyHitNV %main "main" %89
OpSource GLSL 460
OpSourceExtension "GL_EXT_nonuniform_qualifier"
OpSourceExtension "GL_NV_ray_tracing"
@@ -5526,51 +3350,11 @@ OpDecorate %sbo Binding 0
OpDecorate %images DescriptorSet 0
OpDecorate %images Binding 1
OpDecorate %images NonWritable
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kInputDecorations + kOutputDecorations + R"(
+; CHECK: OpDecorate %89 BuiltIn LaunchIdNV
%void = OpTypeVoid
-)";
-
- const std::string defs_after =
- R"(OpCapability RuntimeDescriptorArray
-OpCapability RayTracingNV
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_NV_ray_tracing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint AnyHitNV %main "main" %89
-OpSource GLSL 460
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpSourceExtension "GL_NV_ray_tracing"
-OpName %main "main"
-OpName %StorageBuffer "StorageBuffer"
-OpMemberName %StorageBuffer 0 "index"
-OpMemberName %StorageBuffer 1 "red"
-OpName %sbo "sbo"
-OpName %images "images"
-OpMemberDecorate %StorageBuffer 0 Offset 0
-OpMemberDecorate %StorageBuffer 1 Offset 4
-OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
-OpDecorate %images NonWritable
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_39 Block
-OpMemberDecorate %_struct_39 0 Offset 0
-OpDecorate %41 DescriptorSet 7
-OpDecorate %41 Binding 1
-OpDecorate %_struct_63 Block
-OpMemberDecorate %_struct_63 0 Offset 0
-OpMemberDecorate %_struct_63 1 Offset 4
-OpDecorate %65 DescriptorSet 7
-OpDecorate %65 Binding 0
-OpDecorate %89 BuiltIn LaunchIdNV
-%void = OpTypeVoid
-)";
-
- const std::string func_before =
- R"(%3 = OpTypeFunction %void
+%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%StorageBuffer = OpTypeStruct %uint %float
@@ -5590,6 +3374,38 @@ OpDecorate %89 BuiltIn LaunchIdNV
%v4float = OpTypeVector %float 4
%uint_0 = OpConstant %uint 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %34 = OpTypeFunction %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kInputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %bool = OpTypeBool
+; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint
+)" + kOutputGlobals + R"(
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_5315 = OpConstant %uint 5315
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %v3uint = OpTypeVector %uint 3
+; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
+; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_6 = OpConstant %uint 6
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_51 = OpConstant %uint 51
+; CHECK: %113 = OpConstantNull %v4float
+; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint
+; CHECK: %uint_48 = OpConstant %uint 48
+; CHECK: %141 = OpConstantNull %uint
+; CHECK: %uint_54 = OpConstant %uint 54
+)";
+ // clang-format on
+
+ const std::string main_func = R"(
%main = OpFunction %void None %3
%5 = OpLabel
%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
@@ -5600,211 +3416,69 @@ OpDecorate %89 BuiltIn LaunchIdNV
%29 = OpCompositeExtract %float %27 0
%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
OpStore %31 %29
+; CHECK-NOT: OpStore %31 %29
+; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
+; CHECK: %134 = OpULessThan %bool %uint_0 %133
+; CHECK: OpSelectionMerge %135 None
+; CHECK: OpBranchConditional %134 %136 %137
+; CHECK: %136 = OpLabel
+; CHECK: %138 = OpLoad %uint %25
+; CHECK: OpBranch %135
+; CHECK: %137 = OpLabel
+; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %135
+; CHECK: %135 = OpLabel
+; CHECK: %142 = OpPhi %uint %138 %136 %141 %137
+; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+; CHECK: %28 = OpLoad %13 %27
+; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
+; CHECK: %50 = OpULessThan %bool %142 %48
+; CHECK: OpSelectionMerge %51 None
+; CHECK: OpBranchConditional %50 %52 %53
+; CHECK: %52 = OpLabel
+; CHECK: %54 = OpLoad %13 %27
+; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
+; CHECK: %144 = OpULessThan %bool %uint_0 %143
+; CHECK: OpSelectionMerge %145 None
+; CHECK: OpBranchConditional %144 %146 %147
+; CHECK: %146 = OpLabel
+; CHECK: %148 = OpLoad %13 %27
+; CHECK: %149 = OpImageRead %v4float %148 %20
+; CHECK: OpBranch %145
+; CHECK: %147 = OpLabel
+; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
+; CHECK: OpBranch %145
+; CHECK: %145 = OpLabel
+; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147
+; CHECK: OpBranch %51
+; CHECK: %53 = OpLabel
+; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
+; CHECK: OpBranch %51
+; CHECK: %51 = OpLabel
+; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53
+; CHECK: %30 = OpCompositeExtract %float %114 0
+; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
+; CHECK: %153 = OpULessThan %bool %uint_0 %152
+; CHECK: OpSelectionMerge %154 None
+; CHECK: OpBranchConditional %153 %155 %156
+; CHECK: %155 = OpLabel
+; CHECK: OpStore %31 %30
+; CHECK: OpBranch %154
+; CHECK: %156 = OpLabel
+; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %154
+; CHECK: %154 = OpLabel
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%7 = OpTypeFunction %void
-%uint = OpTypeInt 32 0
-%float = OpTypeFloat 32
-%StorageBuffer = OpTypeStruct %uint %float
-%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
-%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
-%int = OpTypeInt 32 1
-%int_1 = OpConstant %int 1
-%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
-%_runtimearr_13 = OpTypeRuntimeArray %13
-%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
-%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_uint = OpTypePointer Uniform %uint
-%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
-%v2int = OpTypeVector %int 2
-%20 = OpConstantComposite %v2int %int_0 %int_0
-%v4float = OpTypeVector %float 4
-%uint_0 = OpConstant %uint 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%uint_1 = OpConstant %uint 1
-%34 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_39 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
-%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%57 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_63 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63
-%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_5315 = OpConstant %uint 5315
-%uint_3 = OpConstant %uint 3
-%v3uint = OpTypeVector %uint 3
-%_ptr_Input_v3uint = OpTypePointer Input %v3uint
-%89 = OpVariable %_ptr_Input_v3uint Input
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_51 = OpConstant %uint 51
-%113 = OpConstantNull %v4float
-%116 = OpTypeFunction %uint %uint %uint %uint %uint
-%uint_48 = OpConstant %uint 48
-%141 = OpConstantNull %uint
-%uint_54 = OpConstant %uint 54
-%main = OpFunction %void None %7
-%24 = OpLabel
-%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
-%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
-%134 = OpULessThan %bool %uint_0 %133
-OpSelectionMerge %135 None
-OpBranchConditional %134 %136 %137
-%136 = OpLabel
-%138 = OpLoad %uint %25
-OpBranch %135
-%137 = OpLabel
-%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0
-OpBranch %135
-%135 = OpLabel
-%142 = OpPhi %uint %138 %136 %141 %137
-%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
-%28 = OpLoad %13 %27
-%48 = OpFunctionCall %uint %33 %uint_1 %uint_1
-%50 = OpULessThan %bool %142 %48
-OpSelectionMerge %51 None
-OpBranchConditional %50 %52 %53
-%52 = OpLabel
-%54 = OpLoad %13 %27
-%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142
-%144 = OpULessThan %bool %uint_0 %143
-OpSelectionMerge %145 None
-OpBranchConditional %144 %146 %147
-%146 = OpLabel
-%148 = OpLoad %13 %27
-%149 = OpImageRead %v4float %148 %20
-OpBranch %145
-%147 = OpLabel
-%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0
-OpBranch %145
-%145 = OpLabel
-%151 = OpPhi %v4float %149 %146 %113 %147
-OpBranch %51
-%53 = OpLabel
-%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48
-OpBranch %51
-%51 = OpLabel
-%114 = OpPhi %v4float %151 %145 %113 %53
-%30 = OpCompositeExtract %float %114 0
-%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
-%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
-%153 = OpULessThan %bool %uint_0 %152
-OpSelectionMerge %154 None
-OpBranchConditional %153 %155 %156
-%155 = OpLabel
-OpStore %31 %30
-OpBranch %154
-%156 = OpLabel
-%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0
-OpBranch %154
-%154 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%33 = OpFunction %uint None %34
-%35 = OpFunctionParameter %uint
-%36 = OpFunctionParameter %uint
-%37 = OpLabel
-%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35
-%44 = OpLoad %uint %43
-%45 = OpIAdd %uint %44 %36
-%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45
-%47 = OpLoad %uint %46
-OpReturnValue %47
-OpFunctionEnd
-%56 = OpFunction %void None %57
-%58 = OpFunctionParameter %uint
-%59 = OpFunctionParameter %uint
-%60 = OpFunctionParameter %uint
-%61 = OpFunctionParameter %uint
-%62 = OpLabel
-%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0
-%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10
-%70 = OpIAdd %uint %69 %uint_10
-%71 = OpArrayLength %uint %65 1
-%72 = OpULessThanEqual %bool %70 %71
-OpSelectionMerge %73 None
-OpBranchConditional %72 %74 %73
-%74 = OpLabel
-%75 = OpIAdd %uint %69 %uint_0
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75
-OpStore %76 %uint_10
-%78 = OpIAdd %uint %69 %uint_1
-%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78
-OpStore %79 %uint_23
-%81 = OpIAdd %uint %69 %uint_2
-%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81
-OpStore %82 %58
-%85 = OpIAdd %uint %69 %uint_3
-%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85
-OpStore %86 %uint_5315
-%90 = OpLoad %v3uint %89
-%91 = OpCompositeExtract %uint %90 0
-%92 = OpCompositeExtract %uint %90 1
-%93 = OpCompositeExtract %uint %90 2
-%94 = OpIAdd %uint %69 %uint_4
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94
-OpStore %95 %91
-%97 = OpIAdd %uint %69 %uint_5
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97
-OpStore %98 %92
-%100 = OpIAdd %uint %69 %uint_6
-%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100
-OpStore %101 %93
-%103 = OpIAdd %uint %69 %uint_7
-%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103
-OpStore %104 %59
-%106 = OpIAdd %uint %69 %uint_8
-%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106
-OpStore %107 %60
-%109 = OpIAdd %uint %69 %uint_9
-%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109
-OpStore %110 %61
-OpBranch %73
-%73 = OpLabel
-OpReturn
-OpFunctionEnd
-%115 = OpFunction %uint None %116
-%117 = OpFunctionParameter %uint
-%118 = OpFunctionParameter %uint
-%119 = OpFunctionParameter %uint
-%120 = OpFunctionParameter %uint
-%121 = OpLabel
-%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117
-%123 = OpLoad %uint %122
-%124 = OpIAdd %uint %123 %118
-%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124
-%126 = OpLoad %uint %125
-%127 = OpIAdd %uint %126 %119
-%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127
-%129 = OpLoad %uint %128
-%130 = OpIAdd %uint %129 %120
-%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130
-%132 = OpLoad %uint %131
-OpReturnValue %132
-OpFunctionEnd
-)";
+ const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
+ true, 7u, 23u, true, true, false,
+ false, false);
}
TEST_F(InstBindlessTest,
@@ -5825,14 +3499,17 @@ TEST_F(InstBindlessTest,
// sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r;
// }
- const std::string defs_before =
- R"(OpCapability RuntimeDescriptorArray
+ // clang-format off
+ const std::string defs = R"(
+OpCapability RuntimeDescriptorArray
OpCapability RayTracingNV
OpExtension "SPV_EXT_descriptor_indexing"
OpExtension "SPV_NV_ray_tracing"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint ClosestHitNV %main "main"
+; CHECK: OpEntryPoint ClosestHitNV %main "main" %89
OpSource GLSL 460
OpSourceExtension "GL_EXT_nonuniform_qualifier"
OpSourceExtension "GL_NV_ray_tracing"
@@ -5850,51 +3527,11 @@ OpDecorate %sbo Binding 0
OpDecorate %images DescriptorSet 0
OpDecorate %images Binding 1
OpDecorate %images NonWritable
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kInputDecorations + kOutputDecorations + R"(
+; CHECK: OpDecorate %89 BuiltIn LaunchIdNV
%void = OpTypeVoid
-)";
-
- const std::string defs_after =
- R"(OpCapability RuntimeDescriptorArray
-OpCapability RayTracingNV
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_NV_ray_tracing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint ClosestHitNV %main "main" %89
-OpSource GLSL 460
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpSourceExtension "GL_NV_ray_tracing"
-OpName %main "main"
-OpName %StorageBuffer "StorageBuffer"
-OpMemberName %StorageBuffer 0 "index"
-OpMemberName %StorageBuffer 1 "red"
-OpName %sbo "sbo"
-OpName %images "images"
-OpMemberDecorate %StorageBuffer 0 Offset 0
-OpMemberDecorate %StorageBuffer 1 Offset 4
-OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
-OpDecorate %images NonWritable
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_39 Block
-OpMemberDecorate %_struct_39 0 Offset 0
-OpDecorate %41 DescriptorSet 7
-OpDecorate %41 Binding 1
-OpDecorate %_struct_63 Block
-OpMemberDecorate %_struct_63 0 Offset 0
-OpMemberDecorate %_struct_63 1 Offset 4
-OpDecorate %65 DescriptorSet 7
-OpDecorate %65 Binding 0
-OpDecorate %89 BuiltIn LaunchIdNV
-%void = OpTypeVoid
-)";
-
- const std::string func_before =
- R"(%3 = OpTypeFunction %void
+%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%StorageBuffer = OpTypeStruct %uint %float
@@ -5914,6 +3551,38 @@ OpDecorate %89 BuiltIn LaunchIdNV
%v4float = OpTypeVector %float 4
%uint_0 = OpConstant %uint 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %34 = OpTypeFunction %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kInputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %bool = OpTypeBool
+; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint
+)" + kOutputGlobals + R"(
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_5316 = OpConstant %uint 5316
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %v3uint = OpTypeVector %uint 3
+; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
+; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_6 = OpConstant %uint 6
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_51 = OpConstant %uint 51
+; CHECK: %113 = OpConstantNull %v4float
+; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint
+; CHECK: %uint_48 = OpConstant %uint 48
+; CHECK: %141 = OpConstantNull %uint
+; CHECK: %uint_54 = OpConstant %uint 54
+)";
+ // clang-format on
+
+ const std::string main_func = R"(
%main = OpFunction %void None %3
%5 = OpLabel
%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
@@ -5924,211 +3593,69 @@ OpDecorate %89 BuiltIn LaunchIdNV
%29 = OpCompositeExtract %float %27 0
%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
OpStore %31 %29
+; CHECK-NOT: OpStore %31 %29
+; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
+; CHECK: %134 = OpULessThan %bool %uint_0 %133
+; CHECK: OpSelectionMerge %135 None
+; CHECK: OpBranchConditional %134 %136 %137
+; CHECK: %136 = OpLabel
+; CHECK: %138 = OpLoad %uint %25
+; CHECK: OpBranch %135
+; CHECK: %137 = OpLabel
+; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %135
+; CHECK: %135 = OpLabel
+; CHECK: %142 = OpPhi %uint %138 %136 %141 %137
+; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+; CHECK: %28 = OpLoad %13 %27
+; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
+; CHECK: %50 = OpULessThan %bool %142 %48
+; CHECK: OpSelectionMerge %51 None
+; CHECK: OpBranchConditional %50 %52 %53
+; CHECK: %52 = OpLabel
+; CHECK: %54 = OpLoad %13 %27
+; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
+; CHECK: %144 = OpULessThan %bool %uint_0 %143
+; CHECK: OpSelectionMerge %145 None
+; CHECK: OpBranchConditional %144 %146 %147
+; CHECK: %146 = OpLabel
+; CHECK: %148 = OpLoad %13 %27
+; CHECK: %149 = OpImageRead %v4float %148 %20
+; CHECK: OpBranch %145
+; CHECK: %147 = OpLabel
+; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
+; CHECK: OpBranch %145
+; CHECK: %145 = OpLabel
+; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147
+; CHECK: OpBranch %51
+; CHECK: %53 = OpLabel
+; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
+; CHECK: OpBranch %51
+; CHECK: %51 = OpLabel
+; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53
+; CHECK: %30 = OpCompositeExtract %float %114 0
+; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
+; CHECK: %153 = OpULessThan %bool %uint_0 %152
+; CHECK: OpSelectionMerge %154 None
+; CHECK: OpBranchConditional %153 %155 %156
+; CHECK: %155 = OpLabel
+; CHECK: OpStore %31 %30
+; CHECK: OpBranch %154
+; CHECK: %156 = OpLabel
+; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %154
+; CHECK: %154 = OpLabel
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%7 = OpTypeFunction %void
-%uint = OpTypeInt 32 0
-%float = OpTypeFloat 32
-%StorageBuffer = OpTypeStruct %uint %float
-%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
-%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
-%int = OpTypeInt 32 1
-%int_1 = OpConstant %int 1
-%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
-%_runtimearr_13 = OpTypeRuntimeArray %13
-%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
-%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_uint = OpTypePointer Uniform %uint
-%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
-%v2int = OpTypeVector %int 2
-%20 = OpConstantComposite %v2int %int_0 %int_0
-%v4float = OpTypeVector %float 4
-%uint_0 = OpConstant %uint 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%uint_1 = OpConstant %uint 1
-%34 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_39 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
-%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%57 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_63 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63
-%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_5316 = OpConstant %uint 5316
-%uint_3 = OpConstant %uint 3
-%v3uint = OpTypeVector %uint 3
-%_ptr_Input_v3uint = OpTypePointer Input %v3uint
-%89 = OpVariable %_ptr_Input_v3uint Input
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_51 = OpConstant %uint 51
-%113 = OpConstantNull %v4float
-%116 = OpTypeFunction %uint %uint %uint %uint %uint
-%uint_48 = OpConstant %uint 48
-%141 = OpConstantNull %uint
-%uint_54 = OpConstant %uint 54
-%main = OpFunction %void None %7
-%24 = OpLabel
-%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
-%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
-%134 = OpULessThan %bool %uint_0 %133
-OpSelectionMerge %135 None
-OpBranchConditional %134 %136 %137
-%136 = OpLabel
-%138 = OpLoad %uint %25
-OpBranch %135
-%137 = OpLabel
-%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0
-OpBranch %135
-%135 = OpLabel
-%142 = OpPhi %uint %138 %136 %141 %137
-%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
-%28 = OpLoad %13 %27
-%48 = OpFunctionCall %uint %33 %uint_1 %uint_1
-%50 = OpULessThan %bool %142 %48
-OpSelectionMerge %51 None
-OpBranchConditional %50 %52 %53
-%52 = OpLabel
-%54 = OpLoad %13 %27
-%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142
-%144 = OpULessThan %bool %uint_0 %143
-OpSelectionMerge %145 None
-OpBranchConditional %144 %146 %147
-%146 = OpLabel
-%148 = OpLoad %13 %27
-%149 = OpImageRead %v4float %148 %20
-OpBranch %145
-%147 = OpLabel
-%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0
-OpBranch %145
-%145 = OpLabel
-%151 = OpPhi %v4float %149 %146 %113 %147
-OpBranch %51
-%53 = OpLabel
-%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48
-OpBranch %51
-%51 = OpLabel
-%114 = OpPhi %v4float %151 %145 %113 %53
-%30 = OpCompositeExtract %float %114 0
-%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
-%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
-%153 = OpULessThan %bool %uint_0 %152
-OpSelectionMerge %154 None
-OpBranchConditional %153 %155 %156
-%155 = OpLabel
-OpStore %31 %30
-OpBranch %154
-%156 = OpLabel
-%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0
-OpBranch %154
-%154 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%33 = OpFunction %uint None %34
-%35 = OpFunctionParameter %uint
-%36 = OpFunctionParameter %uint
-%37 = OpLabel
-%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35
-%44 = OpLoad %uint %43
-%45 = OpIAdd %uint %44 %36
-%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45
-%47 = OpLoad %uint %46
-OpReturnValue %47
-OpFunctionEnd
-%56 = OpFunction %void None %57
-%58 = OpFunctionParameter %uint
-%59 = OpFunctionParameter %uint
-%60 = OpFunctionParameter %uint
-%61 = OpFunctionParameter %uint
-%62 = OpLabel
-%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0
-%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10
-%70 = OpIAdd %uint %69 %uint_10
-%71 = OpArrayLength %uint %65 1
-%72 = OpULessThanEqual %bool %70 %71
-OpSelectionMerge %73 None
-OpBranchConditional %72 %74 %73
-%74 = OpLabel
-%75 = OpIAdd %uint %69 %uint_0
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75
-OpStore %76 %uint_10
-%78 = OpIAdd %uint %69 %uint_1
-%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78
-OpStore %79 %uint_23
-%81 = OpIAdd %uint %69 %uint_2
-%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81
-OpStore %82 %58
-%85 = OpIAdd %uint %69 %uint_3
-%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85
-OpStore %86 %uint_5316
-%90 = OpLoad %v3uint %89
-%91 = OpCompositeExtract %uint %90 0
-%92 = OpCompositeExtract %uint %90 1
-%93 = OpCompositeExtract %uint %90 2
-%94 = OpIAdd %uint %69 %uint_4
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94
-OpStore %95 %91
-%97 = OpIAdd %uint %69 %uint_5
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97
-OpStore %98 %92
-%100 = OpIAdd %uint %69 %uint_6
-%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100
-OpStore %101 %93
-%103 = OpIAdd %uint %69 %uint_7
-%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103
-OpStore %104 %59
-%106 = OpIAdd %uint %69 %uint_8
-%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106
-OpStore %107 %60
-%109 = OpIAdd %uint %69 %uint_9
-%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109
-OpStore %110 %61
-OpBranch %73
-%73 = OpLabel
-OpReturn
-OpFunctionEnd
-%115 = OpFunction %uint None %116
-%117 = OpFunctionParameter %uint
-%118 = OpFunctionParameter %uint
-%119 = OpFunctionParameter %uint
-%120 = OpFunctionParameter %uint
-%121 = OpLabel
-%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117
-%123 = OpLoad %uint %122
-%124 = OpIAdd %uint %123 %118
-%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124
-%126 = OpLoad %uint %125
-%127 = OpIAdd %uint %126 %119
-%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127
-%129 = OpLoad %uint %128
-%130 = OpIAdd %uint %129 %120
-%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130
-%132 = OpLoad %uint %131
-OpReturnValue %132
-OpFunctionEnd
-)";
+ const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
+ true, 7u, 23u, true, true, false,
+ false, false);
}
TEST_F(InstBindlessTest,
@@ -6149,14 +3676,17 @@ TEST_F(InstBindlessTest,
// sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r;
// }
- const std::string defs_before =
- R"(OpCapability RuntimeDescriptorArray
+ // clang-format off
+ const std::string defs = R"(
+OpCapability RuntimeDescriptorArray
OpCapability RayTracingNV
OpExtension "SPV_EXT_descriptor_indexing"
OpExtension "SPV_NV_ray_tracing"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint MissNV %main "main"
+; CHECK: OpEntryPoint MissNV %main "main" %89
OpSource GLSL 460
OpSourceExtension "GL_EXT_nonuniform_qualifier"
OpSourceExtension "GL_NV_ray_tracing"
@@ -6174,51 +3704,11 @@ OpDecorate %sbo Binding 0
OpDecorate %images DescriptorSet 0
OpDecorate %images Binding 1
OpDecorate %images NonWritable
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kInputDecorations + kOutputDecorations + R"(
+; CHECK: OpDecorate %89 BuiltIn LaunchIdNV
%void = OpTypeVoid
-)";
-
- const std::string defs_after =
- R"(OpCapability RuntimeDescriptorArray
-OpCapability RayTracingNV
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_NV_ray_tracing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint MissNV %main "main" %89
-OpSource GLSL 460
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpSourceExtension "GL_NV_ray_tracing"
-OpName %main "main"
-OpName %StorageBuffer "StorageBuffer"
-OpMemberName %StorageBuffer 0 "index"
-OpMemberName %StorageBuffer 1 "red"
-OpName %sbo "sbo"
-OpName %images "images"
-OpMemberDecorate %StorageBuffer 0 Offset 0
-OpMemberDecorate %StorageBuffer 1 Offset 4
-OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
-OpDecorate %images NonWritable
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_39 Block
-OpMemberDecorate %_struct_39 0 Offset 0
-OpDecorate %41 DescriptorSet 7
-OpDecorate %41 Binding 1
-OpDecorate %_struct_63 Block
-OpMemberDecorate %_struct_63 0 Offset 0
-OpMemberDecorate %_struct_63 1 Offset 4
-OpDecorate %65 DescriptorSet 7
-OpDecorate %65 Binding 0
-OpDecorate %89 BuiltIn LaunchIdNV
-%void = OpTypeVoid
-)";
-
- const std::string func_before =
- R"(%3 = OpTypeFunction %void
+%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%StorageBuffer = OpTypeStruct %uint %float
@@ -6238,6 +3728,38 @@ OpDecorate %89 BuiltIn LaunchIdNV
%v4float = OpTypeVector %float 4
%uint_0 = OpConstant %uint 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %34 = OpTypeFunction %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kInputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %bool = OpTypeBool
+; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint
+)" + kOutputGlobals + R"(
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_5317 = OpConstant %uint 5317
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %v3uint = OpTypeVector %uint 3
+; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
+; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_6 = OpConstant %uint 6
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_51 = OpConstant %uint 51
+; CHECK: %113 = OpConstantNull %v4float
+; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint
+; CHECK: %uint_48 = OpConstant %uint 48
+; CHECK: %141 = OpConstantNull %uint
+; CHECK: %uint_54 = OpConstant %uint 54
+)";
+ // clang-format on
+
+ const std::string main_func = R"(
%main = OpFunction %void None %3
%5 = OpLabel
%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
@@ -6248,211 +3770,69 @@ OpDecorate %89 BuiltIn LaunchIdNV
%29 = OpCompositeExtract %float %27 0
%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
OpStore %31 %29
+; CHECK-NOT OpStore %31 %29
+; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
+; CHECK: %134 = OpULessThan %bool %uint_0 %133
+; CHECK: OpSelectionMerge %135 None
+; CHECK: OpBranchConditional %134 %136 %137
+; CHECK: %136 = OpLabel
+; CHECK: %138 = OpLoad %uint %25
+; CHECK: OpBranch %135
+; CHECK: %137 = OpLabel
+; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %135
+; CHECK: %135 = OpLabel
+; CHECK: %142 = OpPhi %uint %138 %136 %141 %137
+; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+; CHECK: %28 = OpLoad %13 %27
+; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
+; CHECK: %50 = OpULessThan %bool %142 %48
+; CHECK: OpSelectionMerge %51 None
+; CHECK: OpBranchConditional %50 %52 %53
+; CHECK: %52 = OpLabel
+; CHECK: %54 = OpLoad %13 %27
+; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
+; CHECK: %144 = OpULessThan %bool %uint_0 %143
+; CHECK: OpSelectionMerge %145 None
+; CHECK: OpBranchConditional %144 %146 %147
+; CHECK: %146 = OpLabel
+; CHECK: %148 = OpLoad %13 %27
+; CHECK: %149 = OpImageRead %v4float %148 %20
+; CHECK: OpBranch %145
+; CHECK: %147 = OpLabel
+; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
+; CHECK: OpBranch %145
+; CHECK: %145 = OpLabel
+; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147
+; CHECK: OpBranch %51
+; CHECK: %53 = OpLabel
+; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
+; CHECK: OpBranch %51
+; CHECK: %51 = OpLabel
+; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53
+; CHECK: %30 = OpCompositeExtract %float %114 0
+; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
+; CHECK: %153 = OpULessThan %bool %uint_0 %152
+; CHECK: OpSelectionMerge %154 None
+; CHECK: OpBranchConditional %153 %155 %156
+; CHECK: %155 = OpLabel
+; CHECK: OpStore %31 %30
+; CHECK: OpBranch %154
+; CHECK: %156 = OpLabel
+; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %154
+; CHECK: %154 = OpLabel
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%7 = OpTypeFunction %void
-%uint = OpTypeInt 32 0
-%float = OpTypeFloat 32
-%StorageBuffer = OpTypeStruct %uint %float
-%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
-%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
-%int = OpTypeInt 32 1
-%int_1 = OpConstant %int 1
-%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
-%_runtimearr_13 = OpTypeRuntimeArray %13
-%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
-%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_uint = OpTypePointer Uniform %uint
-%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
-%v2int = OpTypeVector %int 2
-%20 = OpConstantComposite %v2int %int_0 %int_0
-%v4float = OpTypeVector %float 4
-%uint_0 = OpConstant %uint 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%uint_1 = OpConstant %uint 1
-%34 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_39 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
-%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%57 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_63 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63
-%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_5317 = OpConstant %uint 5317
-%uint_3 = OpConstant %uint 3
-%v3uint = OpTypeVector %uint 3
-%_ptr_Input_v3uint = OpTypePointer Input %v3uint
-%89 = OpVariable %_ptr_Input_v3uint Input
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_51 = OpConstant %uint 51
-%113 = OpConstantNull %v4float
-%116 = OpTypeFunction %uint %uint %uint %uint %uint
-%uint_48 = OpConstant %uint 48
-%141 = OpConstantNull %uint
-%uint_54 = OpConstant %uint 54
-%main = OpFunction %void None %7
-%24 = OpLabel
-%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
-%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
-%134 = OpULessThan %bool %uint_0 %133
-OpSelectionMerge %135 None
-OpBranchConditional %134 %136 %137
-%136 = OpLabel
-%138 = OpLoad %uint %25
-OpBranch %135
-%137 = OpLabel
-%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0
-OpBranch %135
-%135 = OpLabel
-%142 = OpPhi %uint %138 %136 %141 %137
-%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
-%28 = OpLoad %13 %27
-%48 = OpFunctionCall %uint %33 %uint_1 %uint_1
-%50 = OpULessThan %bool %142 %48
-OpSelectionMerge %51 None
-OpBranchConditional %50 %52 %53
-%52 = OpLabel
-%54 = OpLoad %13 %27
-%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142
-%144 = OpULessThan %bool %uint_0 %143
-OpSelectionMerge %145 None
-OpBranchConditional %144 %146 %147
-%146 = OpLabel
-%148 = OpLoad %13 %27
-%149 = OpImageRead %v4float %148 %20
-OpBranch %145
-%147 = OpLabel
-%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0
-OpBranch %145
-%145 = OpLabel
-%151 = OpPhi %v4float %149 %146 %113 %147
-OpBranch %51
-%53 = OpLabel
-%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48
-OpBranch %51
-%51 = OpLabel
-%114 = OpPhi %v4float %151 %145 %113 %53
-%30 = OpCompositeExtract %float %114 0
-%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
-%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
-%153 = OpULessThan %bool %uint_0 %152
-OpSelectionMerge %154 None
-OpBranchConditional %153 %155 %156
-%155 = OpLabel
-OpStore %31 %30
-OpBranch %154
-%156 = OpLabel
-%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0
-OpBranch %154
-%154 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%33 = OpFunction %uint None %34
-%35 = OpFunctionParameter %uint
-%36 = OpFunctionParameter %uint
-%37 = OpLabel
-%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35
-%44 = OpLoad %uint %43
-%45 = OpIAdd %uint %44 %36
-%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45
-%47 = OpLoad %uint %46
-OpReturnValue %47
-OpFunctionEnd
-%56 = OpFunction %void None %57
-%58 = OpFunctionParameter %uint
-%59 = OpFunctionParameter %uint
-%60 = OpFunctionParameter %uint
-%61 = OpFunctionParameter %uint
-%62 = OpLabel
-%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0
-%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10
-%70 = OpIAdd %uint %69 %uint_10
-%71 = OpArrayLength %uint %65 1
-%72 = OpULessThanEqual %bool %70 %71
-OpSelectionMerge %73 None
-OpBranchConditional %72 %74 %73
-%74 = OpLabel
-%75 = OpIAdd %uint %69 %uint_0
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75
-OpStore %76 %uint_10
-%78 = OpIAdd %uint %69 %uint_1
-%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78
-OpStore %79 %uint_23
-%81 = OpIAdd %uint %69 %uint_2
-%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81
-OpStore %82 %58
-%85 = OpIAdd %uint %69 %uint_3
-%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85
-OpStore %86 %uint_5317
-%90 = OpLoad %v3uint %89
-%91 = OpCompositeExtract %uint %90 0
-%92 = OpCompositeExtract %uint %90 1
-%93 = OpCompositeExtract %uint %90 2
-%94 = OpIAdd %uint %69 %uint_4
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94
-OpStore %95 %91
-%97 = OpIAdd %uint %69 %uint_5
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97
-OpStore %98 %92
-%100 = OpIAdd %uint %69 %uint_6
-%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100
-OpStore %101 %93
-%103 = OpIAdd %uint %69 %uint_7
-%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103
-OpStore %104 %59
-%106 = OpIAdd %uint %69 %uint_8
-%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106
-OpStore %107 %60
-%109 = OpIAdd %uint %69 %uint_9
-%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109
-OpStore %110 %61
-OpBranch %73
-%73 = OpLabel
-OpReturn
-OpFunctionEnd
-%115 = OpFunction %uint None %116
-%117 = OpFunctionParameter %uint
-%118 = OpFunctionParameter %uint
-%119 = OpFunctionParameter %uint
-%120 = OpFunctionParameter %uint
-%121 = OpLabel
-%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117
-%123 = OpLoad %uint %122
-%124 = OpIAdd %uint %123 %118
-%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124
-%126 = OpLoad %uint %125
-%127 = OpIAdd %uint %126 %119
-%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127
-%129 = OpLoad %uint %128
-%130 = OpIAdd %uint %129 %120
-%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130
-%132 = OpLoad %uint %131
-OpReturnValue %132
-OpFunctionEnd
-)";
+ const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
+ true, 7u, 23u, true, true, false,
+ false, false);
}
TEST_F(InstBindlessTest,
@@ -6473,14 +3853,17 @@ TEST_F(InstBindlessTest,
// sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r;
// }
- const std::string defs_before =
- R"(OpCapability RuntimeDescriptorArray
+ // clang-format off
+ const std::string defs = R"(
+OpCapability RuntimeDescriptorArray
OpCapability RayTracingNV
OpExtension "SPV_EXT_descriptor_indexing"
OpExtension "SPV_NV_ray_tracing"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint CallableNV %main "main"
+; CHECK: OpEntryPoint CallableNV %main "main" %89
OpSource GLSL 460
OpSourceExtension "GL_EXT_nonuniform_qualifier"
OpSourceExtension "GL_NV_ray_tracing"
@@ -6498,51 +3881,11 @@ OpDecorate %sbo Binding 0
OpDecorate %images DescriptorSet 0
OpDecorate %images Binding 1
OpDecorate %images NonWritable
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kInputDecorations + kOutputDecorations + R"(
+; CHECK: OpDecorate %89 BuiltIn LaunchIdNV
%void = OpTypeVoid
-)";
-
- const std::string defs_after =
- R"(OpCapability RuntimeDescriptorArray
-OpCapability RayTracingNV
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_NV_ray_tracing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint CallableNV %main "main" %89
-OpSource GLSL 460
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpSourceExtension "GL_NV_ray_tracing"
-OpName %main "main"
-OpName %StorageBuffer "StorageBuffer"
-OpMemberName %StorageBuffer 0 "index"
-OpMemberName %StorageBuffer 1 "red"
-OpName %sbo "sbo"
-OpName %images "images"
-OpMemberDecorate %StorageBuffer 0 Offset 0
-OpMemberDecorate %StorageBuffer 1 Offset 4
-OpDecorate %StorageBuffer BufferBlock
-OpDecorate %sbo DescriptorSet 0
-OpDecorate %sbo Binding 0
-OpDecorate %images DescriptorSet 0
-OpDecorate %images Binding 1
-OpDecorate %images NonWritable
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_39 Block
-OpMemberDecorate %_struct_39 0 Offset 0
-OpDecorate %41 DescriptorSet 7
-OpDecorate %41 Binding 1
-OpDecorate %_struct_63 Block
-OpMemberDecorate %_struct_63 0 Offset 0
-OpMemberDecorate %_struct_63 1 Offset 4
-OpDecorate %65 DescriptorSet 7
-OpDecorate %65 Binding 0
-OpDecorate %89 BuiltIn LaunchIdNV
-%void = OpTypeVoid
-)";
-
- const std::string func_before =
- R"(%3 = OpTypeFunction %void
+%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%StorageBuffer = OpTypeStruct %uint %float
@@ -6562,6 +3905,38 @@ OpDecorate %89 BuiltIn LaunchIdNV
%v4float = OpTypeVector %float 4
%uint_0 = OpConstant %uint 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %34 = OpTypeFunction %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kInputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %bool = OpTypeBool
+; CHECK: %57 = OpTypeFunction %void %uint %uint %uint %uint
+)" + kOutputGlobals + R"(
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_5318 = OpConstant %uint 5318
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %v3uint = OpTypeVector %uint 3
+; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
+; CHECK: %89 = OpVariable %_ptr_Input_v3uint Input
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_6 = OpConstant %uint 6
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_51 = OpConstant %uint 51
+; CHECK: %113 = OpConstantNull %v4float
+; CHECK: %116 = OpTypeFunction %uint %uint %uint %uint %uint
+; CHECK: %uint_48 = OpConstant %uint 48
+; CHECK: %141 = OpConstantNull %uint
+; CHECK: %uint_54 = OpConstant %uint 54
+)";
+ // clang-format on
+
+ const std::string main_func = R"(
%main = OpFunction %void None %3
%5 = OpLabel
%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
@@ -6572,211 +3947,69 @@ OpDecorate %89 BuiltIn LaunchIdNV
%29 = OpCompositeExtract %float %27 0
%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
OpStore %31 %29
+; CHECK-NOT: OpStore %31 %29
+; CHECK: %133 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
+; CHECK: %134 = OpULessThan %bool %uint_0 %133
+; CHECK: OpSelectionMerge %135 None
+; CHECK: OpBranchConditional %134 %136 %137
+; CHECK: %136 = OpLabel
+; CHECK: %138 = OpLoad %uint %25
+; CHECK: OpBranch %135
+; CHECK: %137 = OpLabel
+; CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_48 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %135
+; CHECK: %135 = OpLabel
+; CHECK: %142 = OpPhi %uint %138 %136 %141 %137
+; CHECK: %27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
+; CHECK: %28 = OpLoad %13 %27
+; CHECK: %48 = OpFunctionCall %uint %inst_bindless_direct_read_2 %uint_1 %uint_1
+; CHECK: %50 = OpULessThan %bool %142 %48
+; CHECK: OpSelectionMerge %51 None
+; CHECK: OpBranchConditional %50 %52 %53
+; CHECK: %52 = OpLabel
+; CHECK: %54 = OpLoad %13 %27
+; CHECK: %143 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_1 %142
+; CHECK: %144 = OpULessThan %bool %uint_0 %143
+; CHECK: OpSelectionMerge %145 None
+; CHECK: OpBranchConditional %144 %146 %147
+; CHECK: %146 = OpLabel
+; CHECK: %148 = OpLoad %13 %27
+; CHECK: %149 = OpImageRead %v4float %148 %20
+; CHECK: OpBranch %145
+; CHECK: %147 = OpLabel
+; CHECK: %150 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_1 %142 %uint_0
+; CHECK: OpBranch %145
+; CHECK: %145 = OpLabel
+; CHECK: %151 = OpPhi %v4float %149 %146 %113 %147
+; CHECK: OpBranch %51
+; CHECK: %53 = OpLabel
+; CHECK: %112 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_51 %uint_0 %142 %48
+; CHECK: OpBranch %51
+; CHECK: %51 = OpLabel
+; CHECK: %114 = OpPhi %v4float %151 %145 %113 %53
+; CHECK: %30 = OpCompositeExtract %float %114 0
+; CHECK: %31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+; CHECK: %152 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
+; CHECK: %153 = OpULessThan %bool %uint_0 %152
+; CHECK: OpSelectionMerge %154 None
+; CHECK: OpBranchConditional %153 %155 %156
+; CHECK: %155 = OpLabel
+; CHECK: OpStore %31 %30
+; CHECK: OpBranch %154
+; CHECK: %156 = OpLabel
+; CHECK: %158 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_54 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %154
+; CHECK: %154 = OpLabel
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%7 = OpTypeFunction %void
-%uint = OpTypeInt 32 0
-%float = OpTypeFloat 32
-%StorageBuffer = OpTypeStruct %uint %float
-%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer
-%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform
-%int = OpTypeInt 32 1
-%int_1 = OpConstant %int 1
-%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
-%_runtimearr_13 = OpTypeRuntimeArray %13
-%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
-%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
-%int_0 = OpConstant %int 0
-%_ptr_Uniform_uint = OpTypePointer Uniform %uint
-%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
-%v2int = OpTypeVector %int 2
-%20 = OpConstantComposite %v2int %int_0 %int_0
-%v4float = OpTypeVector %float 4
-%uint_0 = OpConstant %uint 0
-%_ptr_Uniform_float = OpTypePointer Uniform %float
-%uint_1 = OpConstant %uint 1
-%34 = OpTypeFunction %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_39 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
-%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%bool = OpTypeBool
-%57 = OpTypeFunction %void %uint %uint %uint %uint
-%_struct_63 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63
-%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_5318 = OpConstant %uint 5318
-%uint_3 = OpConstant %uint 3
-%v3uint = OpTypeVector %uint 3
-%_ptr_Input_v3uint = OpTypePointer Input %v3uint
-%89 = OpVariable %_ptr_Input_v3uint Input
-%uint_5 = OpConstant %uint 5
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_51 = OpConstant %uint 51
-%113 = OpConstantNull %v4float
-%116 = OpTypeFunction %uint %uint %uint %uint %uint
-%uint_48 = OpConstant %uint 48
-%141 = OpConstantNull %uint
-%uint_54 = OpConstant %uint 54
-%main = OpFunction %void None %7
-%24 = OpLabel
-%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
-%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
-%134 = OpULessThan %bool %uint_0 %133
-OpSelectionMerge %135 None
-OpBranchConditional %134 %136 %137
-%136 = OpLabel
-%138 = OpLoad %uint %25
-OpBranch %135
-%137 = OpLabel
-%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0
-OpBranch %135
-%135 = OpLabel
-%142 = OpPhi %uint %138 %136 %141 %137
-%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142
-%28 = OpLoad %13 %27
-%48 = OpFunctionCall %uint %33 %uint_1 %uint_1
-%50 = OpULessThan %bool %142 %48
-OpSelectionMerge %51 None
-OpBranchConditional %50 %52 %53
-%52 = OpLabel
-%54 = OpLoad %13 %27
-%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142
-%144 = OpULessThan %bool %uint_0 %143
-OpSelectionMerge %145 None
-OpBranchConditional %144 %146 %147
-%146 = OpLabel
-%148 = OpLoad %13 %27
-%149 = OpImageRead %v4float %148 %20
-OpBranch %145
-%147 = OpLabel
-%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0
-OpBranch %145
-%145 = OpLabel
-%151 = OpPhi %v4float %149 %146 %113 %147
-OpBranch %51
-%53 = OpLabel
-%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48
-OpBranch %51
-%51 = OpLabel
-%114 = OpPhi %v4float %151 %145 %113 %53
-%30 = OpCompositeExtract %float %114 0
-%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
-%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0
-%153 = OpULessThan %bool %uint_0 %152
-OpSelectionMerge %154 None
-OpBranchConditional %153 %155 %156
-%155 = OpLabel
-OpStore %31 %30
-OpBranch %154
-%156 = OpLabel
-%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0
-OpBranch %154
-%154 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%33 = OpFunction %uint None %34
-%35 = OpFunctionParameter %uint
-%36 = OpFunctionParameter %uint
-%37 = OpLabel
-%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35
-%44 = OpLoad %uint %43
-%45 = OpIAdd %uint %44 %36
-%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45
-%47 = OpLoad %uint %46
-OpReturnValue %47
-OpFunctionEnd
-%56 = OpFunction %void None %57
-%58 = OpFunctionParameter %uint
-%59 = OpFunctionParameter %uint
-%60 = OpFunctionParameter %uint
-%61 = OpFunctionParameter %uint
-%62 = OpLabel
-%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0
-%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10
-%70 = OpIAdd %uint %69 %uint_10
-%71 = OpArrayLength %uint %65 1
-%72 = OpULessThanEqual %bool %70 %71
-OpSelectionMerge %73 None
-OpBranchConditional %72 %74 %73
-%74 = OpLabel
-%75 = OpIAdd %uint %69 %uint_0
-%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75
-OpStore %76 %uint_10
-%78 = OpIAdd %uint %69 %uint_1
-%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78
-OpStore %79 %uint_23
-%81 = OpIAdd %uint %69 %uint_2
-%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81
-OpStore %82 %58
-%85 = OpIAdd %uint %69 %uint_3
-%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85
-OpStore %86 %uint_5318
-%90 = OpLoad %v3uint %89
-%91 = OpCompositeExtract %uint %90 0
-%92 = OpCompositeExtract %uint %90 1
-%93 = OpCompositeExtract %uint %90 2
-%94 = OpIAdd %uint %69 %uint_4
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94
-OpStore %95 %91
-%97 = OpIAdd %uint %69 %uint_5
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97
-OpStore %98 %92
-%100 = OpIAdd %uint %69 %uint_6
-%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100
-OpStore %101 %93
-%103 = OpIAdd %uint %69 %uint_7
-%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103
-OpStore %104 %59
-%106 = OpIAdd %uint %69 %uint_8
-%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106
-OpStore %107 %60
-%109 = OpIAdd %uint %69 %uint_9
-%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109
-OpStore %110 %61
-OpBranch %73
-%73 = OpLabel
-OpReturn
-OpFunctionEnd
-%115 = OpFunction %uint None %116
-%117 = OpFunctionParameter %uint
-%118 = OpFunctionParameter %uint
-%119 = OpFunctionParameter %uint
-%120 = OpFunctionParameter %uint
-%121 = OpLabel
-%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117
-%123 = OpLoad %uint %122
-%124 = OpIAdd %uint %123 %118
-%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124
-%126 = OpLoad %uint %125
-%127 = OpIAdd %uint %126 %119
-%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127
-%129 = OpLoad %uint %128
-%130 = OpIAdd %uint %129 %120
-%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130
-%132 = OpLoad %uint %131
-OpReturnValue %132
-OpFunctionEnd
-)";
+ const std::string new_funcs = kDirectRead2 + kStreamWrite4Ray + kDirectRead4;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
+ true, 7u, 23u, true, true, false,
+ false, false);
}
TEST_F(InstBindlessTest, InstBoundsInitSameBlockOpReplication) {
@@ -6806,16 +4039,17 @@ TEST_F(InstBindlessTest, InstBoundsInitSameBlockOpReplication) {
// outColor = vec4(x, y, 0.0, 0.0);
// }
//
- // clang-format on
- const std::string defs_before =
- R"(OpCapability Shader
+ const std::string defs = R"(
+OpCapability Shader
OpCapability ShaderNonUniformEXT
OpCapability SampledImageArrayNonUniformIndexingEXT
OpExtension "SPV_EXT_descriptor_indexing"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %inTexcoord %outColor
+; CHECK: OpEntryPoint Fragment %main "main" %inTexcoord %outColor %gl_FragCoord
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpSourceExtension "GL_EXT_nonuniform_qualifier"
@@ -6845,6 +4079,12 @@ OpDecorate %Uniforms Block
OpDecorate %uniforms DescriptorSet 0
OpDecorate %uniforms Binding 0
OpDecorate %outColor Location 0
+; CHECK: OpDecorate %63 NonUniform
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+)" + kInputDecorations + R"(
+; CHECK: OpDecorate %151 NonUniform
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
@@ -6876,122 +4116,35 @@ OpDecorate %outColor Location 0
%_ptr_Output_v4float = OpTypePointer Output %v4float
%outColor = OpVariable %_ptr_Output_v4float Output
%float_0 = OpConstant %float 0
+; CHECK: %bool = OpTypeBool
+; CHECK: %68 = OpTypeFunction %void %uint %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kOutputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_79 = OpConstant %uint 79
+; CHECK: %122 = OpConstantNull %v4float
+; CHECK: %126 = OpTypeFunction %uint %uint %uint %uint %uint
+)" + kInputGlobals + R"(
+; CHECK: %uint_87 = OpConstant %uint 87
+; CHECK: %165 = OpConstantNull %v2float
+; CHECK: %uint_89 = OpConstant %uint 89
)";
+ // clang-format on
- const std::string defs_after =
- R"(OpCapability Shader
-OpCapability ShaderNonUniform
-OpCapability SampledImageArrayNonUniformIndexing
-OpExtension "SPV_EXT_descriptor_indexing"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %inTexcoord %outColor %gl_FragCoord
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_nonuniform_qualifier"
-OpName %main "main"
-OpName %index "index"
-OpName %x "x"
-OpName %uniformTexArr "uniformTexArr"
-OpName %uniformSampler "uniformSampler"
-OpName %inTexcoord "inTexcoord"
-OpName %y "y"
-OpName %uniformTex "uniformTex"
-OpName %Uniforms "Uniforms"
-OpMemberName %Uniforms 0 "var0"
-OpName %uniforms "uniforms"
-OpName %outColor "outColor"
-OpDecorate %uniformTexArr DescriptorSet 0
-OpDecorate %uniformTexArr Binding 3
-OpDecorate %19 NonUniform
-OpDecorate %22 NonUniform
-OpDecorate %uniformSampler DescriptorSet 0
-OpDecorate %uniformSampler Binding 1
-OpDecorate %inTexcoord Location 0
-OpDecorate %uniformTex DescriptorSet 0
-OpDecorate %uniformTex Binding 2
-OpMemberDecorate %Uniforms 0 Offset 0
-OpDecorate %Uniforms Block
-OpDecorate %uniforms DescriptorSet 0
-OpDecorate %uniforms Binding 0
-OpDecorate %outColor Location 0
-OpDecorate %63 NonUniform
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_75 Block
-OpMemberDecorate %_struct_75 0 Offset 0
-OpMemberDecorate %_struct_75 1 Offset 4
-OpDecorate %77 DescriptorSet 7
-OpDecorate %77 Binding 0
-OpDecorate %gl_FragCoord BuiltIn FragCoord
-OpDecorate %_struct_132 Block
-OpMemberDecorate %_struct_132 0 Offset 0
-OpDecorate %134 DescriptorSet 7
-OpDecorate %134 Binding 1
-OpDecorate %151 NonUniform
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%int = OpTypeInt 32 1
-%_ptr_Function_int = OpTypePointer Function %int
-%int_0 = OpConstant %int 0
-%float = OpTypeFloat 32
-%_ptr_Function_float = OpTypePointer Function %float
-%13 = OpTypeImage %float 2D 0 0 0 1 Unknown
-%uint = OpTypeInt 32 0
-%uint_8 = OpConstant %uint 8
-%_arr_13_uint_8 = OpTypeArray %13 %uint_8
-%_ptr_UniformConstant__arr_13_uint_8 = OpTypePointer UniformConstant %_arr_13_uint_8
-%uniformTexArr = OpVariable %_ptr_UniformConstant__arr_13_uint_8 UniformConstant
-%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
-%23 = OpTypeSampler
-%_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23
-%uniformSampler = OpVariable %_ptr_UniformConstant_23 UniformConstant
-%27 = OpTypeSampledImage %13
-%v2float = OpTypeVector %float 2
-%_ptr_Input_v2float = OpTypePointer Input %v2float
-%inTexcoord = OpVariable %_ptr_Input_v2float Input
-%v4float = OpTypeVector %float 4
-%uint_0 = OpConstant %uint 0
-%uniformTex = OpVariable %_ptr_UniformConstant_13 UniformConstant
-%Uniforms = OpTypeStruct %v2float
-%_ptr_Uniform_Uniforms = OpTypePointer Uniform %Uniforms
-%uniforms = OpVariable %_ptr_Uniform_Uniforms Uniform
-%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%outColor = OpVariable %_ptr_Output_v4float Output
-%float_0 = OpConstant %float 0
-%bool = OpTypeBool
-%68 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_75 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_75 = OpTypePointer StorageBuffer %_struct_75
-%77 = OpVariable %_ptr_StorageBuffer__struct_75 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_1 = OpConstant %uint 1
-%uint_23 = OpConstant %uint 23
-%uint_2 = OpConstant %uint 2
-%uint_3 = OpConstant %uint 3
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-%v4uint = OpTypeVector %uint 4
-%uint_5 = OpConstant %uint 5
-%uint_7 = OpConstant %uint 7
-%uint_9 = OpConstant %uint 9
-%uint_79 = OpConstant %uint 79
-%122 = OpConstantNull %v4float
-%126 = OpTypeFunction %uint %uint %uint %uint %uint
-%_struct_132 = OpTypeStruct %_runtimearr_uint
-%_ptr_StorageBuffer__struct_132 = OpTypePointer StorageBuffer %_struct_132
-%134 = OpVariable %_ptr_StorageBuffer__struct_132 StorageBuffer
-%uint_87 = OpConstant %uint 87
-%165 = OpConstantNull %v2float
-%uint_89 = OpConstant %uint 89
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %3
+ const std::string main_func = R"(
+%main = OpFunction %void None %3
%5 = OpLabel
%index = OpVariable %_ptr_Function_int Function
%x = OpVariable %_ptr_Function_float Function
@@ -7015,6 +4168,36 @@ OpStore %x %36
%49 = OpFMul %v2float %42 %48
%50 = OpImageSampleImplicitLod %v4float %41 %49
%51 = OpCompositeExtract %float %50 0
+; CHECK-NOT: %51 = OpCompositeExtract %float %50 0
+; CHECK: %157 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
+; CHECK: %158 = OpULessThan %bool %uint_0 %157
+; CHECK: OpSelectionMerge %159 None
+; CHECK: OpBranchConditional %158 %160 %161
+; CHECK: %160 = OpLabel
+; CHECK: %162 = OpLoad %v2float %47
+; CHECK: OpBranch %159
+; CHECK: %161 = OpLabel
+; CHECK: %164 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_87 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %159
+; CHECK: %159 = OpLabel
+; CHECK: %166 = OpPhi %v2float %162 %160 %165 %161
+; CHECK: %49 = OpFMul %v2float %42 %166
+; CHECK: %167 = OpSampledImage %27 %39 %40
+; CHECK: %168 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_2 %uint_0
+; CHECK: %169 = OpULessThan %bool %uint_0 %168
+; CHECK: OpSelectionMerge %170 None
+; CHECK: OpBranchConditional %169 %171 %172
+; CHECK: %171 = OpLabel
+; CHECK: %173 = OpLoad %13 %uniformTex
+; CHECK: %174 = OpSampledImage %27 %173 %40
+; CHECK: %175 = OpImageSampleImplicitLod %v4float %174 %49
+; CHECK: OpBranch %170
+; CHECK: %172 = OpLabel
+; CHECK: %177 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_89 %uint_1 %uint_0 %uint_0
+; CHECK: OpBranch %170
+; CHECK: %170 = OpLabel
+; CHECK: %178 = OpPhi %v4float %175 %171 %122 %172
+; CHECK: %51 = OpCompositeExtract %float %178 0
OpStore %y %51
%54 = OpLoad %float %x
%55 = OpLoad %float %y
@@ -7024,168 +4207,12 @@ OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %3
-%5 = OpLabel
-%index = OpVariable %_ptr_Function_int Function
-%x = OpVariable %_ptr_Function_float Function
-%y = OpVariable %_ptr_Function_float Function
-OpStore %index %int_0
-%19 = OpLoad %int %index
-%21 = OpAccessChain %_ptr_UniformConstant_13 %uniformTexArr %19
-%22 = OpLoad %13 %21
-%26 = OpLoad %23 %uniformSampler
-%28 = OpSampledImage %27 %22 %26
-%32 = OpLoad %v2float %inTexcoord
-%59 = OpULessThan %bool %19 %uint_8
-OpSelectionMerge %60 None
-OpBranchConditional %59 %61 %62
-%61 = OpLabel
-%63 = OpLoad %13 %21
-%64 = OpSampledImage %27 %63 %26
-%124 = OpBitcast %uint %19
-%146 = OpFunctionCall %uint %125 %uint_0 %uint_0 %uint_3 %124
-%147 = OpULessThan %bool %uint_0 %146
-OpSelectionMerge %148 None
-OpBranchConditional %147 %149 %150
-%149 = OpLabel
-%151 = OpLoad %13 %21
-%152 = OpSampledImage %27 %151 %26
-%153 = OpImageSampleImplicitLod %v4float %152 %32
-OpBranch %148
-%150 = OpLabel
-%154 = OpBitcast %uint %19
-%155 = OpFunctionCall %void %67 %uint_79 %uint_1 %154 %uint_0
-OpBranch %148
-%148 = OpLabel
-%156 = OpPhi %v4float %153 %149 %122 %150
-OpBranch %60
-%62 = OpLabel
-%66 = OpBitcast %uint %19
-%121 = OpFunctionCall %void %67 %uint_79 %uint_0 %66 %uint_8
-OpBranch %60
-%60 = OpLabel
-%123 = OpPhi %v4float %156 %148 %122 %62
-%36 = OpCompositeExtract %float %123 0
-OpStore %x %36
-%39 = OpLoad %13 %uniformTex
-%40 = OpLoad %23 %uniformSampler
-%41 = OpSampledImage %27 %39 %40
-%42 = OpLoad %v2float %inTexcoord
-%47 = OpAccessChain %_ptr_Uniform_v2float %uniforms %int_0
-%157 = OpFunctionCall %uint %125 %uint_0 %uint_0 %uint_0 %uint_0
-%158 = OpULessThan %bool %uint_0 %157
-OpSelectionMerge %159 None
-OpBranchConditional %158 %160 %161
-%160 = OpLabel
-%162 = OpLoad %v2float %47
-OpBranch %159
-%161 = OpLabel
-%164 = OpFunctionCall %void %67 %uint_87 %uint_1 %uint_0 %uint_0
-OpBranch %159
-%159 = OpLabel
-%166 = OpPhi %v2float %162 %160 %165 %161
-%49 = OpFMul %v2float %42 %166
-%167 = OpSampledImage %27 %39 %40
-%168 = OpFunctionCall %uint %125 %uint_0 %uint_0 %uint_2 %uint_0
-%169 = OpULessThan %bool %uint_0 %168
-OpSelectionMerge %170 None
-OpBranchConditional %169 %171 %172
-%171 = OpLabel
-%173 = OpLoad %13 %uniformTex
-%174 = OpSampledImage %27 %173 %40
-%175 = OpImageSampleImplicitLod %v4float %174 %49
-OpBranch %170
-%172 = OpLabel
-%177 = OpFunctionCall %void %67 %uint_89 %uint_1 %uint_0 %uint_0
-OpBranch %170
-%170 = OpLabel
-%178 = OpPhi %v4float %175 %171 %122 %172
-%51 = OpCompositeExtract %float %178 0
-OpStore %y %51
-%54 = OpLoad %float %x
-%55 = OpLoad %float %y
-%57 = OpCompositeConstruct %v4float %54 %55 %float_0 %float_0
-OpStore %outColor %57
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%67 = OpFunction %void None %68
-%69 = OpFunctionParameter %uint
-%70 = OpFunctionParameter %uint
-%71 = OpFunctionParameter %uint
-%72 = OpFunctionParameter %uint
-%73 = OpLabel
-%79 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_0
-%82 = OpAtomicIAdd %uint %79 %uint_4 %uint_0 %uint_10
-%83 = OpIAdd %uint %82 %uint_10
-%84 = OpArrayLength %uint %77 1
-%85 = OpULessThanEqual %bool %83 %84
-OpSelectionMerge %86 None
-OpBranchConditional %85 %87 %86
-%87 = OpLabel
-%88 = OpIAdd %uint %82 %uint_0
-%90 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %88
-OpStore %90 %uint_10
-%92 = OpIAdd %uint %82 %uint_1
-%93 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %92
-OpStore %93 %uint_23
-%95 = OpIAdd %uint %82 %uint_2
-%96 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %95
-OpStore %96 %69
-%98 = OpIAdd %uint %82 %uint_3
-%99 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %98
-OpStore %99 %uint_4
-%102 = OpLoad %v4float %gl_FragCoord
-%104 = OpBitcast %v4uint %102
-%105 = OpCompositeExtract %uint %104 0
-%106 = OpIAdd %uint %82 %uint_4
-%107 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %106
-OpStore %107 %105
-%108 = OpCompositeExtract %uint %104 1
-%110 = OpIAdd %uint %82 %uint_5
-%111 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %110
-OpStore %111 %108
-%113 = OpIAdd %uint %82 %uint_7
-%114 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %113
-OpStore %114 %70
-%115 = OpIAdd %uint %82 %uint_8
-%116 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %115
-OpStore %116 %71
-%118 = OpIAdd %uint %82 %uint_9
-%119 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %118
-OpStore %119 %72
-OpBranch %86
-%86 = OpLabel
-OpReturn
-OpFunctionEnd
-%125 = OpFunction %uint None %126
-%127 = OpFunctionParameter %uint
-%128 = OpFunctionParameter %uint
-%129 = OpFunctionParameter %uint
-%130 = OpFunctionParameter %uint
-%131 = OpLabel
-%135 = OpAccessChain %_ptr_StorageBuffer_uint %134 %uint_0 %127
-%136 = OpLoad %uint %135
-%137 = OpIAdd %uint %136 %128
-%138 = OpAccessChain %_ptr_StorageBuffer_uint %134 %uint_0 %137
-%139 = OpLoad %uint %138
-%140 = OpIAdd %uint %139 %129
-%141 = OpAccessChain %_ptr_StorageBuffer_uint %134 %uint_0 %140
-%142 = OpLoad %uint %141
-%143 = OpIAdd %uint %142 %130
-%144 = OpAccessChain %_ptr_StorageBuffer_uint %134 %uint_0 %143
-%145 = OpLoad %uint %144
-OpReturnValue %145
-OpFunctionEnd
-)";
+ const std::string new_funcs = kStreamWrite4Frag + kDirectRead4;
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBindlessCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u, true, true, false, false, false);
+ SinglePassRunAndMatch<InstBindlessCheckPass>(defs + main_func + new_funcs,
+ true, 7u, 23u, true, true, false,
+ false, false);
}
TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) {
@@ -7225,13 +4252,14 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) {
// return ps_output;
// }
+ // clang-format off
const std::string text = R"(
OpCapability Shader
;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor
-;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %130 %157 %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_FragCoord
OpExecutionMode %MainPs OriginUpperLeft
OpSource HLSL 500
OpName %MainPs "MainPs"
@@ -7260,15 +4288,7 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) {
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
- ;CHECK: OpDecorate %_struct_128 Block
- ;CHECK: OpMemberDecorate %_struct_128 0 Offset 0
- ;CHECK: OpDecorate %130 DescriptorSet 7
- ;CHECK: OpDecorate %130 Binding 1
- ;CHECK: OpDecorate %_struct_155 Block
- ;CHECK: OpMemberDecorate %_struct_155 0 Offset 0
- ;CHECK: OpMemberDecorate %_struct_155 1 Offset 4
- ;CHECK: OpDecorate %157 DescriptorSet 7
- ;CHECK: OpDecorate %157 Binding 0
+)" + kInputDecorations + kOutputDecorations + R"(
;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%3 = OpTypeFunction %void
@@ -7304,15 +4324,11 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) {
;CHECK: %uint_1 = OpConstant %uint 1
;CHECK: %122 = OpTypeFunction %uint %uint %uint %uint
;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
- ;CHECK: %_struct_128 = OpTypeStruct %_runtimearr_uint
- ;CHECK: %_ptr_StorageBuffer__struct_128 = OpTypePointer StorageBuffer %_struct_128
- ;CHECK: %130 = OpVariable %_ptr_StorageBuffer__struct_128 StorageBuffer
+ )" + kInputGlobals + R"(
;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
;CHECK: %uint_4 = OpConstant %uint 4
;CHECK: %148 = OpTypeFunction %void %uint %uint %uint %uint %uint
- ;CHECK: %_struct_155 = OpTypeStruct %uint %_runtimearr_uint
- ;CHECK: %_ptr_StorageBuffer__struct_155 = OpTypePointer StorageBuffer %_struct_155
- ;CHECK: %157 = OpVariable %_ptr_StorageBuffer__struct_155 StorageBuffer
+ )" + kOutputGlobals + R"(
;CHECK: %uint_11 = OpConstant %uint 11
;CHECK: %uint_23 = OpConstant %uint 23
;CHECK: %uint_2 = OpConstant %uint 2
@@ -7329,7 +4345,7 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) {
;CHECK: %uint_75 = OpConstant %uint 75
%MainPs = OpFunction %void None %3
%5 = OpLabel
- ;CHECK: %140 = OpFunctionCall %uint %121 %uint_1 %uint_1 %uint_0
+ ;CHECK: %140 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_1 %uint_0
;CHECK: OpBranch %117
;CHECK: %117 = OpLabel
;CHECK: OpBranch %116
@@ -7352,7 +4368,7 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) {
;CHECK: %146 = OpLoad %v2float %86
;CHECK: OpBranch %143
;CHECK: %145 = OpLabel
- ;CHECK: %201 = OpFunctionCall %void %147 %uint_71 %uint_4 %uint_0 %119 %140
+ ;CHECK: %201 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_71 %uint_4 %uint_0 %119 %140
;CHECK: OpBranch %143
;CHECK: %143 = OpLabel
;CHECK: %203 = OpPhi %v2float %146 %144 %202 %145
@@ -7369,7 +4385,7 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) {
;CHECK: %209 = OpLoad %v2float %89
;CHECK: OpBranch %206
;CHECK: %208 = OpLabel
- ;CHECK: %211 = OpFunctionCall %void %147 %uint_75 %uint_4 %uint_0 %204 %140
+ ;CHECK: %211 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_75 %uint_4 %uint_0 %204 %140
;CHECK: OpBranch %206
;CHECK: %206 = OpLabel
;CHECK: %212 = OpPhi %v2float %209 %207 %202 %208
@@ -7386,75 +4402,8 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) {
OpStore %_entryPointOutput_vColor %100
OpReturn
OpFunctionEnd
- ;CHECK: %121 = OpFunction %uint None %122
- ;CHECK: %123 = OpFunctionParameter %uint
- ;CHECK: %124 = OpFunctionParameter %uint
- ;CHECK: %125 = OpFunctionParameter %uint
- ;CHECK: %126 = OpLabel
- ;CHECK: %132 = OpAccessChain %_ptr_StorageBuffer_uint %130 %uint_0 %123
- ;CHECK: %133 = OpLoad %uint %132
- ;CHECK: %134 = OpIAdd %uint %133 %124
- ;CHECK: %135 = OpAccessChain %_ptr_StorageBuffer_uint %130 %uint_0 %134
- ;CHECK: %136 = OpLoad %uint %135
- ;CHECK: %137 = OpIAdd %uint %136 %125
- ;CHECK: %138 = OpAccessChain %_ptr_StorageBuffer_uint %130 %uint_0 %137
- ;CHECK: %139 = OpLoad %uint %138
- ;CHECK: OpReturnValue %139
- ;CHECK: OpFunctionEnd
- ;CHECK: %147 = OpFunction %void None %148
- ;CHECK: %149 = OpFunctionParameter %uint
- ;CHECK: %150 = OpFunctionParameter %uint
- ;CHECK: %151 = OpFunctionParameter %uint
- ;CHECK: %152 = OpFunctionParameter %uint
- ;CHECK: %153 = OpFunctionParameter %uint
- ;CHECK: %154 = OpLabel
- ;CHECK: %158 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_0
- ;CHECK: %160 = OpAtomicIAdd %uint %158 %uint_4 %uint_0 %uint_11
- ;CHECK: %161 = OpIAdd %uint %160 %uint_11
- ;CHECK: %162 = OpArrayLength %uint %157 1
- ;CHECK: %163 = OpULessThanEqual %bool %161 %162
- ;CHECK: OpSelectionMerge %164 None
- ;CHECK: OpBranchConditional %163 %165 %164
- ;CHECK: %165 = OpLabel
- ;CHECK: %166 = OpIAdd %uint %160 %uint_0
- ;CHECK: %167 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %166
- ;CHECK: OpStore %167 %uint_11
- ;CHECK: %169 = OpIAdd %uint %160 %uint_1
- ;CHECK: %170 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %169
- ;CHECK: OpStore %170 %uint_23
- ;CHECK: %172 = OpIAdd %uint %160 %uint_2
- ;CHECK: %173 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %172
- ;CHECK: OpStore %173 %149
- ;CHECK: %175 = OpIAdd %uint %160 %uint_3
- ;CHECK: %176 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %175
- ;CHECK: OpStore %176 %uint_4
- ;CHECK: %179 = OpLoad %v4float %gl_FragCoord
- ;CHECK: %181 = OpBitcast %v4uint %179
- ;CHECK: %182 = OpCompositeExtract %uint %181 0
- ;CHECK: %183 = OpIAdd %uint %160 %uint_4
- ;CHECK: %184 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %183
- ;CHECK: OpStore %184 %182
- ;CHECK: %185 = OpCompositeExtract %uint %181 1
- ;CHECK: %187 = OpIAdd %uint %160 %uint_5
- ;CHECK: %188 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %187
- ;CHECK: OpStore %188 %185
- ;CHECK: %189 = OpIAdd %uint %160 %uint_7
- ;CHECK: %190 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %189
- ;CHECK: OpStore %190 %150
- ;CHECK: %192 = OpIAdd %uint %160 %uint_8
- ;CHECK: %193 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %192
- ;CHECK: OpStore %193 %151
- ;CHECK: %195 = OpIAdd %uint %160 %uint_9
- ;CHECK: %196 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %195
- ;CHECK: OpStore %196 %152
- ;CHECK: %198 = OpIAdd %uint %160 %uint_10
- ;CHECK: %199 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %198
- ;CHECK: OpStore %199 %153
- ;CHECK: OpBranch %164
- ;CHECK: %164 = OpLabel
- ;CHECK: OpReturn
- ;CHECK: OpFunctionEnd
- )";
+)" + kDirectRead3 + kStreamWrite5Frag;
+ // clang-format on
SetTargetEnv(SPV_ENV_VULKAN_1_2);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@@ -7498,6 +4447,7 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) {
// return ps_output;
// }
+ // clang-format off
const std::string text = R"(
OpCapability Shader
;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
@@ -7540,15 +4490,7 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) {
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-;CHECK: OpDecorate %_struct_111 Block
-;CHECK: OpMemberDecorate %_struct_111 0 Offset 0
-;CHECK: OpDecorate %113 DescriptorSet 7
-;CHECK: OpDecorate %113 Binding 1
-;CHECK: OpDecorate %_struct_139 Block
-;CHECK: OpMemberDecorate %_struct_139 0 Offset 0
-;CHECK: OpMemberDecorate %_struct_139 1 Offset 4
-;CHECK: OpDecorate %141 DescriptorSet 7
-;CHECK: OpDecorate %141 Binding 0
+)" + kInputDecorations + kOutputDecorations + R"(
;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%3 = OpTypeFunction %void
@@ -7591,16 +4533,12 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) {
;CHECK: %uint_1 = OpConstant %uint 1
;CHECK: %105 = OpTypeFunction %uint %uint %uint %uint
;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint
-;CHECK:%_struct_111 = OpTypeStruct %_runtimearr_uint
-;CHECK:%_ptr_StorageBuffer__struct_111 = OpTypePointer StorageBuffer %_struct_111
-;CHECK: %113 = OpVariable %_ptr_StorageBuffer__struct_111 StorageBuffer
+)" + kInputGlobals + R"(
;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
;CHECK: %bool = OpTypeBool
;CHECK: %uint_4 = OpConstant %uint 4
;CHECK: %132 = OpTypeFunction %void %uint %uint %uint %uint %uint
-;CHECK:%_struct_139 = OpTypeStruct %uint %_runtimearr_uint
-;CHECK:%_ptr_StorageBuffer__struct_139 = OpTypePointer StorageBuffer %_struct_139
-;CHECK: %141 = OpVariable %_ptr_StorageBuffer__struct_139 StorageBuffer
+)" + kOutputGlobals + R"(
;CHECK: %uint_11 = OpConstant %uint 11
;CHECK: %uint_23 = OpConstant %uint 23
;CHECK: %uint_3 = OpConstant %uint 3
@@ -7615,7 +4553,7 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) {
;CHECK: %185 = OpConstantNull %v2float
%MainPs = OpFunction %void None %3
%5 = OpLabel
-;CHECK: %123 = OpFunctionCall %uint %104 %uint_1 %uint_2 %uint_0
+;CHECK: %123 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_2 %uint_0
;CHECK: OpBranch %93
;CHECK: %93 = OpLabel
;CHECK: OpBranch %92
@@ -7637,7 +4575,7 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) {
;CHECK: %130 = OpLoad %v2float %81
;CHECK: OpBranch %127
;CHECK: %129 = OpLabel
-;CHECK: %184 = OpFunctionCall %void %131 %uint_78 %uint_4 %uint_0 %101 %123
+;CHECK: %184 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_78 %uint_4 %uint_0 %101 %123
;CHECK: OpBranch %127
;CHECK: %127 = OpLabel
;CHECK: %186 = OpPhi %v2float %130 %128 %185 %129
@@ -7651,75 +4589,8 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) {
OpStore %_entryPointOutput_vColor %91
OpReturn
OpFunctionEnd
-;CHECK: %104 = OpFunction %uint None %105
-;CHECK: %106 = OpFunctionParameter %uint
-;CHECK: %107 = OpFunctionParameter %uint
-;CHECK: %108 = OpFunctionParameter %uint
-;CHECK: %109 = OpLabel
-;CHECK: %115 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %106
-;CHECK: %116 = OpLoad %uint %115
-;CHECK: %117 = OpIAdd %uint %116 %107
-;CHECK: %118 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %117
-;CHECK: %119 = OpLoad %uint %118
-;CHECK: %120 = OpIAdd %uint %119 %108
-;CHECK: %121 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %120
-;CHECK: %122 = OpLoad %uint %121
-;CHECK: OpReturnValue %122
-;CHECK: OpFunctionEnd
-;CHECK: %131 = OpFunction %void None %132
-;CHECK: %133 = OpFunctionParameter %uint
-;CHECK: %134 = OpFunctionParameter %uint
-;CHECK: %135 = OpFunctionParameter %uint
-;CHECK: %136 = OpFunctionParameter %uint
-;CHECK: %137 = OpFunctionParameter %uint
-;CHECK: %138 = OpLabel
-;CHECK: %142 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_0
-;CHECK: %144 = OpAtomicIAdd %uint %142 %uint_4 %uint_0 %uint_11
-;CHECK: %145 = OpIAdd %uint %144 %uint_11
-;CHECK: %146 = OpArrayLength %uint %141 1
-;CHECK: %147 = OpULessThanEqual %bool %145 %146
-;CHECK: OpSelectionMerge %148 None
-;CHECK: OpBranchConditional %147 %149 %148
-;CHECK: %149 = OpLabel
-;CHECK: %150 = OpIAdd %uint %144 %uint_0
-;CHECK: %151 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %150
-;CHECK: OpStore %151 %uint_11
-;CHECK: %153 = OpIAdd %uint %144 %uint_1
-;CHECK: %154 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %153
-;CHECK: OpStore %154 %uint_23
-;CHECK: %155 = OpIAdd %uint %144 %uint_2
-;CHECK: %156 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %155
-;CHECK: OpStore %156 %133
-;CHECK: %158 = OpIAdd %uint %144 %uint_3
-;CHECK: %159 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %158
-;CHECK: OpStore %159 %uint_4
-;CHECK: %162 = OpLoad %v4float %gl_FragCoord
-;CHECK: %164 = OpBitcast %v4uint %162
-;CHECK: %165 = OpCompositeExtract %uint %164 0
-;CHECK: %166 = OpIAdd %uint %144 %uint_4
-;CHECK: %167 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %166
-;CHECK: OpStore %167 %165
-;CHECK: %168 = OpCompositeExtract %uint %164 1
-;CHECK: %170 = OpIAdd %uint %144 %uint_5
-;CHECK: %171 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %170
-;CHECK: OpStore %171 %168
-;CHECK: %172 = OpIAdd %uint %144 %uint_7
-;CHECK: %173 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %172
-;CHECK: OpStore %173 %134
-;CHECK: %175 = OpIAdd %uint %144 %uint_8
-;CHECK: %176 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %175
-;CHECK: OpStore %176 %135
-;CHECK: %178 = OpIAdd %uint %144 %uint_9
-;CHECK: %179 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %178
-;CHECK: OpStore %179 %136
-;CHECK: %181 = OpIAdd %uint %144 %uint_10
-;CHECK: %182 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %181
-;CHECK: OpStore %182 %137
-;CHECK: OpBranch %148
-;CHECK: %148 = OpLabel
-;CHECK: OpReturn
-;CHECK: OpFunctionEnd
- )";
+)" + kDirectRead3 + kStreamWrite5Frag;
+ // clang-format on
SetTargetEnv(SPV_ENV_VULKAN_1_2);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@@ -7733,13 +4604,14 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) {
//
// Same source as UniformArrayRefNoDescInit
+ // clang-format off
const std::string text = R"(
OpCapability Shader
;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor
-;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %113 %144 %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_FragCoord
OpExecutionMode %MainPs OriginUpperLeft
OpSource HLSL 500
OpName %MainPs "MainPs"
@@ -7776,15 +4648,7 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) {
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-;CHECK: OpDecorate %_struct_111 Block
-;CHECK: OpMemberDecorate %_struct_111 0 Offset 0
-;CHECK: OpDecorate %113 DescriptorSet 7
-;CHECK: OpDecorate %113 Binding 1
-;CHECK: OpDecorate %_struct_142 Block
-;CHECK: OpMemberDecorate %_struct_142 0 Offset 0
-;CHECK: OpMemberDecorate %_struct_142 1 Offset 4
-;CHECK: OpDecorate %144 DescriptorSet 7
-;CHECK: OpDecorate %144 Binding 0
+)" + kInputDecorations + kOutputDecorations + R"(
;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%3 = OpTypeFunction %void
@@ -7826,16 +4690,12 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) {
;CHECK: %uint_2 = OpConstant %uint 2
;CHECK: %104 = OpTypeFunction %uint %uint %uint %uint %uint
;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint
-;CHECK:%_struct_111 = OpTypeStruct %_runtimearr_uint
-;CHECK:%_ptr_StorageBuffer__struct_111 = OpTypePointer StorageBuffer %_struct_111
-;CHECK: %113 = OpVariable %_ptr_StorageBuffer__struct_111 StorageBuffer
+)" + kInputGlobals + R"(
;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
;CHECK: %bool = OpTypeBool
;CHECK: %uint_4 = OpConstant %uint 4
;CHECK: %135 = OpTypeFunction %void %uint %uint %uint %uint %uint
-;CHECK:%_struct_142 = OpTypeStruct %uint %_runtimearr_uint
-;CHECK:%_ptr_StorageBuffer__struct_142 = OpTypePointer StorageBuffer %_struct_142
-;CHECK: %144 = OpVariable %_ptr_StorageBuffer__struct_142 StorageBuffer
+)" + kOutputGlobals + R"(
;CHECK: %uint_11 = OpConstant %uint 11
;CHECK: %uint_1 = OpConstant %uint 1
;CHECK: %uint_23 = OpConstant %uint 23
@@ -7853,8 +4713,8 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) {
;CHECK: %201 = OpConstantNull %v4float
%MainPs = OpFunction %void None %3
%5 = OpLabel
-;CHECK: %126 = OpFunctionCall %uint %103 %uint_0 %uint_0 %uint_2 %uint_0
-;CHECK: %191 = OpFunctionCall %uint %103 %uint_0 %uint_0 %uint_0 %uint_0
+;CHECK: %126 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_2 %uint_0
+;CHECK: %191 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %uint_0
;CHECK: OpBranch %93
;CHECK: %93 = OpLabel
;CHECK: OpBranch %92
@@ -7878,7 +4738,7 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) {
;CHECK: %133 = OpLoad %v2float %81
;CHECK: OpBranch %130
;CHECK: %132 = OpLabel
-;CHECK: %188 = OpFunctionCall %void %134 %uint_78 %uint_4 %uint_0 %101 %126
+;CHECK: %188 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_78 %uint_4 %uint_0 %101 %126
;CHECK: OpBranch %130
;CHECK: %130 = OpLabel
;CHECK: %190 = OpPhi %v2float %133 %131 %189 %132
@@ -7899,86 +4759,15 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) {
;CHECK: %198 = OpImageSampleImplicitLod %v4float %197 %86
;CHECK: OpBranch %193
;CHECK: %195 = OpLabel
-;CHECK: %200 = OpFunctionCall %void %134 %uint_83 %uint_1 %uint_0 %uint_0 %uint_0
+;CHECK: %200 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_83 %uint_1 %uint_0 %uint_0 %uint_0
;CHECK: OpBranch %193
;CHECK: %193 = OpLabel
;CHECK: %202 = OpPhi %v4float %198 %194 %201 %195
;CHECK: OpStore %_entryPointOutput_vColor %202
OpReturn
OpFunctionEnd
-;CHECK: %103 = OpFunction %uint None %104
-;CHECK: %105 = OpFunctionParameter %uint
-;CHECK: %106 = OpFunctionParameter %uint
-;CHECK: %107 = OpFunctionParameter %uint
-;CHECK: %108 = OpFunctionParameter %uint
-;CHECK: %109 = OpLabel
-;CHECK: %115 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %105
-;CHECK: %116 = OpLoad %uint %115
-;CHECK: %117 = OpIAdd %uint %116 %106
-;CHECK: %118 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %117
-;CHECK: %119 = OpLoad %uint %118
-;CHECK: %120 = OpIAdd %uint %119 %107
-;CHECK: %121 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %120
-;CHECK: %122 = OpLoad %uint %121
-;CHECK: %123 = OpIAdd %uint %122 %108
-;CHECK: %124 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %123
-;CHECK: %125 = OpLoad %uint %124
-;CHECK: OpReturnValue %125
-;CHECK: OpFunctionEnd
-;CHECK: %134 = OpFunction %void None %135
-;CHECK: %136 = OpFunctionParameter %uint
-;CHECK: %137 = OpFunctionParameter %uint
-;CHECK: %138 = OpFunctionParameter %uint
-;CHECK: %139 = OpFunctionParameter %uint
-;CHECK: %140 = OpFunctionParameter %uint
-;CHECK: %141 = OpLabel
-;CHECK: %145 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_0
-;CHECK: %147 = OpAtomicIAdd %uint %145 %uint_4 %uint_0 %uint_11
-;CHECK: %148 = OpIAdd %uint %147 %uint_11
-;CHECK: %149 = OpArrayLength %uint %144 1
-;CHECK: %150 = OpULessThanEqual %bool %148 %149
-;CHECK: OpSelectionMerge %151 None
-;CHECK: OpBranchConditional %150 %152 %151
-;CHECK: %152 = OpLabel
-;CHECK: %153 = OpIAdd %uint %147 %uint_0
-;CHECK: %155 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %153
-;CHECK: OpStore %155 %uint_11
-;CHECK: %157 = OpIAdd %uint %147 %uint_1
-;CHECK: %158 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %157
-;CHECK: OpStore %158 %uint_23
-;CHECK: %159 = OpIAdd %uint %147 %uint_2
-;CHECK: %160 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %159
-;CHECK: OpStore %160 %136
-;CHECK: %162 = OpIAdd %uint %147 %uint_3
-;CHECK: %163 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %162
-;CHECK: OpStore %163 %uint_4
-;CHECK: %166 = OpLoad %v4float %gl_FragCoord
-;CHECK: %168 = OpBitcast %v4uint %166
-;CHECK: %169 = OpCompositeExtract %uint %168 0
-;CHECK: %170 = OpIAdd %uint %147 %uint_4
-;CHECK: %171 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %170
-;CHECK: OpStore %171 %169
-;CHECK: %172 = OpCompositeExtract %uint %168 1
-;CHECK: %174 = OpIAdd %uint %147 %uint_5
-;CHECK: %175 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %174
-;CHECK: OpStore %175 %172
-;CHECK: %176 = OpIAdd %uint %147 %uint_7
-;CHECK: %177 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %176
-;CHECK: OpStore %177 %137
-;CHECK: %179 = OpIAdd %uint %147 %uint_8
-;CHECK: %180 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %179
-;CHECK: OpStore %180 %138
-;CHECK: %182 = OpIAdd %uint %147 %uint_9
-;CHECK: %183 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %182
-;CHECK: OpStore %183 %139
-;CHECK: %185 = OpIAdd %uint %147 %uint_10
-;CHECK: %186 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %185
-;CHECK: OpStore %186 %140
-;CHECK: OpBranch %151
-;CHECK: %151 = OpLabel
-;CHECK: OpReturn
-;CHECK: OpFunctionEnd
- )";
+)" + kDirectRead4 + kStreamWrite5Frag;
+ // clang-format on
SetTargetEnv(SPV_ENV_VULKAN_1_2);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@@ -7992,6 +4781,7 @@ TEST_F(InstBindlessTest, Descriptor16BitIdxRef) {
//
// Use Simple source with min16uint g_nDataIdx
+ // clang-format off
const std::string text = R"(
OpCapability Shader
OpCapability Int16
@@ -8000,7 +4790,7 @@ TEST_F(InstBindlessTest, Descriptor16BitIdxRef) {
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %_ %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor
-;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %_ %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %60 %gl_FragCoord %119
+;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %_ %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %inst_bindless_output_buffer %gl_FragCoord %inst_bindless_input_buffer
OpExecutionMode %MainPs OriginUpperLeft
OpSource HLSL 500
OpName %MainPs "MainPs"
@@ -8020,16 +4810,9 @@ TEST_F(InstBindlessTest, Descriptor16BitIdxRef) {
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-;CHECK: OpDecorate %_struct_58 Block
-;CHECK: OpMemberDecorate %_struct_58 0 Offset 0
-;CHECK: OpMemberDecorate %_struct_58 1 Offset 4
-;CHECK: OpDecorate %60 DescriptorSet 7
-;CHECK: OpDecorate %60 Binding 0
+)" + kOutputDecorations + R"(
;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
-;CHECK: OpDecorate %_struct_117 Block
-;CHECK: OpMemberDecorate %_struct_117 0 Offset 0
-;CHECK: OpDecorate %119 DescriptorSet 7
-;CHECK: OpDecorate %119 Binding 1
+)" + kInputDecorations + R"(
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -8061,9 +4844,7 @@ TEST_F(InstBindlessTest, Descriptor16BitIdxRef) {
;CHECK: %bool = OpTypeBool
;CHECK: %51 = OpTypeFunction %void %uint %uint %uint %uint
;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint
-;CHECK: %_struct_58 = OpTypeStruct %uint %_runtimearr_uint
-;CHECK:%_ptr_StorageBuffer__struct_58 = OpTypePointer StorageBuffer %_struct_58
-;CHECK: %60 = OpVariable %_ptr_StorageBuffer__struct_58 StorageBuffer
+)" + kOutputGlobals + R"(
;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
;CHECK: %uint_10 = OpConstant %uint 10
;CHECK: %uint_4 = OpConstant %uint 4
@@ -8081,9 +4862,7 @@ TEST_F(InstBindlessTest, Descriptor16BitIdxRef) {
;CHECK: %uint_60 = OpConstant %uint 60
;CHECK: %106 = OpConstantNull %v4float
;CHECK: %111 = OpTypeFunction %uint %uint %uint %uint %uint
-;CHECK:%_struct_117 = OpTypeStruct %_runtimearr_uint
-;CHECK:%_ptr_StorageBuffer__struct_117 = OpTypePointer StorageBuffer %_struct_117
-;CHECK: %119 = OpVariable %_ptr_StorageBuffer__struct_117 StorageBuffer
+)" + kInputGlobals + R"(
%MainPs = OpFunction %void None %10
%30 = OpLabel
;CHECK: OpBranch %108
@@ -8109,7 +4888,7 @@ TEST_F(InstBindlessTest, Descriptor16BitIdxRef) {
;CHECK: %47 = OpLoad %16 %34
;CHECK: %48 = OpSampledImage %27 %47 %36
;CHECK: %109 = OpUConvert %uint %33
-;CHECK: %131 = OpFunctionCall %uint %110 %uint_0 %uint_0 %uint_0 %109
+;CHECK: %131 = OpFunctionCall %uint %inst_bindless_direct_read_4 %uint_0 %uint_0 %uint_0 %109
;CHECK: %132 = OpULessThan %bool %uint_0 %131
;CHECK: OpSelectionMerge %133 None
;CHECK: OpBranchConditional %132 %134 %135
@@ -8120,88 +4899,21 @@ TEST_F(InstBindlessTest, Descriptor16BitIdxRef) {
;CHECK: OpBranch %133
;CHECK: %135 = OpLabel
;CHECK: %139 = OpUConvert %uint %33
-;CHECK: %140 = OpFunctionCall %void %50 %uint_60 %uint_1 %139 %uint_0
+;CHECK: %140 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_60 %uint_1 %139 %uint_0
;CHECK: OpBranch %133
;CHECK: %133 = OpLabel
;CHECK: %141 = OpPhi %v4float %138 %134 %106 %135
;CHECK: OpBranch %44
;CHECK: %46 = OpLabel
-;CHECK: %105 = OpFunctionCall %void %50 %uint_60 %uint_0 %41 %uint_128
+;CHECK: %105 = OpFunctionCall %void %inst_bindless_stream_write_4 %uint_60 %uint_0 %41 %uint_128
;CHECK: OpBranch %44
;CHECK: %44 = OpLabel
;CHECK: %107 = OpPhi %v4float %141 %133 %106 %46
;CHECK: OpStore %_entryPointOutput_vColor %107
OpReturn
OpFunctionEnd
-;CHECK: %50 = OpFunction %void None %51
-;CHECK: %52 = OpFunctionParameter %uint
-;CHECK: %53 = OpFunctionParameter %uint
-;CHECK: %54 = OpFunctionParameter %uint
-;CHECK: %55 = OpFunctionParameter %uint
-;CHECK: %56 = OpLabel
-;CHECK: %62 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_0
-;CHECK: %65 = OpAtomicIAdd %uint %62 %uint_4 %uint_0 %uint_10
-;CHECK: %66 = OpIAdd %uint %65 %uint_10
-;CHECK: %67 = OpArrayLength %uint %60 1
-;CHECK: %68 = OpULessThanEqual %bool %66 %67
-;CHECK: OpSelectionMerge %69 None
-;CHECK: OpBranchConditional %68 %70 %69
-;CHECK: %70 = OpLabel
-;CHECK: %71 = OpIAdd %uint %65 %uint_0
-;CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %71
-;CHECK: OpStore %73 %uint_10
-;CHECK: %75 = OpIAdd %uint %65 %uint_1
-;CHECK: %76 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %75
-;CHECK: OpStore %76 %uint_23
-;CHECK: %78 = OpIAdd %uint %65 %uint_2
-;CHECK: %79 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %78
-;CHECK: OpStore %79 %52
-;CHECK: %81 = OpIAdd %uint %65 %uint_3
-;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %81
-;CHECK: OpStore %82 %uint_4
-;CHECK: %85 = OpLoad %v4float %gl_FragCoord
-;CHECK: %87 = OpBitcast %v4uint %85
-;CHECK: %88 = OpCompositeExtract %uint %87 0
-;CHECK: %89 = OpIAdd %uint %65 %uint_4
-;CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %89
-;CHECK: OpStore %90 %88
-;CHECK: %91 = OpCompositeExtract %uint %87 1
-;CHECK: %93 = OpIAdd %uint %65 %uint_5
-;CHECK: %94 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %93
-;CHECK: OpStore %94 %91
-;CHECK: %96 = OpIAdd %uint %65 %uint_7
-;CHECK: %97 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %96
-;CHECK: OpStore %97 %53
-;CHECK: %99 = OpIAdd %uint %65 %uint_8
-;CHECK: %100 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %99
-;CHECK: OpStore %100 %54
-;CHECK: %102 = OpIAdd %uint %65 %uint_9
-;CHECK: %103 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %102
-;CHECK: OpStore %103 %55
-;CHECK: OpBranch %69
-;CHECK: %69 = OpLabel
-;CHECK: OpReturn
-;CHECK: OpFunctionEnd
-;CHECK: %110 = OpFunction %uint None %111
-;CHECK: %112 = OpFunctionParameter %uint
-;CHECK: %113 = OpFunctionParameter %uint
-;CHECK: %114 = OpFunctionParameter %uint
-;CHECK: %115 = OpFunctionParameter %uint
-;CHECK: %116 = OpLabel
-;CHECK: %120 = OpAccessChain %_ptr_StorageBuffer_uint %119 %uint_0 %112
-;CHECK: %121 = OpLoad %uint %120
-;CHECK: %122 = OpIAdd %uint %121 %113
-;CHECK: %123 = OpAccessChain %_ptr_StorageBuffer_uint %119 %uint_0 %122
-;CHECK: %124 = OpLoad %uint %123
-;CHECK: %125 = OpIAdd %uint %124 %114
-;CHECK: %126 = OpAccessChain %_ptr_StorageBuffer_uint %119 %uint_0 %125
-;CHECK: %127 = OpLoad %uint %126
-;CHECK: %128 = OpIAdd %uint %127 %115
-;CHECK: %129 = OpAccessChain %_ptr_StorageBuffer_uint %119 %uint_0 %128
-;CHECK: %130 = OpLoad %uint %129
-;CHECK: OpReturnValue %130
-;CHECK: OpFunctionEnd
- )";
+)" + kStreamWrite4Frag + kDirectRead4;
+ // clang-format on
SetTargetEnv(SPV_ENV_VULKAN_1_2);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@@ -8245,6 +4957,7 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) {
// return ps_output;
// }
+ // clang-format off
const std::string text = R"(
OpCapability Shader
OpCapability Int16
@@ -8253,7 +4966,7 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) {
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor
-;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %69 %97 %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_FragCoord
OpExecutionMode %MainPs OriginUpperLeft
OpSource HLSL 500
OpName %MainPs "MainPs"
@@ -8290,15 +5003,7 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) {
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-;CHECK: OpDecorate %_struct_67 Block
-;CHECK: OpMemberDecorate %_struct_67 0 Offset 0
-;CHECK: OpDecorate %69 DescriptorSet 7
-;CHECK: OpDecorate %69 Binding 1
-;CHECK: OpDecorate %_struct_95 Block
-;CHECK: OpMemberDecorate %_struct_95 0 Offset 0
-;CHECK: OpMemberDecorate %_struct_95 1 Offset 4
-;CHECK: OpDecorate %97 DescriptorSet 7
-;CHECK: OpDecorate %97 Binding 0
+)" + kInputDecorations + kOutputDecorations + R"(
;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%14 = OpTypeFunction %void
@@ -8341,16 +5046,12 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) {
;CHECK: %uint_1 = OpConstant %uint 1
;CHECK: %61 = OpTypeFunction %uint %uint %uint %uint
;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint
-;CHECK: %_struct_67 = OpTypeStruct %_runtimearr_uint
-;CHECK:%_ptr_StorageBuffer__struct_67 = OpTypePointer StorageBuffer %_struct_67
-;CHECK: %69 = OpVariable %_ptr_StorageBuffer__struct_67 StorageBuffer
+)" + kInputGlobals + R"(
;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
;CHECK: %bool = OpTypeBool
;CHECK: %uint_4 = OpConstant %uint 4
;CHECK: %88 = OpTypeFunction %void %uint %uint %uint %uint %uint
-;CHECK: %_struct_95 = OpTypeStruct %uint %_runtimearr_uint
-;CHECK:%_ptr_StorageBuffer__struct_95 = OpTypePointer StorageBuffer %_struct_95
-;CHECK: %97 = OpVariable %_ptr_StorageBuffer__struct_95 StorageBuffer
+)" + kOutputGlobals + R"(
;CHECK: %uint_11 = OpConstant %uint 11
;CHECK: %uint_23 = OpConstant %uint 23
;CHECK: %uint_2 = OpConstant %uint 2
@@ -8366,7 +5067,7 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) {
;CHECK: %142 = OpConstantNull %v2float
%MainPs = OpFunction %void None %14
%37 = OpLabel
-;CHECK: %79 = OpFunctionCall %uint %60 %uint_1 %uint_0 %uint_0
+;CHECK: %79 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0
;CHECK: OpBranch %49
;CHECK: %49 = OpLabel
;CHECK: OpBranch %48
@@ -8391,7 +5092,7 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) {
;CHECK: %86 = OpLoad %v2float %41
;CHECK: OpBranch %83
;CHECK: %85 = OpLabel
-;CHECK: %141 = OpFunctionCall %void %87 %uint_81 %uint_4 %uint_0 %58 %79
+;CHECK: %141 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_81 %uint_4 %uint_0 %58 %79
;CHECK: OpBranch %83
;CHECK: %83 = OpLabel
;CHECK: %143 = OpPhi %v2float %86 %84 %142 %85
@@ -8403,75 +5104,8 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) {
OpStore %_entryPointOutput_vColor %47
OpReturn
OpFunctionEnd
-;CHECK: %60 = OpFunction %uint None %61
-;CHECK: %62 = OpFunctionParameter %uint
-;CHECK: %63 = OpFunctionParameter %uint
-;CHECK: %64 = OpFunctionParameter %uint
-;CHECK: %65 = OpLabel
-;CHECK: %71 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_0 %62
-;CHECK: %72 = OpLoad %uint %71
-;CHECK: %73 = OpIAdd %uint %72 %63
-;CHECK: %74 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_0 %73
-;CHECK: %75 = OpLoad %uint %74
-;CHECK: %76 = OpIAdd %uint %75 %64
-;CHECK: %77 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_0 %76
-;CHECK: %78 = OpLoad %uint %77
-;CHECK: OpReturnValue %78
-;CHECK: OpFunctionEnd
-;CHECK: %87 = OpFunction %void None %88
-;CHECK: %89 = OpFunctionParameter %uint
-;CHECK: %90 = OpFunctionParameter %uint
-;CHECK: %91 = OpFunctionParameter %uint
-;CHECK: %92 = OpFunctionParameter %uint
-;CHECK: %93 = OpFunctionParameter %uint
-;CHECK: %94 = OpLabel
-;CHECK: %98 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_0
-;CHECK: %100 = OpAtomicIAdd %uint %98 %uint_4 %uint_0 %uint_11
-;CHECK: %101 = OpIAdd %uint %100 %uint_11
-;CHECK: %102 = OpArrayLength %uint %97 1
-;CHECK: %103 = OpULessThanEqual %bool %101 %102
-;CHECK: OpSelectionMerge %104 None
-;CHECK: OpBranchConditional %103 %105 %104
-;CHECK: %105 = OpLabel
-;CHECK: %106 = OpIAdd %uint %100 %uint_0
-;CHECK: %107 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %106
-;CHECK: OpStore %107 %uint_11
-;CHECK: %109 = OpIAdd %uint %100 %uint_1
-;CHECK: %110 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %109
-;CHECK: OpStore %110 %uint_23
-;CHECK: %112 = OpIAdd %uint %100 %uint_2
-;CHECK: %113 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %112
-;CHECK: OpStore %113 %89
-;CHECK: %115 = OpIAdd %uint %100 %uint_3
-;CHECK: %116 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %115
-;CHECK: OpStore %116 %uint_4
-;CHECK: %119 = OpLoad %v4float %gl_FragCoord
-;CHECK: %121 = OpBitcast %v4uint %119
-;CHECK: %122 = OpCompositeExtract %uint %121 0
-;CHECK: %123 = OpIAdd %uint %100 %uint_4
-;CHECK: %124 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %123
-;CHECK: OpStore %124 %122
-;CHECK: %125 = OpCompositeExtract %uint %121 1
-;CHECK: %127 = OpIAdd %uint %100 %uint_5
-;CHECK: %128 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %127
-;CHECK: OpStore %128 %125
-;CHECK: %129 = OpIAdd %uint %100 %uint_7
-;CHECK: %130 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %129
-;CHECK: OpStore %130 %90
-;CHECK: %132 = OpIAdd %uint %100 %uint_8
-;CHECK: %133 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %132
-;CHECK: OpStore %133 %91
-;CHECK: %135 = OpIAdd %uint %100 %uint_9
-;CHECK: %136 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %135
-;CHECK: OpStore %136 %92
-;CHECK: %138 = OpIAdd %uint %100 %uint_10
-;CHECK: %139 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %138
-;CHECK: OpStore %139 %93
-;CHECK: OpBranch %104
-;CHECK: %104 = OpLabel
-;CHECK: OpReturn
-;CHECK: OpFunctionEnd
- )";
+ )" + kDirectRead3 + kStreamWrite5Frag;
+ // clang-format on
SetTargetEnv(SPV_ENV_VULKAN_1_2);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@@ -8498,13 +5132,14 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) {
// v_vtxResult = var[2][1];
// }
- const std::string text = R"(
+ // clang-format off
+ std::string text = R"(
OpCapability Shader
;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position
-;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %45 %72 %gl_VertexIndex %gl_InstanceIndex
+;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_VertexIndex %gl_InstanceIndex
OpSource GLSL 450
OpSourceExtension "GL_EXT_scalar_block_layout"
OpName %main "main"
@@ -8527,16 +5162,9 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) {
;CHECK: OpDecorate %116 RelaxedPrecision
OpDecorate %a_position Location 0
;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-;CHECK: OpDecorate %_struct_43 Block
-;CHECK: OpMemberDecorate %_struct_43 0 Offset 0
-;CHECK: OpDecorate %45 DescriptorSet 7
-;CHECK: OpDecorate %45 Binding 1
+)" + kInputDecorations + R"(
;CHECK: OpDecorate %61 RelaxedPrecision
-;CHECK: OpDecorate %_struct_70 Block
-;CHECK: OpMemberDecorate %_struct_70 0 Offset 0
-;CHECK: OpMemberDecorate %_struct_70 1 Offset 4
-;CHECK: OpDecorate %72 DescriptorSet 7
-;CHECK: OpDecorate %72 Binding 0
+)" + kOutputDecorations + R"(
;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
%void = OpTypeVoid
@@ -8564,15 +5192,11 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) {
;CHECK; %uint_3 = OpConstant %uint 3
;CHECK; %37 = OpTypeFunction %uint %uint %uint %uint
;CHECK;%_runtimearr_uint = OpTypeRuntimeArray %uint
-;CHECK; %_struct_43 = OpTypeStruct %_runtimearr_uint
-;CHECK;%_ptr_StorageBuffer__struct_43 = OpTypePointer StorageBuffer %_struct_43
-;CHECK; %45 = OpVariable %_ptr_StorageBuffer__struct_43 StorageBuffer
+)" + kInputGlobals + R"(
;CHECK;%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
;CHECK; %bool = OpTypeBool
;CHECK; %63 = OpTypeFunction %void %uint %uint %uint %uint %uint
-;CHECK; %_struct_70 = OpTypeStruct %uint %_runtimearr_uint
-;CHECK;%_ptr_StorageBuffer__struct_70 = OpTypePointer StorageBuffer %_struct_70
-;CHECK; %72 = OpVariable %_ptr_StorageBuffer__struct_70 StorageBuffer
+)" + kOutputGlobals + R"(
;CHECK; %uint_11 = OpConstant %uint 11
;CHECK; %uint_23 = OpConstant %uint 23
;CHECK; %uint_2 = OpConstant %uint 2
@@ -8588,7 +5212,7 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) {
;CHECK; %115 = OpConstantNull %float
%main = OpFunction %void None %3
%5 = OpLabel
-;CHECK: %55 = OpFunctionCall %uint %36 %uint_1 %uint_0 %uint_0
+;CHECK: %55 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0
;CHECK: OpBranch %26
;CHECK: %26 = OpLabel
;CHECK: OpBranch %25
@@ -8608,7 +5232,7 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) {
;CHECK: %61 = OpLoad %float %20
;CHECK: OpBranch %58
;CHECK: %60 = OpLabel
-;CHECK: %114 = OpFunctionCall %void %62 %uint_45 %uint_4 %uint_0 %35 %55
+;CHECK: %114 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_45 %uint_4 %uint_0 %35 %55
;CHECK: OpBranch %58
;CHECK: %58 = OpLabel
;CHECK: %116 = OpPhi %float %61 %59 %115 %60
@@ -8617,73 +5241,8 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) {
;CHECK: OpStore %v_vtxResult %116
OpReturn
OpFunctionEnd
-;CHECK: %36 = OpFunction %uint None %37
-;CHECK: %38 = OpFunctionParameter %uint
-;CHECK: %39 = OpFunctionParameter %uint
-;CHECK: %40 = OpFunctionParameter %uint
-;CHECK: %41 = OpLabel
-;CHECK: %47 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_0 %38
-;CHECK: %48 = OpLoad %uint %47
-;CHECK: %49 = OpIAdd %uint %48 %39
-;CHECK: %50 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_0 %49
-;CHECK: %51 = OpLoad %uint %50
-;CHECK: %52 = OpIAdd %uint %51 %40
-;CHECK: %53 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_0 %52
-;CHECK: %54 = OpLoad %uint %53
-;CHECK: OpReturnValue %54
-;CHECK: OpFunctionEnd
-;CHECK: %62 = OpFunction %void None %63
-;CHECK: %64 = OpFunctionParameter %uint
-;CHECK: %65 = OpFunctionParameter %uint
-;CHECK: %66 = OpFunctionParameter %uint
-;CHECK: %67 = OpFunctionParameter %uint
-;CHECK: %68 = OpFunctionParameter %uint
-;CHECK: %69 = OpLabel
-;CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_0
-;CHECK: %75 = OpAtomicIAdd %uint %73 %uint_4 %uint_0 %uint_11
-;CHECK: %76 = OpIAdd %uint %75 %uint_11
-;CHECK: %77 = OpArrayLength %uint %72 1
-;CHECK: %78 = OpULessThanEqual %bool %76 %77
-;CHECK: OpSelectionMerge %79 None
-;CHECK: OpBranchConditional %78 %80 %79
-;CHECK: %80 = OpLabel
-;CHECK: %81 = OpIAdd %uint %75 %uint_0
-;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %81
-;CHECK: OpStore %82 %uint_11
-;CHECK: %84 = OpIAdd %uint %75 %uint_1
-;CHECK: %85 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %84
-;CHECK: OpStore %85 %uint_23
-;CHECK: %87 = OpIAdd %uint %75 %uint_2
-;CHECK: %88 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %87
-;CHECK: OpStore %88 %64
-;CHECK: %89 = OpIAdd %uint %75 %uint_3
-;CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %89
-;CHECK: OpStore %90 %uint_0
-;CHECK: %93 = OpLoad %uint %gl_VertexIndex
-;CHECK: %94 = OpIAdd %uint %75 %uint_4
-;CHECK: %95 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %94
-;CHECK: OpStore %95 %93
-;CHECK: %97 = OpLoad %uint %gl_InstanceIndex
-;CHECK: %99 = OpIAdd %uint %75 %uint_5
-;CHECK: %100 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %99
-;CHECK: OpStore %100 %97
-;CHECK: %102 = OpIAdd %uint %75 %uint_7
-;CHECK: %103 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %102
-;CHECK: OpStore %103 %65
-;CHECK: %105 = OpIAdd %uint %75 %uint_8
-;CHECK: %106 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %105
-;CHECK: OpStore %106 %66
-;CHECK: %108 = OpIAdd %uint %75 %uint_9
-;CHECK: %109 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %108
-;CHECK: OpStore %109 %67
-;CHECK: %111 = OpIAdd %uint %75 %uint_10
-;CHECK: %112 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %111
-;CHECK: OpStore %112 %68
-;CHECK: OpBranch %79
-;CHECK: %79 = OpLabel
-;CHECK: OpReturn
-;CHECK: OpFunctionEnd
- )";
+ )" + kDirectRead3 + kStreamWrite5Vert;
+ // clang-format on
SetTargetEnv(SPV_ENV_VULKAN_1_2);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@@ -8710,13 +5269,14 @@ TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) {
// v_vtxResult = var[2][1];
// }
+ // clang-format off
const std::string text = R"(
OpCapability Shader
;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position
-;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %45 %72 %gl_VertexIndex %gl_InstanceIndex
+;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_VertexIndex %gl_InstanceIndex
OpSource GLSL 450
OpSourceExtension "GL_EXT_scalar_block_layout"
OpName %main "main"
@@ -8739,16 +5299,9 @@ TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) {
;CHECK: OpDecorate %115 RelaxedPrecision
OpDecorate %a_position Location 0
;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-;CHECK: OpDecorate %_struct_43 Block
-;CHECK: OpMemberDecorate %_struct_43 0 Offset 0
-;CHECK: OpDecorate %45 DescriptorSet 7
-;CHECK: OpDecorate %45 Binding 1
+)" + kInputDecorations + R"(
;CHECK: OpDecorate %61 RelaxedPrecision
-;CHECK: OpDecorate %_struct_70 Block
-;CHECK: OpMemberDecorate %_struct_70 0 Offset 0
-;CHECK: OpMemberDecorate %_struct_70 1 Offset 4
-;CHECK: OpDecorate %72 DescriptorSet 7
-;CHECK: OpDecorate %72 Binding 0
+)" + kOutputDecorations + R"(
;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
%void = OpTypeVoid
@@ -8776,15 +5329,11 @@ TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) {
;CHECK: %uint_3 = OpConstant %uint 3
;CHECK: %37 = OpTypeFunction %uint %uint %uint %uint
;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint
-;CHECK: %_struct_43 = OpTypeStruct %_runtimearr_uint
-;CHECK:%_ptr_StorageBuffer__struct_43 = OpTypePointer StorageBuffer %_struct_43
-;CHECK: %45 = OpVariable %_ptr_StorageBuffer__struct_43 StorageBuffer
+)" + kInputGlobals + R"(
;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
;CHECK: %bool = OpTypeBool
;CHECK: %63 = OpTypeFunction %void %uint %uint %uint %uint %uint
-;CHECK: %_struct_70 = OpTypeStruct %uint %_runtimearr_uint
-;CHECK:%_ptr_StorageBuffer__struct_70 = OpTypePointer StorageBuffer %_struct_70
-;CHECK: %72 = OpVariable %_ptr_StorageBuffer__struct_70 StorageBuffer
+)" + kOutputGlobals + R"(
;CHECK: %uint_11 = OpConstant %uint 11
;CHECK: %uint_23 = OpConstant %uint 23
;CHECK: %uint_2 = OpConstant %uint 2
@@ -8799,7 +5348,7 @@ TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) {
;CHECK: %114 = OpConstantNull %float
%main = OpFunction %void None %3
%5 = OpLabel
-;CHECK: %55 = OpFunctionCall %uint %36 %uint_1 %uint_0 %uint_0
+;CHECK: %55 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0
;CHECK: OpBranch %26
;CHECK: %26 = OpLabel
;CHECK: OpBranch %25
@@ -8819,7 +5368,7 @@ TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) {
;CHECK: %61 = OpLoad %float %20
;CHECK: OpBranch %58
;CHECK: %60 = OpLabel
-;CHECK: %113 = OpFunctionCall %void %62 %uint_45 %uint_4 %uint_0 %35 %55
+;CHECK: %113 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_45 %uint_4 %uint_0 %35 %55
;CHECK: OpBranch %58
;CHECK: %58 = OpLabel
;CHECK: %115 = OpPhi %float %61 %59 %114 %60
@@ -8828,75 +5377,11 @@ TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) {
;CHECK: OpStore %v_vtxResult %115
OpReturn
OpFunctionEnd
-;CHECK: %36 = OpFunction %uint None %37
-;CHECK: %38 = OpFunctionParameter %uint
-;CHECK: %39 = OpFunctionParameter %uint
-;CHECK: %40 = OpFunctionParameter %uint
-;CHECK: %41 = OpLabel
-;CHECK: %47 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_0 %38
-;CHECK: %48 = OpLoad %uint %47
-;CHECK: %49 = OpIAdd %uint %48 %39
-;CHECK: %50 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_0 %49
-;CHECK: %51 = OpLoad %uint %50
-;CHECK: %52 = OpIAdd %uint %51 %40
-;CHECK: %53 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_0 %52
-;CHECK: %54 = OpLoad %uint %53
-;CHECK: OpReturnValue %54
-;CHECK: OpFunctionEnd
-;CHECK: %62 = OpFunction %void None %63
-;CHECK: %64 = OpFunctionParameter %uint
-;CHECK: %65 = OpFunctionParameter %uint
-;CHECK: %66 = OpFunctionParameter %uint
-;CHECK: %67 = OpFunctionParameter %uint
-;CHECK: %68 = OpFunctionParameter %uint
-;CHECK: %69 = OpLabel
-;CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_0
-;CHECK: %75 = OpAtomicIAdd %uint %73 %uint_4 %uint_0 %uint_11
-;CHECK: %76 = OpIAdd %uint %75 %uint_11
-;CHECK: %77 = OpArrayLength %uint %72 1
-;CHECK: %78 = OpULessThanEqual %bool %76 %77
-;CHECK: OpSelectionMerge %79 None
-;CHECK: OpBranchConditional %78 %80 %79
-;CHECK: %80 = OpLabel
-;CHECK: %81 = OpIAdd %uint %75 %uint_0
-;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %81
-;CHECK: OpStore %82 %uint_11
-;CHECK: %84 = OpIAdd %uint %75 %uint_1
-;CHECK: %85 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %84
-;CHECK: OpStore %85 %uint_23
-;CHECK: %87 = OpIAdd %uint %75 %uint_2
-;CHECK: %88 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %87
-;CHECK: OpStore %88 %64
-;CHECK: %89 = OpIAdd %uint %75 %uint_3
-;CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %89
-;CHECK: OpStore %90 %uint_0
-;CHECK: %93 = OpLoad %uint %gl_VertexIndex
-;CHECK: %94 = OpIAdd %uint %75 %uint_4
-;CHECK: %95 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %94
-;CHECK: OpStore %95 %93
-;CHECK: %97 = OpLoad %uint %gl_InstanceIndex
-;CHECK: %99 = OpIAdd %uint %75 %uint_5
-;CHECK: %100 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %99
-;CHECK: OpStore %100 %97
-;CHECK: %102 = OpIAdd %uint %75 %uint_7
-;CHECK: %103 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %102
-;CHECK: OpStore %103 %65
-;CHECK: %104 = OpIAdd %uint %75 %uint_8
-;CHECK: %105 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %104
-;CHECK: OpStore %105 %66
-;CHECK: %107 = OpIAdd %uint %75 %uint_9
-;CHECK: %108 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %107
-;CHECK: OpStore %108 %67
-;CHECK: %110 = OpIAdd %uint %75 %uint_10
-;CHECK: %111 = OpAccessChain %_ptr_StorageBuffer_uint %72 %uint_1 %110
-;CHECK: OpStore %111 %68
-;CHECK: OpBranch %79
-;CHECK: %79 = OpLabel
-;CHECK: OpReturn
-;CHECK: OpFunctionEnd
- )";
+ )" + kDirectRead3 + kStreamWrite5Vert;
+ // clang-format on
SetTargetEnv(SPV_ENV_VULKAN_1_2);
+ ValidatorOptions()->uniform_buffer_standard_layout = true;
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<InstBindlessCheckPass>(text, true, 7u, 23u, false,
false, true, false, true);
@@ -8921,13 +5406,14 @@ TEST_F(InstBindlessTest, UniformMatrixVecRefRowMajor) {
// v_vtxResult = var[2][3][1];
// }
+ // clang-format off
const std::string text = R"(
OpCapability Shader
;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position
-;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %54 %81 %gl_VertexIndex %gl_InstanceIndex
+;CHECK: OpEntryPoint Vertex %main "main" %v_vtxResult %_ %a_position %inst_bindless_input_buffer %inst_bindless_output_buffer %gl_VertexIndex %gl_InstanceIndex
OpSource GLSL 450
OpSourceExtension "GL_EXT_scalar_block_layout"
OpName %main "main"
@@ -8951,16 +5437,9 @@ TEST_F(InstBindlessTest, UniformMatrixVecRefRowMajor) {
;CHECK: OpDecorate %125 RelaxedPrecision
OpDecorate %a_position Location 0
;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-;CHECK: OpDecorate %_struct_52 Block
-;CHECK: OpMemberDecorate %_struct_52 0 Offset 0
-;CHECK: OpDecorate %54 DescriptorSet 7
-;CHECK: OpDecorate %54 Binding 1
+)" + kInputDecorations + R"(
;CHECK: OpDecorate %70 RelaxedPrecision
-;CHECK: OpDecorate %_struct_79 Block
-;CHECK: OpMemberDecorate %_struct_79 0 Offset 0
-;CHECK: OpMemberDecorate %_struct_79 1 Offset 4
-;CHECK: OpDecorate %81 DescriptorSet 7
-;CHECK: OpDecorate %81 Binding 0
+)" + kOutputDecorations + R"(
;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
%void = OpTypeVoid
@@ -8995,15 +5474,11 @@ TEST_F(InstBindlessTest, UniformMatrixVecRefRowMajor) {
;CHECK: %uint_1 = OpConstant %uint 1
;CHECK: %46 = OpTypeFunction %uint %uint %uint %uint
;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint
-;CHECK: %_struct_52 = OpTypeStruct %_runtimearr_uint
-;CHECK:%_ptr_StorageBuffer__struct_52 = OpTypePointer StorageBuffer %_struct_52
-;CHECK: %54 = OpVariable %_ptr_StorageBuffer__struct_52 StorageBuffer
+)" + kInputGlobals + R"(
;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
;CHECK: %bool = OpTypeBool
;CHECK: %72 = OpTypeFunction %void %uint %uint %uint %uint %uint
-;CHECK: %_struct_79 = OpTypeStruct %uint %_runtimearr_uint
-;CHECK:%_ptr_StorageBuffer__struct_79 = OpTypePointer StorageBuffer %_struct_79
-;CHECK: %81 = OpVariable %_ptr_StorageBuffer__struct_79 StorageBuffer
+)" + kOutputGlobals + R"(
;CHECK: %uint_11 = OpConstant %uint 11
;CHECK: %uint_23 = OpConstant %uint 23
;CHECK: %uint_2 = OpConstant %uint 2
@@ -9019,7 +5494,7 @@ TEST_F(InstBindlessTest, UniformMatrixVecRefRowMajor) {
;CHECK: %124 = OpConstantNull %v2float
%main = OpFunction %void None %3
%5 = OpLabel
-;CHECK: %64 = OpFunctionCall %uint %45 %uint_1 %uint_0 %uint_0
+;CHECK: %64 = OpFunctionCall %uint %inst_bindless_direct_read_3 %uint_1 %uint_0 %uint_0
;CHECK: OpBranch %31
;CHECK: %31 = OpLabel
;CHECK: OpBranch %30
@@ -9043,80 +5518,15 @@ TEST_F(InstBindlessTest, UniformMatrixVecRefRowMajor) {
;CHECK: %70 = OpLoad %v2float %25
;CHECK: OpBranch %67
;CHECK: %69 = OpLabel
-;CHECK: %123 = OpFunctionCall %void %71 %uint_51 %uint_4 %uint_0 %43 %64
+;CHECK: %123 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_51 %uint_4 %uint_0 %43 %64
;CHECK: OpBranch %67
;CHECK: %67 = OpLabel
;CHECK: %125 = OpPhi %v2float %70 %68 %124 %69
;CHECK: OpStore %v_vtxResult %125
OpReturn
OpFunctionEnd
-;CHECK: %45 = OpFunction %uint None %46
-;CHECK: %47 = OpFunctionParameter %uint
-;CHECK: %48 = OpFunctionParameter %uint
-;CHECK: %49 = OpFunctionParameter %uint
-;CHECK: %50 = OpLabel
-;CHECK: %56 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_0 %47
-;CHECK: %57 = OpLoad %uint %56
-;CHECK: %58 = OpIAdd %uint %57 %48
-;CHECK: %59 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_0 %58
-;CHECK: %60 = OpLoad %uint %59
-;CHECK: %61 = OpIAdd %uint %60 %49
-;CHECK: %62 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_0 %61
-;CHECK: %63 = OpLoad %uint %62
-;CHECK: OpReturnValue %63
-;CHECK: OpFunctionEnd
-;CHECK: %71 = OpFunction %void None %72
-;CHECK: %73 = OpFunctionParameter %uint
-;CHECK: %74 = OpFunctionParameter %uint
-;CHECK: %75 = OpFunctionParameter %uint
-;CHECK: %76 = OpFunctionParameter %uint
-;CHECK: %77 = OpFunctionParameter %uint
-;CHECK: %78 = OpLabel
-;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_0
-;CHECK: %84 = OpAtomicIAdd %uint %82 %uint_4 %uint_0 %uint_11
-;CHECK: %85 = OpIAdd %uint %84 %uint_11
-;CHECK: %86 = OpArrayLength %uint %81 1
-;CHECK: %87 = OpULessThanEqual %bool %85 %86
-;CHECK: OpSelectionMerge %88 None
-;CHECK: OpBranchConditional %87 %89 %88
-;CHECK: %89 = OpLabel
-;CHECK: %90 = OpIAdd %uint %84 %uint_0
-;CHECK: %91 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %90
-;CHECK: OpStore %91 %uint_11
-;CHECK: %93 = OpIAdd %uint %84 %uint_1
-;CHECK: %94 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %93
-;CHECK: OpStore %94 %uint_23
-;CHECK: %96 = OpIAdd %uint %84 %uint_2
-;CHECK: %97 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %96
-;CHECK: OpStore %97 %73
-;CHECK: %98 = OpIAdd %uint %84 %uint_3
-;CHECK: %99 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %98
-;CHECK: OpStore %99 %uint_0
-;CHECK: %102 = OpLoad %uint %gl_VertexIndex
-;CHECK: %103 = OpIAdd %uint %84 %uint_4
-;CHECK: %104 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %103
-;CHECK: OpStore %104 %102
-;CHECK: %106 = OpLoad %uint %gl_InstanceIndex
-;CHECK: %108 = OpIAdd %uint %84 %uint_5
-;CHECK: %109 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %108
-;CHECK: OpStore %109 %106
-;CHECK: %111 = OpIAdd %uint %84 %uint_7
-;CHECK: %112 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %111
-;CHECK: OpStore %112 %74
-;CHECK: %114 = OpIAdd %uint %84 %uint_8
-;CHECK: %115 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %114
-;CHECK: OpStore %115 %75
-;CHECK: %117 = OpIAdd %uint %84 %uint_9
-;CHECK: %118 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %117
-;CHECK: OpStore %118 %76
-;CHECK: %120 = OpIAdd %uint %84 %uint_10
-;CHECK: %121 = OpAccessChain %_ptr_StorageBuffer_uint %81 %uint_1 %120
-;CHECK: OpStore %121 %77
-;CHECK: OpBranch %88
-;CHECK: %88 = OpLabel
-;CHECK: OpReturn
-;CHECK: OpFunctionEnd
- )";
+ )" + kDirectRead3 + kStreamWrite5Vert;
+ // clang-format on
SetTargetEnv(SPV_ENV_VULKAN_1_2);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@@ -9136,6 +5546,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBRead) {
// x = imageLoad(s, ii);
// }
+ // clang-format off
const std::string text = R"(
OpCapability Shader
OpCapability ImageBuffer
@@ -9157,11 +5568,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBRead) {
OpDecorate %ii Flat
OpDecorate %ii Location 13
;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-;CHECK: OpDecorate %_struct_43 Block
-;CHECK: OpMemberDecorate %_struct_43 0 Offset 0
-;CHECK: OpMemberDecorate %_struct_43 1 Offset 4
-;CHECK: OpDecorate %45 DescriptorSet 7
-;CHECK: OpDecorate %45 Binding 0
+)" + kOutputDecorations + R"(
;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%3 = OpTypeFunction %void
@@ -9181,9 +5588,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBRead) {
;CHECK: %uint_7 = OpConstant %uint 7
;CHECK: %35 = OpTypeFunction %void %uint %uint %uint %uint %uint
;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-;CHECK: %_struct_43 = OpTypeStruct %uint %_runtimearr_uint
-;CHECK: %_ptr_StorageBuffer__struct_43 = OpTypePointer StorageBuffer %_struct_43
-;CHECK: %45 = OpVariable %_ptr_StorageBuffer__struct_43 StorageBuffer
+)" + kOutputGlobals + R"(
;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
;CHECK: %uint_11 = OpConstant %uint 11
;CHECK: %uint_4 = OpConstant %uint 4
@@ -9224,67 +5629,15 @@ TEST_F(InstBindlessTest, ImageBufferOOBRead) {
;CHECK: %33 = OpImageRead %v4float %32 %17
;CHECK: OpBranch %29
;CHECK: %31 = OpLabel
-;CHECK: %92 = OpFunctionCall %void %34 %uint_33 %uint_7 %uint_0 %23 %25
+;CHECK: %92 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_33 %uint_7 %uint_0 %23 %25
;CHECK: OpBranch %29
;CHECK: %29 = OpLabel
;CHECK: %94 = OpPhi %v4float %33 %30 %93 %31
;CHECK: OpStore %x %94
OpReturn
OpFunctionEnd
-;CHECK: %34 = OpFunction %void None %35
-;CHECK: %36 = OpFunctionParameter %uint
-;CHECK: %37 = OpFunctionParameter %uint
-;CHECK: %38 = OpFunctionParameter %uint
-;CHECK: %39 = OpFunctionParameter %uint
-;CHECK: %40 = OpFunctionParameter %uint
-;CHECK: %41 = OpLabel
-;CHECK: %47 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_0
-;CHECK: %50 = OpAtomicIAdd %uint %47 %uint_4 %uint_0 %uint_11
-;CHECK: %51 = OpIAdd %uint %50 %uint_11
-;CHECK: %52 = OpArrayLength %uint %45 1
-;CHECK: %53 = OpULessThanEqual %bool %51 %52
-;CHECK: OpSelectionMerge %54 None
-;CHECK: OpBranchConditional %53 %55 %54
-;CHECK: %55 = OpLabel
-;CHECK: %56 = OpIAdd %uint %50 %uint_0
-;CHECK: %58 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %56
-;CHECK: OpStore %58 %uint_11
-;CHECK: %60 = OpIAdd %uint %50 %uint_1
-;CHECK: %61 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %60
-;CHECK: OpStore %61 %uint_23
-;CHECK: %63 = OpIAdd %uint %50 %uint_2
-;CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %63
-;CHECK: OpStore %64 %36
-;CHECK: %66 = OpIAdd %uint %50 %uint_3
-;CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %66
-;CHECK: OpStore %67 %uint_4
-;CHECK: %70 = OpLoad %v4float %gl_FragCoord
-;CHECK: %72 = OpBitcast %v4uint %70
-;CHECK: %73 = OpCompositeExtract %uint %72 0
-;CHECK: %74 = OpIAdd %uint %50 %uint_4
-;CHECK: %75 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %74
-;CHECK: OpStore %75 %73
-;CHECK: %76 = OpCompositeExtract %uint %72 1
-;CHECK: %78 = OpIAdd %uint %50 %uint_5
-;CHECK: %79 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %78
-;CHECK: OpStore %79 %76
-;CHECK: %80 = OpIAdd %uint %50 %uint_7
-;CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %80
-;CHECK: OpStore %81 %37
-;CHECK: %83 = OpIAdd %uint %50 %uint_8
-;CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %83
-;CHECK: OpStore %84 %38
-;CHECK: %86 = OpIAdd %uint %50 %uint_9
-;CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %86
-;CHECK: OpStore %87 %39
-;CHECK: %89 = OpIAdd %uint %50 %uint_10
-;CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %89
-;CHECK: OpStore %90 %40
-;CHECK: OpBranch %54
-;CHECK: %54 = OpLabel
-;CHECK: OpReturn
-;CHECK: OpFunctionEnd
- )";
+ )" + kStreamWrite5Frag;
+ // clang-format on
SetTargetEnv(SPV_ENV_VULKAN_1_2);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@@ -9304,6 +5657,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) {
// imageStore(s, ii, x);
// }
+ // clang-format off
const std::string text = R"(
OpCapability Shader
OpCapability ImageBuffer
@@ -9312,7 +5666,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) {
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %s %ii %x
-;CHECK: OpEntryPoint Fragment %main "main" %s %ii %x %44 %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %main "main" %s %ii %x %inst_bindless_output_buffer %gl_FragCoord
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
@@ -9326,11 +5680,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) {
OpDecorate %ii Location 13
OpDecorate %x Location 11
;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-;CHECK: OpDecorate %_struct_42 Block
-;CHECK: OpMemberDecorate %_struct_42 0 Offset 0
-;CHECK: OpMemberDecorate %_struct_42 1 Offset 4
-;CHECK: OpDecorate %44 DescriptorSet 7
-;CHECK: OpDecorate %44 Binding 0
+)" + kOutputDecorations + R"(
;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%3 = OpTypeFunction %void
@@ -9350,9 +5700,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) {
;CHECK: %uint_7 = OpConstant %uint 7
;CHECK: %34 = OpTypeFunction %void %uint %uint %uint %uint %uint
;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-;CHECK: %_struct_42 = OpTypeStruct %uint %_runtimearr_uint
-;CHECK: %_ptr_StorageBuffer__struct_42 = OpTypePointer StorageBuffer %_struct_42
-;CHECK: %44 = OpVariable %_ptr_StorageBuffer__struct_42 StorageBuffer
+)" + kOutputGlobals + R"(
;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
;CHECK: %uint_11 = OpConstant %uint 11
;CHECK: %uint_4 = OpConstant %uint 4
@@ -9391,65 +5739,13 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) {
;CHECK: OpImageWrite %32 %14 %18
;CHECK: OpBranch %29
;CHECK: %31 = OpLabel
-;CHECK: %91 = OpFunctionCall %void %33 %uint_34 %uint_7 %uint_0 %23 %25
+;CHECK: %91 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_34 %uint_7 %uint_0 %23 %25
;CHECK: OpBranch %29
;CHECK: %29 = OpLabel
OpReturn
OpFunctionEnd
-;CHECK: %33 = OpFunction %void None %34
-;CHECK: %35 = OpFunctionParameter %uint
-;CHECK: %36 = OpFunctionParameter %uint
-;CHECK: %37 = OpFunctionParameter %uint
-;CHECK: %38 = OpFunctionParameter %uint
-;CHECK: %39 = OpFunctionParameter %uint
-;CHECK: %40 = OpLabel
-;CHECK: %46 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_0
-;CHECK: %49 = OpAtomicIAdd %uint %46 %uint_4 %uint_0 %uint_11
-;CHECK: %50 = OpIAdd %uint %49 %uint_11
-;CHECK: %51 = OpArrayLength %uint %44 1
-;CHECK: %52 = OpULessThanEqual %bool %50 %51
-;CHECK: OpSelectionMerge %53 None
-;CHECK: OpBranchConditional %52 %54 %53
-;CHECK: %54 = OpLabel
-;CHECK: %55 = OpIAdd %uint %49 %uint_0
-;CHECK: %57 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %55
-;CHECK: OpStore %57 %uint_11
-;CHECK: %59 = OpIAdd %uint %49 %uint_1
-;CHECK: %60 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %59
-;CHECK: OpStore %60 %uint_23
-;CHECK: %62 = OpIAdd %uint %49 %uint_2
-;CHECK: %63 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %62
-;CHECK: OpStore %63 %35
-;CHECK: %65 = OpIAdd %uint %49 %uint_3
-;CHECK: %66 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %65
-;CHECK: OpStore %66 %uint_4
-;CHECK: %69 = OpLoad %v4float %gl_FragCoord
-;CHECK: %71 = OpBitcast %v4uint %69
-;CHECK: %72 = OpCompositeExtract %uint %71 0
-;CHECK: %73 = OpIAdd %uint %49 %uint_4
-;CHECK: %74 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %73
-;CHECK: OpStore %74 %72
-;CHECK: %75 = OpCompositeExtract %uint %71 1
-;CHECK: %77 = OpIAdd %uint %49 %uint_5
-;CHECK: %78 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %77
-;CHECK: OpStore %78 %75
-;CHECK: %79 = OpIAdd %uint %49 %uint_7
-;CHECK: %80 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %79
-;CHECK: OpStore %80 %36
-;CHECK: %82 = OpIAdd %uint %49 %uint_8
-;CHECK: %83 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %82
-;CHECK: OpStore %83 %37
-;CHECK: %85 = OpIAdd %uint %49 %uint_9
-;CHECK: %86 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %85
-;CHECK: OpStore %86 %38
-;CHECK: %88 = OpIAdd %uint %49 %uint_10
-;CHECK: %89 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %88
-;CHECK: OpStore %89 %39
-;CHECK: OpBranch %53
-;CHECK: %53 = OpLabel
-;CHECK: OpReturn
-;CHECK: OpFunctionEnd
- )";
+ )" + kStreamWrite5Frag;
+ // clang-format on
SetTargetEnv(SPV_ENV_VULKAN_1_2);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@@ -9469,6 +5765,7 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) {
// x = texelFetch(s, ii);
// }
+ // clang-format off
const std::string text = R"(
OpCapability Shader
OpCapability SampledBuffer
@@ -9477,7 +5774,7 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) {
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %x %s %ii
-;CHECK: OpEntryPoint Fragment %main "main" %x %s %ii %45 %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %main "main" %x %s %ii %inst_bindless_output_buffer %gl_FragCoord
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
@@ -9490,11 +5787,7 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) {
OpDecorate %ii Flat
OpDecorate %ii Location 13
;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-;CHECK: OpDecorate %_struct_43 Block
-;CHECK: OpMemberDecorate %_struct_43 0 Offset 0
-;CHECK: OpMemberDecorate %_struct_43 1 Offset 4
-;CHECK: OpDecorate %45 DescriptorSet 7
-;CHECK: OpDecorate %45 Binding 0
+)" + kOutputDecorations + R"(
;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%3 = OpTypeFunction %void
@@ -9514,9 +5807,7 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) {
;CHECK: %uint_6 = OpConstant %uint 6
;CHECK: %35 = OpTypeFunction %void %uint %uint %uint %uint %uint
;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-;CHECK: %_struct_43 = OpTypeStruct %uint %_runtimearr_uint
-;CHECK: %_ptr_StorageBuffer__struct_43 = OpTypePointer StorageBuffer %_struct_43
-;CHECK: %45 = OpVariable %_ptr_StorageBuffer__struct_43 StorageBuffer
+)" + kOutputGlobals + R"(
;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
;CHECK: %uint_11 = OpConstant %uint 11
;CHECK: %uint_4 = OpConstant %uint 4
@@ -9558,67 +5849,15 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) {
;CHECK: %33 = OpImageFetch %v4float %32 %17
;CHECK: OpBranch %29
;CHECK: %31 = OpLabel
-;CHECK: %93 = OpFunctionCall %void %34 %uint_32 %uint_6 %uint_0 %23 %25
+;CHECK: %93 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_32 %uint_6 %uint_0 %23 %25
;CHECK: OpBranch %29
;CHECK: %29 = OpLabel
;CHECK: %95 = OpPhi %v4float %33 %30 %94 %31
;CHECK: OpStore %x %95
OpReturn
OpFunctionEnd
-;CHECK: %34 = OpFunction %void None %35
-;CHECK: %36 = OpFunctionParameter %uint
-;CHECK: %37 = OpFunctionParameter %uint
-;CHECK: %38 = OpFunctionParameter %uint
-;CHECK: %39 = OpFunctionParameter %uint
-;CHECK: %40 = OpFunctionParameter %uint
-;CHECK: %41 = OpLabel
-;CHECK: %47 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_0
-;CHECK: %50 = OpAtomicIAdd %uint %47 %uint_4 %uint_0 %uint_11
-;CHECK: %51 = OpIAdd %uint %50 %uint_11
-;CHECK: %52 = OpArrayLength %uint %45 1
-;CHECK: %53 = OpULessThanEqual %bool %51 %52
-;CHECK: OpSelectionMerge %54 None
-;CHECK: OpBranchConditional %53 %55 %54
-;CHECK: %55 = OpLabel
-;CHECK: %56 = OpIAdd %uint %50 %uint_0
-;CHECK: %58 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %56
-;CHECK: OpStore %58 %uint_11
-;CHECK: %60 = OpIAdd %uint %50 %uint_1
-;CHECK: %61 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %60
-;CHECK: OpStore %61 %uint_23
-;CHECK: %63 = OpIAdd %uint %50 %uint_2
-;CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %63
-;CHECK: OpStore %64 %36
-;CHECK: %66 = OpIAdd %uint %50 %uint_3
-;CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %66
-;CHECK: OpStore %67 %uint_4
-;CHECK: %70 = OpLoad %v4float %gl_FragCoord
-;CHECK: %72 = OpBitcast %v4uint %70
-;CHECK: %73 = OpCompositeExtract %uint %72 0
-;CHECK: %74 = OpIAdd %uint %50 %uint_4
-;CHECK: %75 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %74
-;CHECK: OpStore %75 %73
-;CHECK: %76 = OpCompositeExtract %uint %72 1
-;CHECK: %78 = OpIAdd %uint %50 %uint_5
-;CHECK: %79 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %78
-;CHECK: OpStore %79 %76
-;CHECK: %81 = OpIAdd %uint %50 %uint_7
-;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %81
-;CHECK: OpStore %82 %37
-;CHECK: %84 = OpIAdd %uint %50 %uint_8
-;CHECK: %85 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %84
-;CHECK: OpStore %85 %38
-;CHECK: %87 = OpIAdd %uint %50 %uint_9
-;CHECK: %88 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %87
-;CHECK: OpStore %88 %39
-;CHECK: %90 = OpIAdd %uint %50 %uint_10
-;CHECK: %91 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %90
-;CHECK: OpStore %91 %40
-;CHECK: OpBranch %54
-;CHECK: %54 = OpLabel
-;CHECK: OpReturn
-;CHECK: OpFunctionEnd
- )";
+ )" + kStreamWrite5Frag;
+ // clang-format on
SetTargetEnv(SPV_ENV_VULKAN_1_2);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@@ -9638,6 +5877,7 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) {
// x = texelFetch(s, ii);
// }
+ // clang-format off
const std::string text = R"(
OpCapability Shader
OpCapability SampledBuffer
@@ -9646,7 +5886,7 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) {
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %x %s %ii
-;CHECK: OpEntryPoint Fragment %main "main" %x %s %ii %48 %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %main "main" %x %s %ii %inst_bindless_output_buffer %gl_FragCoord
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
@@ -9659,11 +5899,7 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) {
OpDecorate %ii Flat
OpDecorate %ii Location 13
;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-;CHECK: OpDecorate %_struct_46 Block
-;CHECK: OpMemberDecorate %_struct_46 0 Offset 0
-;CHECK: OpMemberDecorate %_struct_46 1 Offset 4
-;CHECK: OpDecorate %48 DescriptorSet 7
-;CHECK: OpDecorate %48 Binding 0
+)" + kOutputDecorations + R"(
;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%3 = OpTypeFunction %void
@@ -9684,9 +5920,7 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) {
;CHECK: %uint_6 = OpConstant %uint 6
;CHECK: %38 = OpTypeFunction %void %uint %uint %uint %uint %uint
;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-;CHECK: %_struct_46 = OpTypeStruct %uint %_runtimearr_uint
-;CHECK: %_ptr_StorageBuffer__struct_46 = OpTypePointer StorageBuffer %_struct_46
-;CHECK: %48 = OpVariable %_ptr_StorageBuffer__struct_46 StorageBuffer
+)" + kOutputGlobals + R"(
;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
;CHECK: %uint_11 = OpConstant %uint 11
;CHECK: %uint_4 = OpConstant %uint 4
@@ -9730,67 +5964,15 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) {
;CHECK: %36 = OpImageFetch %v4float %35 %18
;CHECK: OpBranch %31
;CHECK: %33 = OpLabel
-;CHECK: %96 = OpFunctionCall %void %37 %uint_34 %uint_6 %uint_0 %25 %27
+;CHECK: %96 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_34 %uint_6 %uint_0 %25 %27
;CHECK: OpBranch %31
;CHECK: %31 = OpLabel
;CHECK: %98 = OpPhi %v4float %36 %32 %97 %33
;CHECK: OpStore %x %98
OpReturn
OpFunctionEnd
-;CHECK: %37 = OpFunction %void None %38
-;CHECK: %39 = OpFunctionParameter %uint
-;CHECK: %40 = OpFunctionParameter %uint
-;CHECK: %41 = OpFunctionParameter %uint
-;CHECK: %42 = OpFunctionParameter %uint
-;CHECK: %43 = OpFunctionParameter %uint
-;CHECK: %44 = OpLabel
-;CHECK: %50 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0
-;CHECK: %53 = OpAtomicIAdd %uint %50 %uint_4 %uint_0 %uint_11
-;CHECK: %54 = OpIAdd %uint %53 %uint_11
-;CHECK: %55 = OpArrayLength %uint %48 1
-;CHECK: %56 = OpULessThanEqual %bool %54 %55
-;CHECK: OpSelectionMerge %57 None
-;CHECK: OpBranchConditional %56 %58 %57
-;CHECK: %58 = OpLabel
-;CHECK: %59 = OpIAdd %uint %53 %uint_0
-;CHECK: %61 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %59
-;CHECK: OpStore %61 %uint_11
-;CHECK: %63 = OpIAdd %uint %53 %uint_1
-;CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %63
-;CHECK: OpStore %64 %uint_23
-;CHECK: %66 = OpIAdd %uint %53 %uint_2
-;CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %66
-;CHECK: OpStore %67 %39
-;CHECK: %69 = OpIAdd %uint %53 %uint_3
-;CHECK: %70 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %69
-;CHECK: OpStore %70 %uint_4
-;CHECK: %73 = OpLoad %v4float %gl_FragCoord
-;CHECK: %75 = OpBitcast %v4uint %73
-;CHECK: %76 = OpCompositeExtract %uint %75 0
-;CHECK: %77 = OpIAdd %uint %53 %uint_4
-;CHECK: %78 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %77
-;CHECK: OpStore %78 %76
-;CHECK: %79 = OpCompositeExtract %uint %75 1
-;CHECK: %81 = OpIAdd %uint %53 %uint_5
-;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %81
-;CHECK: OpStore %82 %79
-;CHECK: %84 = OpIAdd %uint %53 %uint_7
-;CHECK: %85 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %84
-;CHECK: OpStore %85 %40
-;CHECK: %87 = OpIAdd %uint %53 %uint_8
-;CHECK: %88 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %87
-;CHECK: OpStore %88 %41
-;CHECK: %90 = OpIAdd %uint %53 %uint_9
-;CHECK: %91 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %90
-;CHECK: OpStore %91 %42
-;CHECK: %93 = OpIAdd %uint %53 %uint_10
-;CHECK: %94 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %93
-;CHECK: OpStore %94 %43
-;CHECK: OpBranch %57
-;CHECK: %57 = OpLabel
-;CHECK: OpReturn
-;CHECK: OpFunctionEnd
- )";
+ )" + kStreamWrite5Frag;
+ // clang-format on
SetTargetEnv(SPV_ENV_VULKAN_1_2);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
@@ -9811,6 +5993,7 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) {
// x = texelFetch(samplerBuffer(tBuf, s), ii);
// }
+ // clang-format off
const std::string text = R"(
OpCapability Shader
OpCapability SampledBuffer
@@ -9819,7 +6002,7 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) {
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %x %tBuf %s %ii
-;CHECK: OpEntryPoint Fragment %main "main" %x %tBuf %s %ii %54 %gl_FragCoord
+;CHECK: OpEntryPoint Fragment %main "main" %x %tBuf %s %ii %inst_bindless_output_buffer %gl_FragCoord
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
@@ -9835,11 +6018,7 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) {
OpDecorate %ii Flat
OpDecorate %ii Location 13
;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-;CHECK: OpDecorate %_struct_52 Block
-;CHECK: OpMemberDecorate %_struct_52 0 Offset 0
-;CHECK: OpMemberDecorate %_struct_52 1 Offset 4
-;CHECK: OpDecorate %54 DescriptorSet 7
-;CHECK: OpDecorate %54 Binding 0
+)" + kOutputDecorations + R"(
;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%3 = OpTypeFunction %void
@@ -9863,9 +6042,7 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) {
;CHECK: %uint_6 = OpConstant %uint 6
;CHECK: %44 = OpTypeFunction %void %uint %uint %uint %uint %uint
;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-;CHECK: %_struct_52 = OpTypeStruct %uint %_runtimearr_uint
-;CHECK: %_ptr_StorageBuffer__struct_52 = OpTypePointer StorageBuffer %_struct_52
-;CHECK: %54 = OpVariable %_ptr_StorageBuffer__struct_52 StorageBuffer
+)" + kOutputGlobals + R"(
;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
;CHECK: %uint_11 = OpConstant %uint 11
;CHECK: %uint_4 = OpConstant %uint 4
@@ -9912,67 +6089,15 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) {
;CHECK: %42 = OpImageFetch %v4float %41 %23
;CHECK: OpBranch %36
;CHECK: %38 = OpLabel
-;CHECK: %102 = OpFunctionCall %void %43 %uint_42 %uint_6 %uint_0 %30 %32
+;CHECK: %102 = OpFunctionCall %void %inst_bindless_stream_write_5 %uint_42 %uint_6 %uint_0 %30 %32
;CHECK: OpBranch %36
;CHECK: %36 = OpLabel
;CHECK: %104 = OpPhi %v4float %42 %37 %103 %38
;CHECK: OpStore %x %104
OpReturn
OpFunctionEnd
-;CHECK: %43 = OpFunction %void None %44
-;CHECK: %45 = OpFunctionParameter %uint
-;CHECK: %46 = OpFunctionParameter %uint
-;CHECK: %47 = OpFunctionParameter %uint
-;CHECK: %48 = OpFunctionParameter %uint
-;CHECK: %49 = OpFunctionParameter %uint
-;CHECK: %50 = OpLabel
-;CHECK: %56 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_0
-;CHECK: %59 = OpAtomicIAdd %uint %56 %uint_4 %uint_0 %uint_11
-;CHECK: %60 = OpIAdd %uint %59 %uint_11
-;CHECK: %61 = OpArrayLength %uint %54 1
-;CHECK: %62 = OpULessThanEqual %bool %60 %61
-;CHECK: OpSelectionMerge %63 None
-;CHECK: OpBranchConditional %62 %64 %63
-;CHECK: %64 = OpLabel
-;CHECK: %65 = OpIAdd %uint %59 %uint_0
-;CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %65
-;CHECK: OpStore %67 %uint_11
-;CHECK: %69 = OpIAdd %uint %59 %uint_1
-;CHECK: %70 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %69
-;CHECK: OpStore %70 %uint_23
-;CHECK: %72 = OpIAdd %uint %59 %uint_2
-;CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %72
-;CHECK: OpStore %73 %45
-;CHECK: %75 = OpIAdd %uint %59 %uint_3
-;CHECK: %76 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %75
-;CHECK: OpStore %76 %uint_4
-;CHECK: %79 = OpLoad %v4float %gl_FragCoord
-;CHECK: %81 = OpBitcast %v4uint %79
-;CHECK: %82 = OpCompositeExtract %uint %81 0
-;CHECK: %83 = OpIAdd %uint %59 %uint_4
-;CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %83
-;CHECK: OpStore %84 %82
-;CHECK: %85 = OpCompositeExtract %uint %81 1
-;CHECK: %87 = OpIAdd %uint %59 %uint_5
-;CHECK: %88 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %87
-;CHECK: OpStore %88 %85
-;CHECK: %90 = OpIAdd %uint %59 %uint_7
-;CHECK: %91 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %90
-;CHECK: OpStore %91 %46
-;CHECK: %93 = OpIAdd %uint %59 %uint_8
-;CHECK: %94 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %93
-;CHECK: OpStore %94 %47
-;CHECK: %96 = OpIAdd %uint %59 %uint_9
-;CHECK: %97 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %96
-;CHECK: OpStore %97 %48
-;CHECK: %99 = OpIAdd %uint %59 %uint_10
-;CHECK: %100 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %99
-;CHECK: OpStore %100 %49
-;CHECK: OpBranch %63
-;CHECK: %63 = OpLabel
-;CHECK: OpReturn
-;CHECK: OpFunctionEnd
- )";
+ )" + kStreamWrite5Frag;
+ // clang-format on
SetTargetEnv(SPV_ENV_VULKAN_1_2);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
diff --git a/test/opt/inst_buff_addr_check_test.cpp b/test/opt/inst_buff_addr_check_test.cpp
index 95114b23..e095eb77 100644
--- a/test/opt/inst_buff_addr_check_test.cpp
+++ b/test/opt/inst_buff_addr_check_test.cpp
@@ -1,5 +1,5 @@
-// Copyright (c) 2019 Valve Corporation
-// Copyright (c) 2019 LunarG Inc.
+// Copyright (c) 2019-2022 Valve Corporation
+// Copyright (c) 2019-2022 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -27,6 +27,148 @@ namespace spvtools {
namespace opt {
namespace {
+static const std::string kOutputDecorations = R"(
+; CHECK: OpDecorate [[output_buffer_type:%inst_buff_addr_OutputBuffer]] Block
+; CHECK: OpMemberDecorate [[output_buffer_type]] 0 Offset 0
+; CHECK: OpMemberDecorate [[output_buffer_type]] 1 Offset 4
+; CHECK: OpDecorate [[output_buffer_var:%\w+]] DescriptorSet 7
+; CHECK: OpDecorate [[output_buffer_var]] Binding 0
+)";
+
+static const std::string kOutputGlobals = R"(
+; CHECK: [[output_buffer_type]] = OpTypeStruct %uint %_runtimearr_uint
+; CHECK: [[output_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[output_buffer_type]]
+; CHECK: [[output_buffer_var]] = OpVariable [[output_ptr_type]] StorageBuffer
+)";
+
+static const std::string kStreamWrite4Begin = R"(
+; CHECK: {{%\w+}} = OpFunction %void None {{%\w+}}
+; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint
+; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_0
+; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_10
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10
+; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 1
+; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}}
+; CHECK: OpSelectionMerge {{%\w+}} None
+; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} %uint_10
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} %uint_23
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[param_1]]
+)";
+
+static const std::string kStreamWrite4End = R"(
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[param_2]]
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[param_3]]
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} [[param_4]]
+; CHECK: OpBranch {{%\w+}}
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: OpReturn
+; CHECK: OpFunctionEnd
+)";
+
+// clang-format off
+static const std::string kStreamWrite4Frag = kStreamWrite4Begin + R"(
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} %uint_4
+; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
+; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
+; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+)" + kStreamWrite4End;
+
+static const std::string kStreamWrite4Compute = kStreamWrite4Begin + R"(
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} %uint_5
+; CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
+; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
+; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
+; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 {{%\w+}}
+; CHECK: OpStore {{%\w+}} {{%\w+}}
+)" + kStreamWrite4End;
+// clang-format on
+
+static const std::string kInputDecorations = R"(
+; CHECK: OpDecorate [[input_buffer_type:%inst_buff_addr_InputBuffer]] Block
+; CHECK: OpMemberDecorate [[input_buffer_type]] 0 Offset 0
+; CHECK: OpDecorate [[input_buffer_var:%\w+]] DescriptorSet 7
+; CHECK: OpDecorate [[input_buffer_var]] Binding 2
+)";
+
+static const std::string kInputGlobals = R"(
+; CHECK: [[input_buffer_type]] = OpTypeStruct %_runtimearr_ulong
+; CHECK: [[input_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[input_buffer_type]]
+; CHECK: [[input_buffer_var]] = OpVariable [[input_ptr_type]] StorageBuffer
+)";
+
+static const std::string kSearchAndTest = R"(
+; CHECK: {{%\w+}} = OpFunction %bool None {{%\w+}}
+; CHECK: [[param_1:%\w+]] = OpFunctionParameter %ulong
+; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: OpBranch {{%\w+}}
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: {{%\w+}} = OpPhi %uint %uint_1 {{%\w+}} {{%\w+}} {{%\w+}}
+; CHECK: OpLoopMerge {{%\w+}} {{%\w+}} None
+; CHECK: OpBranch {{%\w+}}
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}}
+; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}}
+; CHECK: {{%\w+}} = OpUGreaterThan %bool {{%\w+}} [[param_1]]
+; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpLabel
+; CHECK: {{%\w+}} = OpISub %uint {{%\w+}} %uint_1
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}}
+; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}}
+; CHECK: {{%\w+}} = OpISub %ulong [[param_1]] {{%\w+}}
+; CHECK: {{%\w+}} = OpUConvert %ulong [[param_2]]
+; CHECK: {{%\w+}} = OpIAdd %ulong {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 %uint_0
+; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}}
+; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
+; CHECK: {{%\w+}} = OpISub %uint {{%\w+}} %uint_1
+; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} {{%\w+}}
+; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}}
+; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}}
+; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}}
+; CHECK: OpReturnValue {{%\w+}}
+; CHECK: OpFunctionEnd
+)";
+// clang-format on
+
using InstBuffAddrTest = PassTest<::testing::Test>;
TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferStore) {
@@ -49,13 +191,16 @@ TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferStore) {
// u_info.data.b = 0xca7;
// }
- const std::string defs_before =
- R"(OpCapability Shader
+ const std::string defs = R"(
+OpCapability Shader
OpCapability PhysicalStorageBufferAddresses
+; CHECK: OpCapability Int64
OpExtension "SPV_EXT_physical_storage_buffer"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint GLCompute %main "main"
+; CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpSourceExtension "GL_EXT_buffer_reference"
@@ -67,54 +212,10 @@ OpName %bufStruct "bufStruct"
OpMemberName %bufStruct 0 "a"
OpMemberName %bufStruct 1 "b"
OpName %u_info "u_info"
-OpMemberDecorate %ufoo 0 Offset 0
-OpMemberDecorate %ufoo 1 Offset 8
-OpDecorate %ufoo Block
-OpDecorate %_arr_int_uint_2 ArrayStride 16
-OpMemberDecorate %bufStruct 0 Offset 0
-OpMemberDecorate %bufStruct 1 Offset 32
-OpDecorate %bufStruct Block
-OpDecorate %u_info DescriptorSet 0
-OpDecorate %u_info Binding 0
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer
-%uint = OpTypeInt 32 0
-%ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_bufStruct %uint
-%int = OpTypeInt 32 1
-%uint_2 = OpConstant %uint 2
-%_arr_int_uint_2 = OpTypeArray %int %uint_2
-%bufStruct = OpTypeStruct %_arr_int_uint_2 %int
-%_ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer PhysicalStorageBuffer %bufStruct
-%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
-%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
-%int_0 = OpConstant %int 0
-%_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct
-%int_1 = OpConstant %int 1
-%int_3239 = OpConstant %int 3239
-%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
)";
- const std::string defs_after =
- R"(OpCapability Shader
-OpCapability PhysicalStorageBufferAddresses
-OpCapability Int64
-OpExtension "SPV_EXT_physical_storage_buffer"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel PhysicalStorageBuffer64 GLSL450
-OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
-OpExecutionMode %main LocalSize 1 1 1
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_buffer_reference"
-OpName %main "main"
-OpName %ufoo "ufoo"
-OpMemberName %ufoo 0 "data"
-OpMemberName %ufoo 1 "offset"
-OpName %bufStruct "bufStruct"
-OpMemberName %bufStruct 0 "a"
-OpMemberName %bufStruct 1 "b"
-OpName %u_info "u_info"
+ // clang-format off
+ const std::string decorates = R"(
OpMemberDecorate %ufoo 0 Offset 0
OpMemberDecorate %ufoo 1 Offset 8
OpDecorate %ufoo Block
@@ -124,20 +225,16 @@ OpMemberDecorate %bufStruct 1 Offset 32
OpDecorate %bufStruct Block
OpDecorate %u_info DescriptorSet 0
OpDecorate %u_info Binding 0
-OpDecorate %_runtimearr_ulong ArrayStride 8
-OpDecorate %_struct_39 Block
-OpMemberDecorate %_struct_39 0 Offset 0
-OpDecorate %41 DescriptorSet 7
-OpDecorate %41 Binding 2
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_77 Block
-OpMemberDecorate %_struct_77 0 Offset 0
-OpMemberDecorate %_struct_77 1 Offset 4
-OpDecorate %79 DescriptorSet 7
-OpDecorate %79 Binding 0
-OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8
+)" + kInputDecorations + R"(
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+)";
+
+ const std::string globals = R"(
%void = OpTypeVoid
-%8 = OpTypeFunction %void
+%3 = OpTypeFunction %void
OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer
%uint = OpTypeInt 32 0
%ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_bufStruct %uint
@@ -153,164 +250,71 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer
%int_1 = OpConstant %int 1
%int_3239 = OpConstant %int 3239
%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
-%ulong = OpTypeInt 64 0
-%uint_4 = OpConstant %uint 4
-%bool = OpTypeBool
-%28 = OpTypeFunction %bool %ulong %uint
-%uint_1 = OpConstant %uint 1
-%_runtimearr_ulong = OpTypeRuntimeArray %ulong
-%_struct_39 = OpTypeStruct %_runtimearr_ulong
-%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
-%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
-%_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong
-%uint_0 = OpConstant %uint 0
-%uint_32 = OpConstant %uint 32
-%70 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_77 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_77 = OpTypePointer StorageBuffer %_struct_77
-%79 = OpVariable %_ptr_StorageBuffer__struct_77 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_10 = OpConstant %uint 10
-%uint_23 = OpConstant %uint 23
-%uint_5 = OpConstant %uint 5
-%uint_3 = OpConstant %uint 3
-%v3uint = OpTypeVector %uint 3
-%_ptr_Input_v3uint = OpTypePointer Input %v3uint
-%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_8 = OpConstant %uint 8
-%uint_9 = OpConstant %uint 9
-%uint_48 = OpConstant %uint 48
+; CHECK: %ulong = OpTypeInt 64 0
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %bool = OpTypeBool
+; CHECK: %28 = OpTypeFunction %bool %ulong %uint
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %_runtimearr_ulong = OpTypeRuntimeArray %ulong
+)" + kInputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong
+; CHECK: %uint_0 = OpConstant %uint 0
+; CHECK: %uint_32 = OpConstant %uint 32
+; CHECK: %70 = OpTypeFunction %void %uint %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+)" + kOutputGlobals + R"(
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %uint_10 = OpConstant %uint 10
+; CHECK: %uint_23 = OpConstant %uint 23
+; CHECK: %uint_5 = OpConstant %uint 5
+; CHECK: %uint_3 = OpConstant %uint 3
+; CHECK: %v3uint = OpTypeVector %uint 3
+; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
+; CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
+; CHECK: %uint_6 = OpConstant %uint 6
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_8 = OpConstant %uint 8
+; CHECK: %uint_9 = OpConstant %uint 9
+; CHECK: %uint_48 = OpConstant %uint 48
)";
+// clang-format off
- const std::string func_before =
- R"(%main = OpFunction %void None %3
+ const std::string main_func = R"(
+%main = OpFunction %void None %3
%5 = OpLabel
%17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
%18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17
%22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1
+; CHECK-NOT: %17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
+; CHECK-NOT: %18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17
+; CHECK-NOT: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1
+; CHECK: %20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
+; CHECK: %21 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %20
+; CHECK: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %21 %int_1
+; CHECK: %24 = OpConvertPtrToU %ulong %22
+; CHECK: %61 = OpFunctionCall %bool %inst_buff_addr_search_and_test %24 %uint_4
+; CHECK: OpSelectionMerge %62 None
+; CHECK: OpBranchConditional %61 %63 %64
+; CHECK: %63 = OpLabel
OpStore %22 %int_3239 Aligned 16
+; CHECK: OpStore %22 %int_3239 Aligned 16
+; CHECK: OpBranch %62
+; CHECK: %64 = OpLabel
+; CHECK: %65 = OpUConvert %uint %24
+; CHECK: %67 = OpShiftRightLogical %ulong %24 %uint_32
+; CHECK: %68 = OpUConvert %uint %67
+; CHECK: %124 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_48 %uint_2 %65 %68
+; CHECK: OpBranch %62
+; CHECK: %62 = OpLabel
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %8
-%19 = OpLabel
-%20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
-%21 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %20
-%22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %21 %int_1
-%24 = OpConvertPtrToU %ulong %22
-%61 = OpFunctionCall %bool %26 %24 %uint_4
-OpSelectionMerge %62 None
-OpBranchConditional %61 %63 %64
-%63 = OpLabel
-OpStore %22 %int_3239 Aligned 16
-OpBranch %62
-%64 = OpLabel
-%65 = OpUConvert %uint %24
-%67 = OpShiftRightLogical %ulong %24 %uint_32
-%68 = OpUConvert %uint %67
-%124 = OpFunctionCall %void %69 %uint_48 %uint_2 %65 %68
-OpBranch %62
-%62 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%26 = OpFunction %bool None %28
-%29 = OpFunctionParameter %ulong
-%30 = OpFunctionParameter %uint
-%31 = OpLabel
-OpBranch %32
-%32 = OpLabel
-%34 = OpPhi %uint %uint_1 %31 %35 %33
-OpLoopMerge %37 %33 None
-OpBranch %33
-%33 = OpLabel
-%35 = OpIAdd %uint %34 %uint_1
-%44 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %35
-%45 = OpLoad %ulong %44
-%46 = OpUGreaterThan %bool %45 %29
-OpBranchConditional %46 %37 %32
-%37 = OpLabel
-%47 = OpISub %uint %35 %uint_1
-%48 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %47
-%49 = OpLoad %ulong %48
-%50 = OpISub %ulong %29 %49
-%51 = OpUConvert %ulong %30
-%52 = OpIAdd %ulong %50 %51
-%53 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %uint_0
-%54 = OpLoad %ulong %53
-%55 = OpUConvert %uint %54
-%56 = OpISub %uint %47 %uint_1
-%57 = OpIAdd %uint %56 %55
-%58 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %57
-%59 = OpLoad %ulong %58
-%60 = OpULessThanEqual %bool %52 %59
-OpReturnValue %60
-OpFunctionEnd
-%69 = OpFunction %void None %70
-%71 = OpFunctionParameter %uint
-%72 = OpFunctionParameter %uint
-%73 = OpFunctionParameter %uint
-%74 = OpFunctionParameter %uint
-%75 = OpLabel
-%81 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_0
-%83 = OpAtomicIAdd %uint %81 %uint_4 %uint_0 %uint_10
-%84 = OpIAdd %uint %83 %uint_10
-%85 = OpArrayLength %uint %79 1
-%86 = OpULessThanEqual %bool %84 %85
-OpSelectionMerge %87 None
-OpBranchConditional %86 %88 %87
-%88 = OpLabel
-%89 = OpIAdd %uint %83 %uint_0
-%90 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %89
-OpStore %90 %uint_10
-%92 = OpIAdd %uint %83 %uint_1
-%93 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %92
-OpStore %93 %uint_23
-%94 = OpIAdd %uint %83 %uint_2
-%95 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %94
-OpStore %95 %71
-%98 = OpIAdd %uint %83 %uint_3
-%99 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %98
-OpStore %99 %uint_5
-%103 = OpLoad %v3uint %gl_GlobalInvocationID
-%104 = OpCompositeExtract %uint %103 0
-%105 = OpCompositeExtract %uint %103 1
-%106 = OpCompositeExtract %uint %103 2
-%107 = OpIAdd %uint %83 %uint_4
-%108 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %107
-OpStore %108 %104
-%109 = OpIAdd %uint %83 %uint_5
-%110 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %109
-OpStore %110 %105
-%112 = OpIAdd %uint %83 %uint_6
-%113 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %112
-OpStore %113 %106
-%115 = OpIAdd %uint %83 %uint_7
-%116 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %115
-OpStore %116 %72
-%118 = OpIAdd %uint %83 %uint_8
-%119 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %118
-OpStore %119 %73
-%121 = OpIAdd %uint %83 %uint_9
-%122 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %121
-OpStore %122 %74
-OpBranch %87
-%87 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
+ const std::string output_funcs = kSearchAndTest + kStreamWrite4Compute;
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBuffAddrCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u);
+ SinglePassRunAndMatch<InstBuffAddrCheckPass>(
+ defs + decorates + globals + main_func + output_funcs, true, 7u, 23u);
}
TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) {
@@ -337,9 +341,10 @@ TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) {
// b.x = 531;
// }
- const std::string defs_before =
- R"(OpCapability Shader
+ const std::string defs = R"(
+OpCapability Shader
OpCapability PhysicalStorageBufferAddresses
+; CHECK: OpCapability Int64
OpExtension "SPV_EXT_physical_storage_buffer"
OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
@@ -349,55 +354,17 @@ OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpSourceExtension "GL_EXT_buffer_reference"
OpName %main "main"
+; CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
OpName %blockType "blockType"
OpMemberName %blockType 0 "x"
OpMemberName %blockType 1 "next"
OpName %rootBlock "rootBlock"
OpMemberName %rootBlock 0 "root"
OpName %r "r"
-OpMemberDecorate %blockType 0 Offset 0
-OpMemberDecorate %blockType 1 Offset 8
-OpDecorate %blockType Block
-OpMemberDecorate %rootBlock 0 Offset 0
-OpDecorate %rootBlock Block
-OpDecorate %r DescriptorSet 0
-OpDecorate %r Binding 0
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer
-%int = OpTypeInt 32 1
-%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBuffer_blockType
-%_ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %blockType
-%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBuffer_blockType
-%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock
-%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer
-%int_0 = OpConstant %int 0
-%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_blockType
-%int_1 = OpConstant %int 1
-%_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType
-%int_531 = OpConstant %int 531
-%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
)";
- const std::string defs_after =
- R"(OpCapability Shader
-OpCapability PhysicalStorageBufferAddresses
-OpCapability Int64
-OpExtension "SPV_EXT_physical_storage_buffer"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel PhysicalStorageBuffer64 GLSL450
-OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
-OpExecutionMode %main LocalSize 1 1 1
-OpSource GLSL 450
-OpSourceExtension "GL_EXT_buffer_reference"
-OpName %main "main"
-OpName %blockType "blockType"
-OpMemberName %blockType 0 "x"
-OpMemberName %blockType 1 "next"
-OpName %rootBlock "rootBlock"
-OpMemberName %rootBlock 0 "root"
-OpName %r "r"
+// clang-format off
+ const std::string decorates = R"(
OpMemberDecorate %blockType 0 Offset 0
OpMemberDecorate %blockType 1 Offset 8
OpDecorate %blockType Block
@@ -405,18 +372,15 @@ OpMemberDecorate %rootBlock 0 Offset 0
OpDecorate %rootBlock Block
OpDecorate %r DescriptorSet 0
OpDecorate %r Binding 0
-OpDecorate %_runtimearr_ulong ArrayStride 8
-OpDecorate %_struct_45 Block
-OpMemberDecorate %_struct_45 0 Offset 0
-OpDecorate %47 DescriptorSet 7
-OpDecorate %47 Binding 2
-OpDecorate %_runtimearr_uint ArrayStride 4
-OpDecorate %_struct_84 Block
-OpMemberDecorate %_struct_84 0 Offset 0
-OpMemberDecorate %_struct_84 1 Offset 4
-OpDecorate %86 DescriptorSet 7
-OpDecorate %86 Binding 0
-OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8
+)" + kInputDecorations + R"(
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+)" + kOutputDecorations + R"(
+; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+)";
+ // clang-format on
+
+ const std::string globals = R"(
%void = OpTypeVoid
%3 = OpTypeFunction %void
OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer
@@ -432,44 +396,10 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer
%_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType
%int_531 = OpConstant %int 531
%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
-%uint = OpTypeInt 32 0
-%uint_2 = OpConstant %uint 2
-%ulong = OpTypeInt 64 0
-%uint_8 = OpConstant %uint 8
-%bool = OpTypeBool
-%34 = OpTypeFunction %bool %ulong %uint
-%uint_1 = OpConstant %uint 1
-%_runtimearr_ulong = OpTypeRuntimeArray %ulong
-%_struct_45 = OpTypeStruct %_runtimearr_ulong
-%_ptr_StorageBuffer__struct_45 = OpTypePointer StorageBuffer %_struct_45
-%47 = OpVariable %_ptr_StorageBuffer__struct_45 StorageBuffer
-%_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong
-%uint_0 = OpConstant %uint 0
-%uint_32 = OpConstant %uint 32
-%77 = OpTypeFunction %void %uint %uint %uint %uint
-%_runtimearr_uint = OpTypeRuntimeArray %uint
-%_struct_84 = OpTypeStruct %uint %_runtimearr_uint
-%_ptr_StorageBuffer__struct_84 = OpTypePointer StorageBuffer %_struct_84
-%86 = OpVariable %_ptr_StorageBuffer__struct_84 StorageBuffer
-%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
-%uint_10 = OpConstant %uint 10
-%uint_4 = OpConstant %uint 4
-%uint_23 = OpConstant %uint 23
-%uint_5 = OpConstant %uint 5
-%uint_3 = OpConstant %uint 3
-%v3uint = OpTypeVector %uint 3
-%_ptr_Input_v3uint = OpTypePointer Input %v3uint
-%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
-%uint_6 = OpConstant %uint 6
-%uint_7 = OpConstant %uint 7
-%uint_9 = OpConstant %uint 9
-%uint_44 = OpConstant %uint 44
-%132 = OpConstantNull %ulong
-%uint_46 = OpConstant %uint 46
-)";
+)" + kInputGlobals + kOutputGlobals;
- const std::string func_before =
- R"(%main = OpFunction %void None %3
+ const std::string main_func = R"(
+%main = OpFunction %void None %3
%5 = OpLabel
%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType %r %int_0
%17 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %16
@@ -477,142 +407,48 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer
%22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
%26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0
OpStore %26 %int_531 Aligned 16
+; CHECK-NOT: %22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
+; CHECK-NOT: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0
+; CHECK: %30 = OpConvertPtrToU %ulong %21
+; CHECK: %67 = OpFunctionCall %bool %inst_buff_addr_search_and_test %30 %uint_8
+; CHECK: OpSelectionMerge %68 None
+; CHECK: OpBranchConditional %67 %69 %70
+; CHECK: %69 = OpLabel
+; CHECK: %71 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
+; CHECK: OpBranch %68
+; CHECK: %70 = OpLabel
+; CHECK: %72 = OpUConvert %uint %30
+; CHECK: %74 = OpShiftRightLogical %ulong %30 %uint_32
+; CHECK: %75 = OpUConvert %uint %74
+; CHECK: %131 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_44 %uint_2 %72 %75
+; CHECK: %133 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType %132
+; CHECK: OpBranch %68
+; CHECK: %68 = OpLabel
+; CHECK: %134 = OpPhi %_ptr_PhysicalStorageBuffer_blockType %71 %69 %133 %70
+; CHECK: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %134 %int_0
+; CHECK: %135 = OpConvertPtrToU %ulong %26
+; CHECK: %136 = OpFunctionCall %bool %inst_buff_addr_search_and_test %135 %uint_4
+; CHECK: OpSelectionMerge %137 None
+; CHECK: OpBranchConditional %136 %138 %139
+; CHECK: %138 = OpLabel
+; CHECK: OpStore %26 %int_531 Aligned 16
+; CHECK: OpBranch %137
+; CHECK: %139 = OpLabel
+; CHECK: %140 = OpUConvert %uint %135
+; CHECK: %141 = OpShiftRightLogical %ulong %135 %uint_32
+; CHECK: %142 = OpUConvert %uint %141
+; CHECK: %144 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_46 %uint_2 %140 %142
+; CHECK: OpBranch %137
+; CHECK: %137 = OpLabel
OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %3
-%5 = OpLabel
-%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType %r %int_0
-%17 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %16
-%21 = OpAccessChain %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType %17 %int_1
-%30 = OpConvertPtrToU %ulong %21
-%67 = OpFunctionCall %bool %32 %30 %uint_8
-OpSelectionMerge %68 None
-OpBranchConditional %67 %69 %70
-%69 = OpLabel
-%71 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
-OpBranch %68
-%70 = OpLabel
-%72 = OpUConvert %uint %30
-%74 = OpShiftRightLogical %ulong %30 %uint_32
-%75 = OpUConvert %uint %74
-%131 = OpFunctionCall %void %76 %uint_44 %uint_2 %72 %75
-%133 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType %132
-OpBranch %68
-%68 = OpLabel
-%134 = OpPhi %_ptr_PhysicalStorageBuffer_blockType %71 %69 %133 %70
-%26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %134 %int_0
-%135 = OpConvertPtrToU %ulong %26
-%136 = OpFunctionCall %bool %32 %135 %uint_4
-OpSelectionMerge %137 None
-OpBranchConditional %136 %138 %139
-%138 = OpLabel
-OpStore %26 %int_531 Aligned 16
-OpBranch %137
-%139 = OpLabel
-%140 = OpUConvert %uint %135
-%141 = OpShiftRightLogical %ulong %135 %uint_32
-%142 = OpUConvert %uint %141
-%144 = OpFunctionCall %void %76 %uint_46 %uint_2 %140 %142
-OpBranch %137
-%137 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- const std::string new_funcs =
- R"(%32 = OpFunction %bool None %34
-%35 = OpFunctionParameter %ulong
-%36 = OpFunctionParameter %uint
-%37 = OpLabel
-OpBranch %38
-%38 = OpLabel
-%40 = OpPhi %uint %uint_1 %37 %41 %39
-OpLoopMerge %43 %39 None
-OpBranch %39
-%39 = OpLabel
-%41 = OpIAdd %uint %40 %uint_1
-%50 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %41
-%51 = OpLoad %ulong %50
-%52 = OpUGreaterThan %bool %51 %35
-OpBranchConditional %52 %43 %38
-%43 = OpLabel
-%53 = OpISub %uint %41 %uint_1
-%54 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %53
-%55 = OpLoad %ulong %54
-%56 = OpISub %ulong %35 %55
-%57 = OpUConvert %ulong %36
-%58 = OpIAdd %ulong %56 %57
-%59 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %uint_0
-%60 = OpLoad %ulong %59
-%61 = OpUConvert %uint %60
-%62 = OpISub %uint %53 %uint_1
-%63 = OpIAdd %uint %62 %61
-%64 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %63
-%65 = OpLoad %ulong %64
-%66 = OpULessThanEqual %bool %58 %65
-OpReturnValue %66
-OpFunctionEnd
-%76 = OpFunction %void None %77
-%78 = OpFunctionParameter %uint
-%79 = OpFunctionParameter %uint
-%80 = OpFunctionParameter %uint
-%81 = OpFunctionParameter %uint
-%82 = OpLabel
-%88 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_0
-%91 = OpAtomicIAdd %uint %88 %uint_4 %uint_0 %uint_10
-%92 = OpIAdd %uint %91 %uint_10
-%93 = OpArrayLength %uint %86 1
-%94 = OpULessThanEqual %bool %92 %93
-OpSelectionMerge %95 None
-OpBranchConditional %94 %96 %95
-%96 = OpLabel
-%97 = OpIAdd %uint %91 %uint_0
-%98 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %97
-OpStore %98 %uint_10
-%100 = OpIAdd %uint %91 %uint_1
-%101 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %100
-OpStore %101 %uint_23
-%102 = OpIAdd %uint %91 %uint_2
-%103 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %102
-OpStore %103 %78
-%106 = OpIAdd %uint %91 %uint_3
-%107 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %106
-OpStore %107 %uint_5
-%111 = OpLoad %v3uint %gl_GlobalInvocationID
-%112 = OpCompositeExtract %uint %111 0
-%113 = OpCompositeExtract %uint %111 1
-%114 = OpCompositeExtract %uint %111 2
-%115 = OpIAdd %uint %91 %uint_4
-%116 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %115
-OpStore %116 %112
-%117 = OpIAdd %uint %91 %uint_5
-%118 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %117
-OpStore %118 %113
-%120 = OpIAdd %uint %91 %uint_6
-%121 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %120
-OpStore %121 %114
-%123 = OpIAdd %uint %91 %uint_7
-%124 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %123
-OpStore %124 %79
-%125 = OpIAdd %uint %91 %uint_8
-%126 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %125
-OpStore %126 %80
-%128 = OpIAdd %uint %91 %uint_9
-%129 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %128
-OpStore %129 %81
-OpBranch %95
-%95 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
+ const std::string output_funcs = kSearchAndTest + kStreamWrite4Compute;
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<InstBuffAddrCheckPass>(
- defs_before + func_before, defs_after + func_after + new_funcs, true,
- true, 7u, 23u);
+ SinglePassRunAndMatch<InstBuffAddrCheckPass>(
+ defs + decorates + globals + main_func + output_funcs, true, 7u, 23u);
}
TEST_F(InstBuffAddrTest, StructLoad) {
@@ -643,7 +479,7 @@ OpCapability PhysicalStorageBufferAddresses
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Fragment %main "main"
-; CHECK: OpEntryPoint Fragment %main "main" %60 %99 %gl_FragCoord
+; CHECK: OpEntryPoint Fragment %main "main" %inst_buff_addr_input_buffer %inst_buff_addr_output_buffer %gl_FragCoord
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpSourceExtension "GL_ARB_gpu_shader_int64"
@@ -657,27 +493,19 @@ OpName %TestBuffer "TestBuffer"
OpMemberName %TestBuffer 0 "test"
)";
- const std::string decorates =
- R"(
+ // clang-format off
+ const std::string decorates = R"(
OpMemberDecorate %Test_0 0 Offset 0
OpMemberDecorate %TestBuffer 0 Offset 0
OpDecorate %TestBuffer Block
; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8
-; CHECK: OpDecorate %_struct_58 Block
-; CHECK: OpMemberDecorate %_struct_58 0 Offset 0
-; CHECK: OpDecorate %60 DescriptorSet 7
-; CHECK: OpDecorate %60 Binding 2
+)" + kInputDecorations + R"(
; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-; CHECK: OpDecorate %_struct_97 Block
-; CHECK: OpMemberDecorate %_struct_97 0 Offset 0
-; CHECK: OpMemberDecorate %_struct_97 1 Offset 4
-; CHECK: OpDecorate %99 DescriptorSet 7
-; CHECK: OpDecorate %99 Binding 0
+)" + kOutputDecorations + R"(
; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
)";
- const std::string globals =
- R"(
+ const std::string globals = R"(
%void = OpTypeVoid
%3 = OpTypeFunction %void
%ulong = OpTypeInt 64 0
@@ -692,15 +520,14 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_TestBuffer PhysicalStorageBuffe
%_ptr_PhysicalStorageBuffer_Test_0 = OpTypePointer PhysicalStorageBuffer %Test_0
%ulong_18446744073172680704 = OpConstant %ulong 18446744073172680704
; CHECK: %47 = OpTypeFunction %bool %ulong %uint
-; CHECK: %_struct_58 = OpTypeStruct %_runtimearr_ulong
-; CHECK: %60 = OpVariable %_ptr_StorageBuffer__struct_58 StorageBuffer
+)" + kInputGlobals + R"(
; CHECK: %90 = OpTypeFunction %void %uint %uint %uint %uint
-; CHECK: %_struct_97 = OpTypeStruct %uint %_runtimearr_uint
-; CHECK: %99 = OpVariable %_ptr_StorageBuffer__struct_97 StorageBuffer
+)" + kOutputGlobals + R"(
; CHECK: %143 = OpConstantNull %Test_0
)";
+ // clang-format on
- const std::string main =
+ const std::string main_func =
R"(
%main = OpFunction %void None %3
%5 = OpLabel
@@ -709,7 +536,7 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_TestBuffer PhysicalStorageBuffe
%39 = OpLoad %Test_0 %38 Aligned 16
; CHECK-NOT: %39 = OpLoad %Test_0 %38 Aligned 16
; CHECK: %43 = OpConvertPtrToU %ulong %38
-; CHECK: %80 = OpFunctionCall %bool %45 %43 %uint_4
+; CHECK: %80 = OpFunctionCall %bool %inst_buff_addr_search_and_test %43 %uint_4
; CHECK: OpSelectionMerge %81 None
; CHECK: OpBranchConditional %80 %82 %83
; CHECK: %82 = OpLabel
@@ -719,7 +546,7 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_TestBuffer PhysicalStorageBuffe
; CHECK: %85 = OpUConvert %uint %43
; CHECK: %87 = OpShiftRightLogical %ulong %43 %uint_32
; CHECK: %88 = OpUConvert %uint %87
-; CHECK: %142 = OpFunctionCall %void %89 %uint_37 %uint_2 %85 %88
+; CHECK: %142 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_37 %uint_2 %85 %88
; CHECK: OpBranch %81
; CHECK: %81 = OpLabel
; CHECK: %144 = OpPhi %Test_0 %84 %82 %143 %83
@@ -730,95 +557,12 @@ OpReturn
OpFunctionEnd
)";
- const std::string output_funcs =
- R"(
-; CHECK: %45 = OpFunction %bool None %47
-; CHECK: %48 = OpFunctionParameter %ulong
-; CHECK: %49 = OpFunctionParameter %uint
-; CHECK: %50 = OpLabel
-; CHECK: OpBranch %51
-; CHECK: %51 = OpLabel
-; CHECK: %53 = OpPhi %uint %uint_1 %50 %54 %52
-; CHECK: OpLoopMerge %56 %52 None
-; CHECK: OpBranch %52
-; CHECK: %52 = OpLabel
-; CHECK: %54 = OpIAdd %uint %53 %uint_1
-; CHECK: %63 = OpAccessChain %_ptr_StorageBuffer_ulong %60 %uint_0 %54
-; CHECK: %64 = OpLoad %ulong %63
-; CHECK: %65 = OpUGreaterThan %bool %64 %48
-; CHECK: OpBranchConditional %65 %56 %51
-; CHECK: %56 = OpLabel
-; CHECK: %66 = OpISub %uint %54 %uint_1
-; CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_ulong %60 %uint_0 %66
-; CHECK: %68 = OpLoad %ulong %67
-; CHECK: %69 = OpISub %ulong %48 %68
-; CHECK: %70 = OpUConvert %ulong %49
-; CHECK: %71 = OpIAdd %ulong %69 %70
-; CHECK: %72 = OpAccessChain %_ptr_StorageBuffer_ulong %60 %uint_0 %uint_0
-; CHECK: %73 = OpLoad %ulong %72
-; CHECK: %74 = OpUConvert %uint %73
-; CHECK: %75 = OpISub %uint %66 %uint_1
-; CHECK: %76 = OpIAdd %uint %75 %74
-; CHECK: %77 = OpAccessChain %_ptr_StorageBuffer_ulong %60 %uint_0 %76
-; CHECK: %78 = OpLoad %ulong %77
-; CHECK: %79 = OpULessThanEqual %bool %71 %78
-; CHECK: OpReturnValue %79
-; CHECK: OpFunctionEnd
-; CHECK: %89 = OpFunction %void None %90
-; CHECK: %91 = OpFunctionParameter %uint
-; CHECK: %92 = OpFunctionParameter %uint
-; CHECK: %93 = OpFunctionParameter %uint
-; CHECK: %94 = OpFunctionParameter %uint
-; CHECK: %95 = OpLabel
-; CHECK: %101 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_0
-; CHECK: %103 = OpAtomicIAdd %uint %101 %uint_4 %uint_0 %uint_10
-; CHECK: %104 = OpIAdd %uint %103 %uint_10
-; CHECK: %105 = OpArrayLength %uint %99 1
-; CHECK: %106 = OpULessThanEqual %bool %104 %105
-; CHECK: OpSelectionMerge %107 None
-; CHECK: OpBranchConditional %106 %108 %107
-; CHECK: %108 = OpLabel
-; CHECK: %109 = OpIAdd %uint %103 %uint_0
-; CHECK: %110 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %109
-; CHECK: OpStore %110 %uint_10
-; CHECK: %112 = OpIAdd %uint %103 %uint_1
-; CHECK: %113 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %112
-; CHECK: OpStore %113 %uint_23
-; CHECK: %114 = OpIAdd %uint %103 %uint_2
-; CHECK: %115 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %114
-; CHECK: OpStore %115 %91
-; CHECK: %117 = OpIAdd %uint %103 %uint_3
-; CHECK: %118 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %117
-; CHECK: OpStore %118 %uint_4
-; CHECK: %122 = OpLoad %v4float %gl_FragCoord
-; CHECK: %124 = OpBitcast %v4uint %122
-; CHECK: %125 = OpCompositeExtract %uint %124 0
-; CHECK: %126 = OpIAdd %uint %103 %uint_4
-; CHECK: %127 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %126
-; CHECK: OpStore %127 %125
-; CHECK: %128 = OpCompositeExtract %uint %124 1
-; CHECK: %130 = OpIAdd %uint %103 %uint_5
-; CHECK: %131 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %130
-; CHECK: OpStore %131 %128
-; CHECK: %133 = OpIAdd %uint %103 %uint_7
-; CHECK: %134 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %133
-; CHECK: OpStore %134 %92
-; CHECK: %136 = OpIAdd %uint %103 %uint_8
-; CHECK: %137 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %136
-; CHECK: OpStore %137 %93
-; CHECK: %139 = OpIAdd %uint %103 %uint_9
-; CHECK: %140 = OpAccessChain %_ptr_StorageBuffer_uint %99 %uint_1 %139
-; CHECK: OpStore %140 %94
-; CHECK: OpBranch %107
-; CHECK: %107 = OpLabel
-; CHECK: OpReturn
-; CHECK: OpFunctionEnd
-)";
+ const std::string output_funcs = kSearchAndTest + kStreamWrite4Frag;
SetTargetEnv(SPV_ENV_VULKAN_1_2);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<InstBuffAddrCheckPass>(
- defs + decorates + globals + main + output_funcs, true);
+ defs + decorates + globals + main_func + output_funcs, true);
}
} // namespace
diff --git a/test/opt/inst_debug_printf_test.cpp b/test/opt/inst_debug_printf_test.cpp
index c5fd6799..57e50440 100644
--- a/test/opt/inst_debug_printf_test.cpp
+++ b/test/opt/inst_debug_printf_test.cpp
@@ -1,5 +1,5 @@
-// Copyright (c) 2020 Valve Corporation
-// Copyright (c) 2020 LunarG Inc.
+// Copyright (c) 2020-2022 Valve Corporation
+// Copyright (c) 2020-2022 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -26,6 +26,20 @@ namespace spvtools {
namespace opt {
namespace {
+static const std::string kOutputDecorations = R"(
+; CHECK: OpDecorate [[output_buffer_type:%inst_printf_OutputBuffer]] Block
+; CHECK: OpMemberDecorate [[output_buffer_type]] 0 Offset 0
+; CHECK: OpMemberDecorate [[output_buffer_type]] 1 Offset 4
+; CHECK: OpDecorate [[output_buffer_var:%\w+]] DescriptorSet 7
+; CHECK: OpDecorate [[output_buffer_var]] Binding 3
+)";
+
+static const std::string kOutputGlobals = R"(
+; CHECK: [[output_buffer_type]] = OpTypeStruct %uint %_runtimearr_uint
+; CHECK: [[output_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[output_buffer_type]]
+; CHECK: [[output_buffer_var]] = OpVariable [[output_ptr_type]] StorageBuffer
+)";
+
using InstDebugPrintfTest = PassTest<::testing::Test>;
TEST_F(InstDebugPrintfTest, V4Float32) {
@@ -65,6 +79,7 @@ OpExecutionMode %2 OriginUpperLeft
%5 = OpString "Color is %vn"
)";
+ // clang-format off
const std::string decorates =
R"(OpDecorate %6 DescriptorSet 0
OpDecorate %6 Binding 1
@@ -73,11 +88,7 @@ OpDecorate %7 Binding 0
OpDecorate %3 Location 0
OpDecorate %4 Location 0
; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
-; CHECK: OpDecorate %_struct_47 Block
-; CHECK: OpMemberDecorate %_struct_47 0 Offset 0
-; CHECK: OpMemberDecorate %_struct_47 1 Offset 4
-; CHECK: OpDecorate %49 DescriptorSet 7
-; CHECK: OpDecorate %49 Binding 3
+)" + kOutputDecorations + R"(
; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
)";
@@ -101,15 +112,14 @@ OpDecorate %4 Location 0
; CHECK: %uint = OpTypeInt 32 0
; CHECK: %38 = OpTypeFunction %void %uint %uint %uint %uint %uint %uint
; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
-; CHECK: %_struct_47 = OpTypeStruct %uint %_runtimearr_uint
-; CHECK: %_ptr_StorageBuffer__struct_47 = OpTypePointer StorageBuffer %_struct_47
-; CHECK: %49 = OpVariable %_ptr_StorageBuffer__struct_47 StorageBuffer
+)" + kOutputGlobals + R"(
; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
; CHECK: %bool = OpTypeBool
; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
; CHECK: %v4uint = OpTypeVector %uint 4
)";
+ // clang-format on
const std::string main =
R"(%2 = OpFunction %void None %9
@@ -129,7 +139,7 @@ OpDecorate %4 Location 0
; CHECK: %34 = OpBitcast %uint %33
; CHECK: %35 = OpCompositeExtract %float %25 3
; CHECK: %36 = OpBitcast %uint %35
-; CHECK: %101 = OpFunctionCall %void %37 %uint_36 %uint_5 %30 %32 %34 %36
+; CHECK: %101 = OpFunctionCall %void %inst_printf_stream_write_6 %uint_36 %uint_5 %30 %32 %34 %36
; CHECK: OpBranch %102
; CHECK: %102 = OpLabel
OpStore %4 %25
@@ -137,8 +147,8 @@ OpReturn
OpFunctionEnd
)";
- const std::string output_func =
- R"(; CHECK: %37 = OpFunction %void None %38
+ const std::string output_func = R"(
+; CHECK: %inst_printf_stream_write_6 = OpFunction %void None %38
; CHECK: %39 = OpFunctionParameter %uint
; CHECK: %40 = OpFunctionParameter %uint
; CHECK: %41 = OpFunctionParameter %uint
@@ -146,50 +156,50 @@ OpFunctionEnd
; CHECK: %43 = OpFunctionParameter %uint
; CHECK: %44 = OpFunctionParameter %uint
; CHECK: %45 = OpLabel
-; CHECK: %52 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_0
+; CHECK: %52 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_0
; CHECK: %55 = OpAtomicIAdd %uint %52 %uint_4 %uint_0 %uint_12
; CHECK: %56 = OpIAdd %uint %55 %uint_12
-; CHECK: %57 = OpArrayLength %uint %49 1
+; CHECK: %57 = OpArrayLength %uint %inst_printf_output_buffer 1
; CHECK: %59 = OpULessThanEqual %bool %56 %57
; CHECK: OpSelectionMerge %60 None
; CHECK: OpBranchConditional %59 %61 %60
; CHECK: %61 = OpLabel
; CHECK: %62 = OpIAdd %uint %55 %uint_0
-; CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %62
+; CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %62
; CHECK: OpStore %64 %uint_12
; CHECK: %66 = OpIAdd %uint %55 %uint_1
-; CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %66
+; CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %66
; CHECK: OpStore %67 %uint_23
; CHECK: %69 = OpIAdd %uint %55 %uint_2
-; CHECK: %70 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %69
+; CHECK: %70 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %69
; CHECK: OpStore %70 %39
; CHECK: %72 = OpIAdd %uint %55 %uint_3
-; CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %72
+; CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %72
; CHECK: OpStore %73 %uint_4
; CHECK: %76 = OpLoad %v4float %gl_FragCoord
; CHECK: %78 = OpBitcast %v4uint %76
; CHECK: %79 = OpCompositeExtract %uint %78 0
; CHECK: %80 = OpIAdd %uint %55 %uint_4
-; CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %80
+; CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %80
; CHECK: OpStore %81 %79
; CHECK: %82 = OpCompositeExtract %uint %78 1
; CHECK: %83 = OpIAdd %uint %55 %uint_5
-; CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %83
+; CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %83
; CHECK: OpStore %84 %82
; CHECK: %86 = OpIAdd %uint %55 %uint_7
-; CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %86
+; CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %86
; CHECK: OpStore %87 %40
; CHECK: %89 = OpIAdd %uint %55 %uint_8
-; CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %89
+; CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %89
; CHECK: OpStore %90 %41
; CHECK: %92 = OpIAdd %uint %55 %uint_9
-; CHECK: %93 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %92
+; CHECK: %93 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %92
; CHECK: OpStore %93 %42
; CHECK: %95 = OpIAdd %uint %55 %uint_10
-; CHECK: %96 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %95
+; CHECK: %96 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %95
; CHECK: OpStore %96 %43
; CHECK: %98 = OpIAdd %uint %55 %uint_11
-; CHECK: %99 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %98
+; CHECK: %99 = OpAccessChain %_ptr_StorageBuffer_uint %inst_printf_output_buffer %uint_1 %98
; CHECK: OpStore %99 %44
; CHECK: OpBranch %60
; CHECK: %60 = OpLabel
diff --git a/test/opt/instruction_test.cpp b/test/opt/instruction_test.cpp
index 2a48134d..dd749ab4 100644
--- a/test/opt/instruction_test.cpp
+++ b/test/opt/instruction_test.cpp
@@ -1525,6 +1525,45 @@ OpFunctionEnd
EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
}
+TEST_F(DescriptorTypeTest, GetShader100DebugOpcode) {
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+ %2 = OpString "ps.hlsl"
+ %3 = OpString "#line 1 \"ps.hlsl\""
+ %void = OpTypeVoid
+ %5 = OpExtInst %void %1 DebugExpression
+ %6 = OpExtInst %void %1 DebugSource %2 %3
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
+ Instruction* debug_expression = context->get_def_use_mgr()->GetDef(5);
+ EXPECT_EQ(debug_expression->GetShader100DebugOpcode(),
+ NonSemanticShaderDebugInfo100DebugExpression);
+ Instruction* debug_source = context->get_def_use_mgr()->GetDef(6);
+ EXPECT_EQ(debug_source->GetShader100DebugOpcode(),
+ NonSemanticShaderDebugInfo100DebugSource);
+
+ // Test that an opcode larger than the max will return Max. This instruction
+ // cannot be in the assembly above because the assembler expects the string
+ // for the opcode, so we cannot use an arbitrary number. However, a binary
+ // file could have an arbitrary number.
+ std::unique_ptr<Instruction> past_max(debug_expression->Clone(context.get()));
+ const uint32_t kExtInstOpcodeInIndex = 1;
+ uint32_t large_opcode = NonSemanticShaderDebugInfo100InstructionsMax + 2;
+ past_max->SetInOperand(kExtInstOpcodeInIndex, {large_opcode});
+ EXPECT_EQ(past_max->GetShader100DebugOpcode(),
+ NonSemanticShaderDebugInfo100InstructionsMax);
+
+ // Test that an opcode without a value in the enum, but less than Max returns
+ // the same value.
+ uint32_t opcode = NonSemanticShaderDebugInfo100InstructionsMax - 2;
+ past_max->SetInOperand(kExtInstOpcodeInIndex, {opcode});
+ EXPECT_EQ(past_max->GetShader100DebugOpcode(), opcode);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/interface_var_sroa_test.cpp b/test/opt/interface_var_sroa_test.cpp
new file mode 100644
index 00000000..77624587
--- /dev/null
+++ b/test/opt/interface_var_sroa_test.cpp
@@ -0,0 +1,410 @@
+// Copyright (c) 2022 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 <iostream>
+
+#include "gmock/gmock.h"
+#include "test/opt/assembly_builder.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using InterfaceVariableScalarReplacementTest = PassTest<::testing::Test>;
+
+TEST_F(InterfaceVariableScalarReplacementTest,
+ ReplaceInterfaceVarsWithScalars) {
+ const std::string spirv = R"(
+ OpCapability Shader
+ OpCapability Tessellation
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint TessellationControl %func "shader" %x %y %z %w %u %v
+
+; CHECK: OpName [[x:%\w+]] "x"
+; CHECK-NOT: OpName {{%\w+}} "x"
+; CHECK: OpName [[y:%\w+]] "y"
+; CHECK-NOT: OpName {{%\w+}} "y"
+; CHECK: OpName [[z0:%\w+]] "z"
+; CHECK: OpName [[z1:%\w+]] "z"
+; CHECK: OpName [[w0:%\w+]] "w"
+; CHECK: OpName [[w1:%\w+]] "w"
+; CHECK: OpName [[u0:%\w+]] "u"
+; CHECK: OpName [[u1:%\w+]] "u"
+; CHECK: OpName [[v0:%\w+]] "v"
+; CHECK: OpName [[v1:%\w+]] "v"
+; CHECK: OpName [[v2:%\w+]] "v"
+; CHECK: OpName [[v3:%\w+]] "v"
+; CHECK: OpName [[v4:%\w+]] "v"
+; CHECK: OpName [[v5:%\w+]] "v"
+ OpName %x "x"
+ OpName %y "y"
+ OpName %z "z"
+ OpName %w "w"
+ OpName %u "u"
+ OpName %v "v"
+
+; CHECK-DAG: OpDecorate [[x]] Location 2
+; CHECK-DAG: OpDecorate [[y]] Location 0
+; CHECK-DAG: OpDecorate [[z0]] Location 0
+; CHECK-DAG: OpDecorate [[z0]] Component 0
+; CHECK-DAG: OpDecorate [[z1]] Location 1
+; CHECK-DAG: OpDecorate [[z1]] Component 0
+; CHECK-DAG: OpDecorate [[z0]] Patch
+; CHECK-DAG: OpDecorate [[z1]] Patch
+; CHECK-DAG: OpDecorate [[w0]] Location 2
+; CHECK-DAG: OpDecorate [[w0]] Component 0
+; CHECK-DAG: OpDecorate [[w1]] Location 3
+; CHECK-DAG: OpDecorate [[w1]] Component 0
+; CHECK-DAG: OpDecorate [[w0]] Patch
+; CHECK-DAG: OpDecorate [[w1]] Patch
+; CHECK-DAG: OpDecorate [[u0]] Location 3
+; CHECK-DAG: OpDecorate [[u0]] Component 2
+; CHECK-DAG: OpDecorate [[u1]] Location 4
+; CHECK-DAG: OpDecorate [[u1]] Component 2
+; CHECK-DAG: OpDecorate [[v0]] Location 3
+; CHECK-DAG: OpDecorate [[v0]] Component 3
+; CHECK-DAG: OpDecorate [[v1]] Location 4
+; CHECK-DAG: OpDecorate [[v1]] Component 3
+; CHECK-DAG: OpDecorate [[v2]] Location 5
+; CHECK-DAG: OpDecorate [[v2]] Component 3
+; CHECK-DAG: OpDecorate [[v3]] Location 6
+; CHECK-DAG: OpDecorate [[v3]] Component 3
+; CHECK-DAG: OpDecorate [[v4]] Location 7
+; CHECK-DAG: OpDecorate [[v4]] Component 3
+; CHECK-DAG: OpDecorate [[v5]] Location 8
+; CHECK-DAG: OpDecorate [[v5]] Component 3
+ OpDecorate %z Patch
+ OpDecorate %w Patch
+ OpDecorate %z Location 0
+ OpDecorate %x Location 2
+ OpDecorate %v Location 3
+ OpDecorate %v Component 3
+ OpDecorate %y Location 0
+ OpDecorate %w Location 2
+ OpDecorate %u Location 3
+ OpDecorate %u Component 2
+
+ %uint = OpTypeInt 32 0
+ %uint_1 = OpConstant %uint 1
+ %uint_2 = OpConstant %uint 2
+ %uint_3 = OpConstant %uint 3
+ %uint_4 = OpConstant %uint 4
+%_arr_uint_uint_2 = OpTypeArray %uint %uint_2
+%_ptr_Output__arr_uint_uint_2 = OpTypePointer Output %_arr_uint_uint_2
+%_ptr_Input__arr_uint_uint_2 = OpTypePointer Input %_arr_uint_uint_2
+%_ptr_Input_uint = OpTypePointer Input %uint
+%_ptr_Output_uint = OpTypePointer Output %uint
+%_arr_arr_uint_uint_2_3 = OpTypeArray %_arr_uint_uint_2 %uint_3
+%_ptr_Input__arr_arr_uint_uint_2_3 = OpTypePointer Input %_arr_arr_uint_uint_2_3
+%_arr_arr_arr_uint_uint_2_3_4 = OpTypeArray %_arr_arr_uint_uint_2_3 %uint_4
+%_ptr_Output__arr_arr_arr_uint_uint_2_3_4 = OpTypePointer Output %_arr_arr_arr_uint_uint_2_3_4
+%_ptr_Output__arr_arr_uint_uint_2_3 = OpTypePointer Output %_arr_arr_uint_uint_2_3
+ %z = OpVariable %_ptr_Output__arr_uint_uint_2 Output
+ %x = OpVariable %_ptr_Output__arr_uint_uint_2 Output
+ %y = OpVariable %_ptr_Input__arr_uint_uint_2 Input
+ %w = OpVariable %_ptr_Input__arr_uint_uint_2 Input
+ %u = OpVariable %_ptr_Input__arr_arr_uint_uint_2_3 Input
+ %v = OpVariable %_ptr_Output__arr_arr_arr_uint_uint_2_3_4 Output
+
+; CHECK-DAG: [[x]] = OpVariable %_ptr_Output__arr_uint_uint_2 Output
+; CHECK-DAG: [[y]] = OpVariable %_ptr_Input__arr_uint_uint_2 Input
+; CHECK-DAG: [[z0]] = OpVariable %_ptr_Output_uint Output
+; CHECK-DAG: [[z1]] = OpVariable %_ptr_Output_uint Output
+; CHECK-DAG: [[w0]] = OpVariable %_ptr_Input_uint Input
+; CHECK-DAG: [[w1]] = OpVariable %_ptr_Input_uint Input
+; CHECK-DAG: [[u0]] = OpVariable %_ptr_Input__arr_uint_uint_3 Input
+; CHECK-DAG: [[u1]] = OpVariable %_ptr_Input__arr_uint_uint_3 Input
+; CHECK-DAG: [[v0]] = OpVariable %_ptr_Output__arr_uint_uint_4 Output
+; CHECK-DAG: [[v1]] = OpVariable %_ptr_Output__arr_uint_uint_4 Output
+; CHECK-DAG: [[v2]] = OpVariable %_ptr_Output__arr_uint_uint_4 Output
+; CHECK-DAG: [[v3]] = OpVariable %_ptr_Output__arr_uint_uint_4 Output
+; CHECK-DAG: [[v4]] = OpVariable %_ptr_Output__arr_uint_uint_4 Output
+; CHECK-DAG: [[v5]] = OpVariable %_ptr_Output__arr_uint_uint_4 Output
+
+ %void = OpTypeVoid
+ %void_f = OpTypeFunction %void
+ %func = OpFunction %void None %void_f
+ %label = OpLabel
+
+; CHECK: [[w0_value:%\w+]] = OpLoad %uint [[w0]]
+; CHECK: [[w1_value:%\w+]] = OpLoad %uint [[w1]]
+; CHECK: [[w_value:%\w+]] = OpCompositeConstruct %_arr_uint_uint_2 [[w0_value]] [[w1_value]]
+; CHECK: [[w0:%\w+]] = OpCompositeExtract %uint [[w_value]] 0
+; CHECK: OpStore [[z0]] [[w0]]
+; CHECK: [[w1:%\w+]] = OpCompositeExtract %uint [[w_value]] 1
+; CHECK: OpStore [[z1]] [[w1]]
+ %w_value = OpLoad %_arr_uint_uint_2 %w
+ OpStore %z %w_value
+
+; CHECK: [[u00_ptr:%\w+]] = OpAccessChain %_ptr_Input_uint [[u0]] %uint_0
+; CHECK: [[u00:%\w+]] = OpLoad %uint [[u00_ptr]]
+; CHECK: [[u10_ptr:%\w+]] = OpAccessChain %_ptr_Input_uint [[u1]] %uint_0
+; CHECK: [[u10:%\w+]] = OpLoad %uint [[u10_ptr]]
+; CHECK: [[u01_ptr:%\w+]] = OpAccessChain %_ptr_Input_uint [[u0]] %uint_1
+; CHECK: [[u01:%\w+]] = OpLoad %uint [[u01_ptr]]
+; CHECK: [[u11_ptr:%\w+]] = OpAccessChain %_ptr_Input_uint [[u1]] %uint_1
+; CHECK: [[u11:%\w+]] = OpLoad %uint [[u11_ptr]]
+; CHECK: [[u02_ptr:%\w+]] = OpAccessChain %_ptr_Input_uint [[u0]] %uint_2
+; CHECK: [[u02:%\w+]] = OpLoad %uint [[u02_ptr]]
+; CHECK: [[u12_ptr:%\w+]] = OpAccessChain %_ptr_Input_uint [[u1]] %uint_2
+; CHECK: [[u12:%\w+]] = OpLoad %uint [[u12_ptr]]
+
+; CHECK-DAG: [[u0_val:%\w+]] = OpCompositeConstruct %_arr_uint_uint_2 [[u00]] [[u10]]
+; CHECK-DAG: [[u1_val:%\w+]] = OpCompositeConstruct %_arr_uint_uint_2 [[u01]] [[u11]]
+; CHECK-DAG: [[u2_val:%\w+]] = OpCompositeConstruct %_arr_uint_uint_2 [[u02]] [[u12]]
+
+; CHECK: [[u_val:%\w+]] = OpCompositeConstruct %_arr__arr_uint_uint_2_uint_3 [[u0_val]] [[u1_val]] [[u2_val]]
+
+; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Output_uint [[v0]] %uint_1
+; CHECK: [[val:%\w+]] = OpCompositeExtract %uint [[u_val]] 0 0
+; CHECK: OpStore [[ptr]] [[val]]
+; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Output_uint [[v1]] %uint_1
+; CHECK: [[val:%\w+]] = OpCompositeExtract %uint [[u_val]] 0 1
+; CHECK: OpStore [[ptr]] [[val]]
+; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Output_uint [[v2]] %uint_1
+; CHECK: [[val:%\w+]] = OpCompositeExtract %uint [[u_val]] 1 0
+; CHECK: OpStore [[ptr]] [[val]]
+; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Output_uint [[v3]] %uint_1
+; CHECK: [[val:%\w+]] = OpCompositeExtract %uint [[u_val]] 1 1
+; CHECK: OpStore [[ptr]] [[val]]
+; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Output_uint [[v4]] %uint_1
+; CHECK: [[val:%\w+]] = OpCompositeExtract %uint [[u_val]] 2 0
+; CHECK: OpStore [[ptr]] [[val]]
+; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Output_uint [[v5]] %uint_1
+; CHECK: [[val:%\w+]] = OpCompositeExtract %uint [[u_val]] 2 1
+; CHECK: OpStore [[ptr]] [[val]]
+ %v_ptr = OpAccessChain %_ptr_Output__arr_arr_uint_uint_2_3 %v %uint_1
+ %u_val = OpLoad %_arr_arr_uint_uint_2_3 %u
+ OpStore %v_ptr %u_val
+
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<InterfaceVariableScalarReplacement>(spirv, true);
+}
+
+TEST_F(InterfaceVariableScalarReplacementTest,
+ CheckPatchDecorationPreservation) {
+ // Make sure scalars for the variables with the extra arrayness have the extra
+ // arrayness after running the pass while others do not have it.
+ // Only "y" does not have the extra arrayness in the following SPIR-V.
+ const std::string spirv = R"(
+ OpCapability Shader
+ OpCapability Tessellation
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint TessellationEvaluation %func "shader" %x %y %z %w
+ OpDecorate %z Patch
+ OpDecorate %w Patch
+ OpDecorate %z Location 0
+ OpDecorate %x Location 2
+ OpDecorate %y Location 0
+ OpDecorate %w Location 1
+ OpName %x "x"
+ OpName %y "y"
+ OpName %z "z"
+ OpName %w "w"
+
+ ; CHECK: OpName [[y:%\w+]] "y"
+ ; CHECK-NOT: OpName {{%\w+}} "y"
+ ; CHECK-DAG: OpName [[z0:%\w+]] "z"
+ ; CHECK-DAG: OpName [[z1:%\w+]] "z"
+ ; CHECK-DAG: OpName [[w0:%\w+]] "w"
+ ; CHECK-DAG: OpName [[w1:%\w+]] "w"
+ ; CHECK-DAG: OpName [[x0:%\w+]] "x"
+ ; CHECK-DAG: OpName [[x1:%\w+]] "x"
+
+ %uint = OpTypeInt 32 0
+ %uint_2 = OpConstant %uint 2
+%_arr_uint_uint_2 = OpTypeArray %uint %uint_2
+%_ptr_Output__arr_uint_uint_2 = OpTypePointer Output %_arr_uint_uint_2
+%_ptr_Input__arr_uint_uint_2 = OpTypePointer Input %_arr_uint_uint_2
+ %z = OpVariable %_ptr_Output__arr_uint_uint_2 Output
+ %x = OpVariable %_ptr_Output__arr_uint_uint_2 Output
+ %y = OpVariable %_ptr_Input__arr_uint_uint_2 Input
+ %w = OpVariable %_ptr_Input__arr_uint_uint_2 Input
+
+ ; CHECK-DAG: [[y]] = OpVariable %_ptr_Input__arr_uint_uint_2 Input
+ ; CHECK-DAG: [[z0]] = OpVariable %_ptr_Output_uint Output
+ ; CHECK-DAG: [[z1]] = OpVariable %_ptr_Output_uint Output
+ ; CHECK-DAG: [[w0]] = OpVariable %_ptr_Input_uint Input
+ ; CHECK-DAG: [[w1]] = OpVariable %_ptr_Input_uint Input
+ ; CHECK-DAG: [[x0]] = OpVariable %_ptr_Output_uint Output
+ ; CHECK-DAG: [[x1]] = OpVariable %_ptr_Output_uint Output
+
+ %void = OpTypeVoid
+ %void_f = OpTypeFunction %void
+ %func = OpFunction %void None %void_f
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<InterfaceVariableScalarReplacement>(spirv, true);
+}
+
+TEST_F(InterfaceVariableScalarReplacementTest,
+ CheckEntryPointInterfaceOperands) {
+ const std::string spirv = R"(
+ OpCapability Shader
+ OpCapability Tessellation
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint TessellationEvaluation %tess "tess" %x %y
+ OpEntryPoint Vertex %vert "vert" %w
+ OpDecorate %z Location 0
+ OpDecorate %x Location 2
+ OpDecorate %y Location 0
+ OpDecorate %w Location 1
+ OpName %x "x"
+ OpName %y "y"
+ OpName %z "z"
+ OpName %w "w"
+
+ ; CHECK: OpName [[y:%\w+]] "y"
+ ; CHECK-NOT: OpName {{%\w+}} "y"
+ ; CHECK-DAG: OpName [[x0:%\w+]] "x"
+ ; CHECK-DAG: OpName [[x1:%\w+]] "x"
+ ; CHECK-DAG: OpName [[w0:%\w+]] "w"
+ ; CHECK-DAG: OpName [[w1:%\w+]] "w"
+ ; CHECK-DAG: OpName [[z:%\w+]] "z"
+ ; CHECK-NOT: OpName {{%\w+}} "z"
+
+ %uint = OpTypeInt 32 0
+ %uint_2 = OpConstant %uint 2
+%_arr_uint_uint_2 = OpTypeArray %uint %uint_2
+%_ptr_Output__arr_uint_uint_2 = OpTypePointer Output %_arr_uint_uint_2
+%_ptr_Input__arr_uint_uint_2 = OpTypePointer Input %_arr_uint_uint_2
+ %z = OpVariable %_ptr_Output__arr_uint_uint_2 Output
+ %x = OpVariable %_ptr_Output__arr_uint_uint_2 Output
+ %y = OpVariable %_ptr_Input__arr_uint_uint_2 Input
+ %w = OpVariable %_ptr_Input__arr_uint_uint_2 Input
+
+ ; CHECK-DAG: [[y]] = OpVariable %_ptr_Input__arr_uint_uint_2 Input
+ ; CHECK-DAG: [[z]] = OpVariable %_ptr_Output__arr_uint_uint_2 Output
+ ; CHECK-DAG: [[w0]] = OpVariable %_ptr_Input_uint Input
+ ; CHECK-DAG: [[w1]] = OpVariable %_ptr_Input_uint Input
+ ; CHECK-DAG: [[x0]] = OpVariable %_ptr_Output_uint Output
+ ; CHECK-DAG: [[x1]] = OpVariable %_ptr_Output_uint Output
+
+ %void = OpTypeVoid
+ %void_f = OpTypeFunction %void
+ %tess = OpFunction %void None %void_f
+ %bb0 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %vert = OpFunction %void None %void_f
+ %bb1 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<InterfaceVariableScalarReplacement>(spirv, true);
+}
+
+class InterfaceVarSROAErrorTest : public PassTest<::testing::Test> {
+ public:
+ InterfaceVarSROAErrorTest()
+ : consumer_([this](spv_message_level_t level, const char*,
+ const spv_position_t& position, const char* message) {
+ if (!error_message_.empty()) error_message_ += "\n";
+ switch (level) {
+ case SPV_MSG_FATAL:
+ case SPV_MSG_INTERNAL_ERROR:
+ case SPV_MSG_ERROR:
+ error_message_ += "ERROR";
+ break;
+ case SPV_MSG_WARNING:
+ error_message_ += "WARNING";
+ break;
+ case SPV_MSG_INFO:
+ error_message_ += "INFO";
+ break;
+ case SPV_MSG_DEBUG:
+ error_message_ += "DEBUG";
+ break;
+ }
+ error_message_ +=
+ ": " + std::to_string(position.index) + ": " + message;
+ }) {}
+
+ Pass::Status RunPass(const std::string& text) {
+ std::unique_ptr<IRContext> context_ =
+ spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_2, consumer_, text);
+ if (!context_.get()) return Pass::Status::Failure;
+
+ PassManager manager;
+ manager.SetMessageConsumer(consumer_);
+ manager.AddPass<InterfaceVariableScalarReplacement>();
+
+ return manager.Run(context_.get());
+ }
+
+ std::string GetErrorMessage() const { return error_message_; }
+
+ void TearDown() override { error_message_.clear(); }
+
+ private:
+ spvtools::MessageConsumer consumer_;
+ std::string error_message_;
+};
+
+TEST_F(InterfaceVarSROAErrorTest, CheckConflictOfExtraArraynessBetweenEntries) {
+ const std::string spirv = R"(
+ OpCapability Shader
+ OpCapability Tessellation
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint TessellationControl %tess "tess" %x %y %z
+ OpEntryPoint Vertex %vert "vert" %z %w
+ OpDecorate %z Location 0
+ OpDecorate %x Location 2
+ OpDecorate %y Location 0
+ OpDecorate %w Location 1
+ OpName %x "x"
+ OpName %y "y"
+ OpName %z "z"
+ OpName %w "w"
+ %uint = OpTypeInt 32 0
+ %uint_2 = OpConstant %uint 2
+%_arr_uint_uint_2 = OpTypeArray %uint %uint_2
+%_ptr_Output__arr_uint_uint_2 = OpTypePointer Output %_arr_uint_uint_2
+%_ptr_Input__arr_uint_uint_2 = OpTypePointer Input %_arr_uint_uint_2
+ %z = OpVariable %_ptr_Output__arr_uint_uint_2 Output
+ %x = OpVariable %_ptr_Output__arr_uint_uint_2 Output
+ %y = OpVariable %_ptr_Input__arr_uint_uint_2 Input
+ %w = OpVariable %_ptr_Input__arr_uint_uint_2 Input
+ %void = OpTypeVoid
+ %void_f = OpTypeFunction %void
+ %tess = OpFunction %void None %void_f
+ %bb0 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %vert = OpFunction %void None %void_f
+ %bb1 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ EXPECT_EQ(RunPass(spirv), Pass::Status::Failure);
+ const char expected_error[] =
+ "ERROR: 0: A variable is arrayed for an entry point but it is not "
+ "arrayed for another entry point\n"
+ " %z = OpVariable %_ptr_Output__arr_uint_uint_2 Output";
+ EXPECT_STREQ(GetErrorMessage().c_str(), expected_error);
+}
+
+} // namespace
+} // namespace opt
+} // namespace spvtools
diff --git a/test/opt/ir_context_test.cpp b/test/opt/ir_context_test.cpp
index b6866d01..dcae7cf8 100644
--- a/test/opt/ir_context_test.cpp
+++ b/test/opt/ir_context_test.cpp
@@ -90,6 +90,21 @@ TEST_F(IRContextTest, IndividualValidAfterBuild) {
}
}
+TEST_F(IRContextTest, DontRebuildValidAnalysis) {
+ std::unique_ptr<Module> module(new Module());
+ IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
+ spvtools::MessageConsumer());
+
+ auto* oldCfg = localContext.cfg();
+ auto* oldDefUse = localContext.get_def_use_mgr();
+ localContext.BuildInvalidAnalyses(IRContext::kAnalysisCFG |
+ IRContext::kAnalysisDefUse);
+ auto* newCfg = localContext.cfg();
+ auto* newDefUse = localContext.get_def_use_mgr();
+ EXPECT_EQ(oldCfg, newCfg);
+ EXPECT_EQ(oldDefUse, newDefUse);
+}
+
TEST_F(IRContextTest, AllValidAfterBuild) {
std::unique_ptr<Module> module = MakeUnique<Module>();
IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
@@ -1132,40 +1147,6 @@ OpFunctionEnd)";
dbg_decl = ctx->get_def_use_mgr()->GetDef(25);
EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex),
20);
-
- // No DebugValue should be added because result id '26' is not used for
- // DebugDeclare.
- ctx->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(dbg_decl, 26, 22,
- dbg_decl, nullptr);
- EXPECT_EQ(dbg_decl->NextNode()->opcode(), SpvOpReturn);
-
- // DebugValue should be added because result id '20' is used for DebugDeclare.
- ctx->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(dbg_decl, 20, 22,
- dbg_decl, nullptr);
- EXPECT_EQ(dbg_decl->NextNode()->GetOpenCL100DebugOpcode(),
- OpenCLDebugInfo100DebugValue);
-
- // Replace all uses of result it '20' with '26'
- EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex),
- 20);
- EXPECT_TRUE(ctx->ReplaceAllUsesWith(20, 26));
- EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex),
- 26);
-
- // No DebugValue should be added because result id '20' is not used for
- // DebugDeclare.
- ctx->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(dbg_decl, 20, 7,
- dbg_decl, nullptr);
- Instruction* dbg_value = dbg_decl->NextNode();
- EXPECT_EQ(dbg_value->GetOpenCL100DebugOpcode(), OpenCLDebugInfo100DebugValue);
- EXPECT_EQ(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex), 22);
-
- // DebugValue should be added because result id '26' is used for DebugDeclare.
- ctx->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(dbg_decl, 26, 7,
- dbg_decl, nullptr);
- dbg_value = dbg_decl->NextNode();
- EXPECT_EQ(dbg_value->GetOpenCL100DebugOpcode(), OpenCLDebugInfo100DebugValue);
- EXPECT_EQ(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex), 7);
}
} // namespace
diff --git a/test/opt/local_access_chain_convert_test.cpp b/test/opt/local_access_chain_convert_test.cpp
index 6fcf23fb..07fb537c 100644
--- a/test/opt/local_access_chain_convert_test.cpp
+++ b/test/opt/local_access_chain_convert_test.cpp
@@ -1156,6 +1156,197 @@ TEST_F(LocalAccessChainConvertTest, AccessChainWithNoIndex) {
SinglePassRunAndMatch<LocalAccessChainConvertPass>(before, true);
}
+TEST_F(LocalAccessChainConvertTest, AccessChainWithLongIndex) {
+ // The access chain take a value that is larger than 32-bit. The index cannot
+ // be encoded in an OpCompositeExtract, so nothing should be done.
+ const std::string before =
+ R"(OpCapability Shader
+OpCapability Int64
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main_0004f4d4_85b2f584"
+OpExecutionMode %2 OriginUpperLeft
+%ulong = OpTypeInt 64 0
+%ulong_8589934592 = OpConstant %ulong 8589934592
+%ulong_8589934591 = OpConstant %ulong 8589934591
+%_arr_ulong_ulong_8589934592 = OpTypeArray %ulong %ulong_8589934592
+%_ptr_Function__arr_ulong_ulong_8589934592 = OpTypePointer Function %_arr_ulong_ulong_8589934592
+%_ptr_Function_ulong = OpTypePointer Function %ulong
+%void = OpTypeVoid
+%10 = OpTypeFunction %void
+%2 = OpFunction %void None %10
+%11 = OpLabel
+%12 = OpVariable %_ptr_Function__arr_ulong_ulong_8589934592 Function
+%13 = OpAccessChain %_ptr_Function_ulong %12 %ulong_8589934591
+%14 = OpLoad %ulong %13
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<LocalAccessChainConvertPass>(before, before, false,
+ true);
+}
+
+TEST_F(LocalAccessChainConvertTest, AccessChainWith32BitIndexInLong) {
+ // The access chain has a value that is 32-bits, but it is stored in a 64-bit
+ // variable. This access change can be converted to an extract.
+ const std::string before =
+ R"(
+; CHECK: OpFunction
+; CHECK: [[var:%\w+]] = OpVariable
+; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[var]]
+; CHECK: OpCompositeExtract %ulong [[ld]] 3
+ OpCapability Shader
+ OpCapability Int64
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main_0004f4d4_85b2f584"
+ OpExecutionMode %2 OriginUpperLeft
+ %ulong = OpTypeInt 64 0
+%ulong_8589934592 = OpConstant %ulong 8589934592
+%ulong_3 = OpConstant %ulong 3
+%_arr_ulong_ulong_8589934592 = OpTypeArray %ulong %ulong_8589934592
+%_ptr_Function__arr_ulong_ulong_8589934592 = OpTypePointer Function %_arr_ulong_ulong_8589934592
+%_ptr_Function_ulong = OpTypePointer Function %ulong
+ %void = OpTypeVoid
+ %10 = OpTypeFunction %void
+ %2 = OpFunction %void None %10
+ %11 = OpLabel
+ %12 = OpVariable %_ptr_Function__arr_ulong_ulong_8589934592 Function
+ %13 = OpAccessChain %_ptr_Function_ulong %12 %ulong_3
+ %14 = OpLoad %ulong %13
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<LocalAccessChainConvertPass>(before, true);
+}
+
+TEST_F(LocalAccessChainConvertTest, AccessChainWithVarIndex) {
+ // The access chain has a value that is not constant, so there should not be
+ // any changes.
+ const std::string before =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main_0004f4d4_85b2f584"
+OpExecutionMode %2 OriginUpperLeft
+%uint = OpTypeInt 32 0
+%uint_5 = OpConstant %uint 5
+%_arr_uint_uint_5 = OpTypeArray %uint %uint_5
+%_ptr_Function__arr_uint_uint_5 = OpTypePointer Function %_arr_uint_uint_5
+%_ptr_Function_uint = OpTypePointer Function %uint
+%8 = OpUndef %uint
+%void = OpTypeVoid
+%10 = OpTypeFunction %void
+%2 = OpFunction %void None %10
+%11 = OpLabel
+%12 = OpVariable %_ptr_Function__arr_uint_uint_5 Function
+%13 = OpAccessChain %_ptr_Function_uint %12 %8
+%14 = OpLoad %uint %13
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<LocalAccessChainConvertPass>(before, before, false,
+ true);
+}
+
+TEST_F(LocalAccessChainConvertTest, OutOfBoundsAccess) {
+ // The access chain indexes element 12 in an array of size 10. Nothing should
+ // be done.
+ const std::string assembly =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main" %3
+OpExecutionMode %2 OriginUpperLeft
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%int_10 = OpConstant %int 10
+%_arr_int_int_10 = OpTypeArray %int %int_10
+%_ptr_Function_int = OpTypePointer Function %int
+%int_12 = OpConstant %int 12
+%_ptr_Output_int = OpTypePointer Output %int
+%3 = OpVariable %_ptr_Output_int Output
+%_ptr_Function__arr_int_int_10 = OpTypePointer Function %_arr_int_int_10
+%2 = OpFunction %void None %5
+%13 = OpLabel
+%14 = OpVariable %_ptr_Function__arr_int_int_10 Function
+%15 = OpAccessChain %_ptr_Function_int %14 %int_12
+%16 = OpLoad %int %15
+OpStore %3 %16
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<LocalAccessChainConvertPass>(assembly, assembly, false,
+ true);
+}
+
+TEST_F(LocalAccessChainConvertTest, OutOfBoundsAccessAtBoundary) {
+ // The access chain indexes element 10 in an array of size 10. Nothing should
+ // be done.
+ const std::string assembly =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main" %3
+OpExecutionMode %2 OriginUpperLeft
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%int_10 = OpConstant %int 10
+%_arr_int_int_10 = OpTypeArray %int %int_10
+%_ptr_Function_int = OpTypePointer Function %int
+%_ptr_Output_int = OpTypePointer Output %int
+%3 = OpVariable %_ptr_Output_int Output
+%_ptr_Function__arr_int_int_10 = OpTypePointer Function %_arr_int_int_10
+%2 = OpFunction %void None %5
+%12 = OpLabel
+%13 = OpVariable %_ptr_Function__arr_int_int_10 Function
+%14 = OpAccessChain %_ptr_Function_int %13 %int_10
+%15 = OpLoad %int %14
+OpStore %3 %15
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<LocalAccessChainConvertPass>(assembly, assembly, false,
+ true);
+}
+
+TEST_F(LocalAccessChainConvertTest, NegativeIndex) {
+ // The access chain has a negative index and should not be converted because
+ // the extract instruction cannot hold a negative number.
+ const std::string assembly =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main"
+OpExecutionMode %2 OriginUpperLeft
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_3808428041 = OpConstant %uint 3808428041
+%_arr_int_uint_3808428041 = OpTypeArray %int %uint_3808428041
+%_ptr_Function__arr_int_uint_3808428041 = OpTypePointer Function %_arr_int_uint_3808428041
+%_ptr_Function_int = OpTypePointer Function %int
+%int_n1272971256 = OpConstant %int -1272971256
+%2 = OpFunction %void None %4
+%12 = OpLabel
+%13 = OpVariable %_ptr_Function__arr_int_uint_3808428041 Function
+%14 = OpAccessChain %_ptr_Function_int %13 %int_n1272971256
+%15 = OpLoad %int %14
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<LocalAccessChainConvertPass>(assembly, assembly, false,
+ true);
+}
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
diff --git a/test/opt/local_single_store_elim_test.cpp b/test/opt/local_single_store_elim_test.cpp
index 5d910c4e..8f43a11d 100644
--- a/test/opt/local_single_store_elim_test.cpp
+++ b/test/opt/local_single_store_elim_test.cpp
@@ -1494,19 +1494,19 @@ TEST_F(LocalSingleStoreElimTest, AddDebugValueforStoreOutOfDebugDeclareScope) {
%56 = OpLoad %v4float %in_var_COLOR
;CHECK: DebugNoScope
;CHECK-NOT: OpLine
-;CHECK: [[pos:%\w+]] = OpLoad %v4float %in_var_POSITION
-;CHECK: [[color:%\w+]] = OpLoad %v4float %in_var_COLOR
-
OpLine %7 7 23
OpStore %param_var_color %56
OpNoLine
%93 = OpExtInst %void %1 DebugScope %48
%73 = OpExtInst %void %1 DebugDeclare %53 %param_var_pos %52
%74 = OpExtInst %void %1 DebugDeclare %51 %param_var_color %52
+;CHECK: [[pos:%\w+]] = OpLoad %v4float %in_var_POSITION
;CHECK: OpLine [[file:%\w+]] 6 23
-;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[dbg_pos]] [[pos]] [[empty_expr:%\w+]]
+;CHECK: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[dbg_pos]] [[pos]] [[empty_expr:%\w+]]
+;CHECK: [[color:%\w+]] = OpLoad %v4float %in_var_COLOR
;CHECK: OpLine [[file]] 7 23
-;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[dbg_color]] [[color]] [[empty_expr]]
+;CHECK: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[dbg_color]] [[color]] [[empty_expr]]
+;CHECK: OpLine [[file]] 9 3
%94 = OpExtInst %void %1 DebugScope %49
OpLine %7 9 3
@@ -1529,6 +1529,227 @@ TEST_F(LocalSingleStoreElimTest, AddDebugValueforStoreOutOfDebugDeclareScope) {
SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
}
+TEST_F(LocalSingleStoreElimTest, DebugValuesForAllLocalsAndParams) {
+ // Texture2D g_tColor;
+ //
+ // SamplerState g_sAniso;
+ //
+ // struct PS_INPUT
+ // {
+ // float2 vTextureCoords : TEXCOORD2 ;
+ // } ;
+ //
+ // struct PS_OUTPUT
+ // {
+ // float4 vColor : SV_Target0 ;
+ // } ;
+ //
+ // void do_sample ( in float2 tc, out float4 c ) {
+ // c = g_tColor . Sample ( g_sAniso , tc ) ;
+ // }
+ //
+ // PS_OUTPUT MainPs ( PS_INPUT i )
+ // {
+ // PS_OUTPUT ps_output ;
+ // float4 color;
+ //
+ // do_sample ( i . vTextureCoords . xy , color ) ;
+ // ps_output . vColor = color;
+ // return ps_output ;
+ // }
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+;CHECK: [[set:%\w+]] = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %MainPs "MainPs" %in_var_TEXCOORD2 %out_var_SV_Target0 %g_tColor %g_sAniso
+ OpExecutionMode %MainPs OriginUpperLeft
+ %7 = OpString "foo2.frag"
+ %21 = OpString "float"
+ %27 = OpString "PS_INPUT"
+ %31 = OpString "vTextureCoords"
+ %34 = OpString "PS_OUTPUT"
+ %38 = OpString "vColor"
+ %40 = OpString "do_sample"
+ %41 = OpString ""
+ %45 = OpString "c"
+ %47 = OpString "tc"
+ %50 = OpString "MainPs"
+ %54 = OpString "color"
+ %56 = OpString "ps_output"
+ %59 = OpString "i"
+ %62 = OpString "@type.sampler"
+ %63 = OpString "type.sampler"
+ %65 = OpString "g_sAniso"
+ %67 = OpString "@type.2d.image"
+ %68 = OpString "type.2d.image"
+ %70 = OpString "TemplateParam"
+ %73 = OpString "g_tColor"
+;CHECK: [[str_c:%\w+]] = OpString "c"
+;CHECK: [[str_tc:%\w+]] = OpString "tc"
+;CHECK: [[str_color:%\w+]] = OpString "color"
+;CHECK: [[str_ps_output:%\w+]] = OpString "ps_output"
+;CHECK: [[str_i:%\w+]] = OpString "i"
+ OpName %type_2d_image "type.2d.image"
+ OpName %g_tColor "g_tColor"
+ OpName %type_sampler "type.sampler"
+ OpName %g_sAniso "g_sAniso"
+ OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2"
+ OpName %out_var_SV_Target0 "out.var.SV_Target0"
+ OpName %MainPs "MainPs"
+ OpName %PS_INPUT "PS_INPUT"
+ OpMemberName %PS_INPUT 0 "vTextureCoords"
+ OpName %PS_OUTPUT "PS_OUTPUT"
+ OpMemberName %PS_OUTPUT 0 "vColor"
+ OpName %type_sampled_image "type.sampled.image"
+ OpDecorate %in_var_TEXCOORD2 Location 0
+ OpDecorate %out_var_SV_Target0 Location 0
+ OpDecorate %g_tColor DescriptorSet 0
+ OpDecorate %g_tColor Binding 0
+ OpDecorate %g_sAniso DescriptorSet 0
+ OpDecorate %g_sAniso Binding 1
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+ %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+ %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %uint_64 = OpConstant %uint 64
+ %uint_0 = OpConstant %uint 0
+ %uint_128 = OpConstant %uint 128
+ %75 = OpTypeFunction %void
+ %PS_INPUT = OpTypeStruct %v2float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+ %PS_OUTPUT = OpTypeStruct %v4float
+ %85 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+ %105 = OpTypeFunction %void %_ptr_Function_v2float %_ptr_Function_v4float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+ %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+ %145 = OpExtInst %void %1 DebugOperation Deref
+ %61 = OpExtInst %void %1 DebugInfoNone
+ %58 = OpExtInst %void %1 DebugExpression
+ %23 = OpExtInst %void %1 DebugTypeBasic %21 %uint_32 Float
+ %24 = OpExtInst %void %1 DebugTypeVector %23 2
+ %25 = OpExtInst %void %1 DebugSource %7
+ %26 = OpExtInst %void %1 DebugCompilationUnit 1 4 %25 HLSL
+ %29 = OpExtInst %void %1 DebugTypeComposite %27 Structure %25 5 8 %26 %27 %uint_64 FlagIsProtected|FlagIsPrivate %30
+ %30 = OpExtInst %void %1 DebugTypeMember %31 %24 %25 7 12 %29 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate
+ %33 = OpExtInst %void %1 DebugTypeVector %23 4
+ %36 = OpExtInst %void %1 DebugTypeComposite %34 Structure %25 10 8 %26 %34 %uint_128 FlagIsProtected|FlagIsPrivate %37
+ %37 = OpExtInst %void %1 DebugTypeMember %38 %33 %25 12 12 %36 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+ %39 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void %24 %33
+ %42 = OpExtInst %void %1 DebugFunction %40 %39 %25 15 1 %26 %41 FlagIsProtected|FlagIsPrivate 15 %61
+ %44 = OpExtInst %void %1 DebugLexicalBlock %25 15 47 %42
+ %46 = OpExtInst %void %1 DebugLocalVariable %45 %33 %25 15 43 %42 FlagIsLocal 2
+ %48 = OpExtInst %void %1 DebugLocalVariable %47 %24 %25 15 28 %42 FlagIsLocal 1
+ %49 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %36 %29
+ %51 = OpExtInst %void %1 DebugFunction %50 %49 %25 19 1 %26 %41 FlagIsProtected|FlagIsPrivate 20 %61
+ %53 = OpExtInst %void %1 DebugLexicalBlock %25 20 1 %51
+ %55 = OpExtInst %void %1 DebugLocalVariable %54 %33 %25 22 12 %53 FlagIsLocal
+ %57 = OpExtInst %void %1 DebugLocalVariable %56 %36 %25 21 15 %53 FlagIsLocal
+ %60 = OpExtInst %void %1 DebugLocalVariable %59 %29 %25 19 29 %51 FlagIsLocal 1
+ %64 = OpExtInst %void %1 DebugTypeComposite %62 Structure %25 0 0 %26 %63 %61 FlagIsProtected|FlagIsPrivate
+ %66 = OpExtInst %void %1 DebugGlobalVariable %65 %64 %25 3 14 %26 %65 %g_sAniso FlagIsDefinition
+ %69 = OpExtInst %void %1 DebugTypeComposite %67 Class %25 0 0 %26 %68 %61 FlagIsProtected|FlagIsPrivate
+ %71 = OpExtInst %void %1 DebugTypeTemplateParameter %70 %33 %61 %25 0 0
+ %72 = OpExtInst %void %1 DebugTypeTemplate %69 %71
+ %74 = OpExtInst %void %1 DebugGlobalVariable %73 %72 %25 1 11 %26 %73 %g_tColor FlagIsDefinition
+ %142 = OpExtInst %void %1 DebugInlinedAt 24 %53
+ %144 = OpExtInst %void %1 DebugExpression %145
+ %155 = OpExtInst %void %1 DebugExpression %145
+;CHECK: [[var_c:%\w+]] = OpExtInst %void [[set]] DebugLocalVariable [[str_c]]
+;CHECK: [[var_tc:%\w+]] = OpExtInst %void [[set]] DebugLocalVariable [[str_tc]]
+;CHECK: [[var_color:%\w+]] = OpExtInst %void [[set]] DebugLocalVariable [[str_color]]
+;CHECK: [[var_ps_output:%\w+]] = OpExtInst %void [[set]] DebugLocalVariable [[str_ps_output]]
+;CHECK: [[var_i:%\w+]] = OpExtInst %void [[set]] DebugLocalVariable [[str_i]]
+ %MainPs = OpFunction %void None %75
+ %76 = OpLabel
+ %153 = OpVariable %_ptr_Function_v2float Function
+ %149 = OpVariable %_ptr_Function_v4float Function
+ %157 = OpExtInst %void %1 DebugScope %53
+ %143 = OpVariable %_ptr_Function_v4float Function
+ %121 = OpVariable %_ptr_Function_v4float Function
+ %122 = OpVariable %_ptr_Function_v2float Function
+ %158 = OpExtInst %void %1 DebugScope %51
+ OpLine %7 19 29
+ %156 = OpExtInst %void %1 DebugValue %60 %153 %155 %int_0
+ %159 = OpExtInst %void %1 DebugScope %53
+ OpLine %7 21 15
+ %146 = OpExtInst %void %1 DebugValue %57 %143 %144 %int_0
+ OpNoLine
+ %160 = OpExtInst %void %1 DebugNoScope
+ %80 = OpLoad %v2float %in_var_TEXCOORD2
+ %81 = OpCompositeConstruct %PS_INPUT %80
+ %154 = OpCompositeExtract %v2float %81 0
+ OpStore %153 %154
+ %161 = OpExtInst %void %1 DebugScope %53
+ OpLine %7 22 12
+ %127 = OpExtInst %void %1 DebugDeclare %55 %121 %58
+ OpLine %7 24 17
+ %129 = OpLoad %v2float %153
+ OpStore %122 %129
+ %162 = OpExtInst %void %1 DebugScope %42 %142
+ OpLine %7 15 28
+ %135 = OpExtInst %void %1 DebugDeclare %48 %122 %58
+ OpLine %7 15 43
+ %136 = OpExtInst %void %1 DebugDeclare %46 %121 %58
+ %163 = OpExtInst %void %1 DebugScope %44 %142
+ OpLine %7 16 9
+ %137 = OpLoad %type_2d_image %g_tColor
+ OpLine %7 16 29
+ %138 = OpLoad %type_sampler %g_sAniso
+ OpLine %7 16 40
+ %139 = OpLoad %v2float %122
+ OpLine %7 16 9
+ %140 = OpSampledImage %type_sampled_image %137 %138
+ %141 = OpImageSampleImplicitLod %v4float %140 %139 None
+ OpLine %7 16 5
+ OpStore %121 %141
+ %164 = OpExtInst %void %1 DebugScope %53
+ OpLine %7 25 26
+ %131 = OpLoad %v4float %121
+ OpLine %7 25 5
+ OpStore %143 %131
+ OpLine %7 26 12
+ %147 = OpLoad %v4float %143
+ %148 = OpCompositeConstruct %PS_OUTPUT %147
+ OpLine %7 26 5
+ %150 = OpCompositeExtract %v4float %148 0
+ OpStore %149 %150
+ OpNoLine
+ %165 = OpExtInst %void %1 DebugNoScope
+ %151 = OpLoad %v4float %149
+ %152 = OpCompositeConstruct %PS_OUTPUT %151
+ %84 = OpCompositeExtract %v4float %152 0
+ OpStore %out_var_SV_Target0 %84
+ OpLine %7 27 1
+ OpReturn
+ OpFunctionEnd
+;CHECK: {{%\w+}} = OpExtInst %void [[set]] DebugValue [[var_i]]
+;CHECK: {{%\w+}} = OpExtInst %void [[set]] DebugValue [[var_tc]]
+;CHECK: {{%\w+}} = OpExtInst %void [[set]] DebugValue [[var_c]]
+;CHECK: {{%\w+}} = OpExtInst %void [[set]] DebugValue [[var_color]]
+;CHECK: {{%\w+}} = OpExtInst %void [[set]] DebugValue [[var_ps_output]]
+ )";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Other types
diff --git a/test/opt/local_ssa_elim_test.cpp b/test/opt/local_ssa_elim_test.cpp
index 4b7542fe..45006ca6 100644
--- a/test/opt/local_ssa_elim_test.cpp
+++ b/test/opt/local_ssa_elim_test.cpp
@@ -2978,6 +2978,7 @@ TEST_F(LocalSSAElimTest, DebugValueForReferenceVariableInBB) {
; CHECK: OpExtInst %void [[ext]] DebugScope [[dbg_main]]
; CHECK: OpStore %f %float_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_x]] %float_0
; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] %float_0
; CHECK-NEXT: OpStore %i %int_0
; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] %int_0
@@ -4251,6 +4252,1210 @@ OpFunctionEnd
SinglePassRunAndCheck<SSARewritePass>(text, text, false);
}
+TEST_F(LocalSSAElimTest, MissingDebugValue) {
+ // Make sure DebugValue for final fragcolor assignment is generated.
+
+ const std::string text =
+ R"(
+ OpCapability Shader
+ OpCapability ImageQuery
+ OpExtension "SPV_KHR_non_semantic_info"
+ %1 = OpExtInstImport "GLSL.std.450"
+ %2 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %in_var_TEXCOORD0 %out_var_SV_TARGET %textureposition %samplerposition %textureNormal %samplerNormal %textureAlbedo %samplerAlbedo %textureShadowMap %samplerShadowMap %ubo
+ OpExecutionMode %main OriginUpperLeft
+ %15 = OpString "d2.frag"
+ %55 = OpString "float"
+ %63 = OpString "// Copyright 2020 Google LLC
+
+Texture2D textureposition : register(t1);
+SamplerState samplerposition : register(s1);
+Texture2D textureNormal : register(t2);
+SamplerState samplerNormal : register(s2);
+Texture2D textureAlbedo : register(t3);
+SamplerState samplerAlbedo : register(s3);
+// Depth from the light's point of view
+//layout (binding = 5) uniform sampler2DShadow samplerShadowMap;
+Texture2DArray textureShadowMap : register(t5);
+SamplerState samplerShadowMap : register(s5);
+
+#define LIGHT_COUNT 3
+#define SHADOW_FACTOR 0.25
+#define AMBIENT_LIGHT 0.1
+#define USE_PCF
+
+struct Light
+{
+ float4 position;
+ float4 target;
+ float4 color;
+ float4x4 viewMatrix;
+};
+
+struct UBO
+{
+ float4 viewPos;
+ Light lights[LIGHT_COUNT];
+ int useShadows;
+ int displayDebugTarget;
+};
+
+cbuffer ubo : register(b4) { UBO ubo; }
+
+float textureProj(float4 P, float layer, float2 offset)
+{
+ float shadow = 1.0;
+ float4 shadowCoord = P / P.w;
+ shadowCoord.xy = shadowCoord.xy * 0.5 + 0.5;
+
+ if (shadowCoord.z > -1.0 && shadowCoord.z < 1.0)
+ {
+ float dist = textureShadowMap.Sample(samplerShadowMap, float3(shadowCoord.xy + offset, layer)).r;
+ if (shadowCoord.w > 0.0 && dist < shadowCoord.z)
+ {
+ shadow = SHADOW_FACTOR;
+ }
+ }
+ return shadow;
+}
+
+float filterPCF(float4 sc, float layer)
+{
+ int2 texDim; int elements; int levels;
+ textureShadowMap.GetDimensions(0, texDim.x, texDim.y, elements, levels);
+ float scale = 1.5;
+ float dx = scale * 1.0 / float(texDim.x);
+ float dy = scale * 1.0 / float(texDim.y);
+
+ float shadowFactor = 0.0;
+ int count = 0;
+ int range = 1;
+
+ for (int x = -range; x <= range; x++)
+ {
+ for (int y = -range; y <= range; y++)
+ {
+ shadowFactor += textureProj(sc, layer, float2(dx*x, dy*y));
+ count++;
+ }
+
+ }
+ return shadowFactor / count;
+}
+
+float3 shadow(float3 fragcolor, float3 fragPos) {
+ for (int i = 0; i < LIGHT_COUNT; ++i)
+ {
+ float4 shadowClip = mul(ubo.lights[i].viewMatrix, float4(fragPos.xyz, 1.0));
+
+ float shadowFactor;
+ #ifdef USE_PCF
+ shadowFactor= filterPCF(shadowClip, i);
+ #else
+ shadowFactor = textureProj(shadowClip, i, float2(0.0, 0.0));
+ #endif
+
+ fragcolor *= shadowFactor;
+ }
+ return fragcolor;
+}
+
+float4 main([[vk::location(0)]] float2 inUV : TEXCOORD0) : SV_TARGET
+{
+ // Get G-Buffer values
+ float3 fragPos = textureposition.Sample(samplerposition, inUV).rgb;
+ float3 normal = textureNormal.Sample(samplerNormal, inUV).rgb;
+ float4 albedo = textureAlbedo.Sample(samplerAlbedo, inUV);
+
+ // Ambient part
+ float3 fragcolor = albedo.rgb * AMBIENT_LIGHT;
+
+ float3 N = normalize(normal);
+
+ for(int i = 0; i < LIGHT_COUNT; ++i)
+ {
+ // Vector to light
+ float3 L = ubo.lights[i].position.xyz - fragPos;
+ // Distance from light to fragment position
+ float dist = length(L);
+ L = normalize(L);
+
+ // Viewer to fragment
+ float3 V = ubo.viewPos.xyz - fragPos;
+ V = normalize(V);
+
+ float lightCosInnerAngle = cos(radians(15.0));
+ float lightCosOuterAngle = cos(radians(25.0));
+ float lightRange = 100.0;
+
+ // Direction vector from source to target
+ float3 dir = normalize(ubo.lights[i].position.xyz - ubo.lights[i].target.xyz);
+
+ // Dual cone spot light with smooth transition between inner and outer angle
+ float cosDir = dot(L, dir);
+ float spotEffect = smoothstep(lightCosOuterAngle, lightCosInnerAngle, cosDir);
+ float heightAttenuation = smoothstep(lightRange, 0.0f, dist);
+
+ // Diffuse lighting
+ float NdotL = max(0.0, dot(N, L));
+ float3 diff = NdotL.xxx;
+
+ // Specular lighting
+ float3 R = reflect(-L, N);
+ float NdotR = max(0.0, dot(R, V));
+ float3 spec = (pow(NdotR, 16.0) * albedo.a * 2.5).xxx;
+
+ fragcolor += float3((diff + spec) * spotEffect * heightAttenuation) * ubo.lights[i].color.rgb * albedo.rgb;
+ }
+
+ // Shadow calculations in a separate pass
+ if (ubo.useShadows > 0)
+ {
+ fragcolor = shadow(fragcolor, fragPos);
+ }
+
+ return float4(fragcolor, 1);
+}
+"
+ %68 = OpString "textureProj"
+ %69 = OpString ""
+ %78 = OpString "dist"
+ %82 = OpString "shadowCoord"
+ %85 = OpString "shadow"
+ %89 = OpString "offset"
+ %92 = OpString "layer"
+ %95 = OpString "P"
+ %99 = OpString "filterPCF"
+ %108 = OpString "int"
+ %110 = OpString "y"
+ %114 = OpString "x"
+ %118 = OpString "range"
+ %122 = OpString "count"
+ %125 = OpString "shadowFactor"
+ %128 = OpString "dy"
+ %131 = OpString "dx"
+ %134 = OpString "scale"
+ %137 = OpString "levels"
+ %141 = OpString "elements"
+ %145 = OpString "texDim"
+ %150 = OpString "sc"
+ %162 = OpString "shadowClip"
+ %166 = OpString "i"
+ %169 = OpString "fragPos"
+ %171 = OpString "fragcolor"
+ %175 = OpString "main"
+ %184 = OpString "spec"
+ %187 = OpString "NdotR"
+ %190 = OpString "R"
+ %193 = OpString "diff"
+ %196 = OpString "NdotL"
+ %199 = OpString "heightAttenuation"
+ %202 = OpString "spotEffect"
+ %205 = OpString "cosDir"
+ %208 = OpString "dir"
+ %211 = OpString "lightRange"
+ %214 = OpString "lightCosOuterAngle"
+ %217 = OpString "lightCosInnerAngle"
+ %220 = OpString "V"
+ %225 = OpString "L"
+ %230 = OpString "N"
+ %235 = OpString "albedo"
+ %238 = OpString "normal"
+ %244 = OpString "inUV"
+ %246 = OpString "viewPos"
+ %249 = OpString "position"
+ %252 = OpString "target"
+ %254 = OpString "color"
+ %259 = OpString "viewMatrix"
+ %263 = OpString "Light"
+ %267 = OpString "lights"
+ %271 = OpString "useShadows"
+ %275 = OpString "displayDebugTarget"
+ %278 = OpString "UBO"
+ %282 = OpString "ubo"
+ %285 = OpString "type.ubo"
+ %289 = OpString "@type.sampler"
+ %290 = OpString "type.sampler"
+ %292 = OpString "samplerShadowMap"
+ %295 = OpString "@type.2d.image.array"
+ %296 = OpString "type.2d.image.array"
+ %298 = OpString "TemplateParam"
+ %301 = OpString "textureShadowMap"
+ %304 = OpString "samplerAlbedo"
+ %306 = OpString "@type.2d.image"
+ %307 = OpString "type.2d.image"
+ %311 = OpString "textureAlbedo"
+ %313 = OpString "samplerNormal"
+ %315 = OpString "textureNormal"
+ %317 = OpString "samplerposition"
+ %319 = OpString "textureposition"
+ OpName %type_2d_image "type.2d.image"
+ OpName %textureposition "textureposition"
+ OpName %type_sampler "type.sampler"
+ OpName %samplerposition "samplerposition"
+ OpName %textureNormal "textureNormal"
+ OpName %samplerNormal "samplerNormal"
+ OpName %textureAlbedo "textureAlbedo"
+ OpName %samplerAlbedo "samplerAlbedo"
+ OpName %type_2d_image_array "type.2d.image.array"
+ OpName %textureShadowMap "textureShadowMap"
+ OpName %samplerShadowMap "samplerShadowMap"
+ OpName %type_ubo "type.ubo"
+ OpMemberName %type_ubo 0 "ubo"
+ OpName %UBO "UBO"
+ OpMemberName %UBO 0 "viewPos"
+ OpMemberName %UBO 1 "lights"
+ OpMemberName %UBO 2 "useShadows"
+ OpMemberName %UBO 3 "displayDebugTarget"
+ OpName %Light "Light"
+ OpMemberName %Light 0 "position"
+ OpMemberName %Light 1 "target"
+ OpMemberName %Light 2 "color"
+ OpMemberName %Light 3 "viewMatrix"
+ OpName %ubo "ubo"
+ OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0"
+ OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+ OpName %main "main"
+ OpName %param_var_inUV "param.var.inUV"
+ OpName %type_sampled_image "type.sampled.image"
+ OpName %type_sampled_image_0 "type.sampled.image"
+ OpDecorate %in_var_TEXCOORD0 Location 0
+ OpDecorate %out_var_SV_TARGET Location 0
+ OpDecorate %textureposition DescriptorSet 0
+ OpDecorate %textureposition Binding 1
+ OpDecorate %samplerposition DescriptorSet 0
+ OpDecorate %samplerposition Binding 1
+ OpDecorate %textureNormal DescriptorSet 0
+ OpDecorate %textureNormal Binding 2
+ OpDecorate %samplerNormal DescriptorSet 0
+ OpDecorate %samplerNormal Binding 2
+ OpDecorate %textureAlbedo DescriptorSet 0
+ OpDecorate %textureAlbedo Binding 3
+ OpDecorate %samplerAlbedo DescriptorSet 0
+ OpDecorate %samplerAlbedo Binding 3
+ OpDecorate %textureShadowMap DescriptorSet 0
+ OpDecorate %textureShadowMap Binding 5
+ OpDecorate %samplerShadowMap DescriptorSet 0
+ OpDecorate %samplerShadowMap Binding 5
+ OpDecorate %ubo DescriptorSet 0
+ OpDecorate %ubo Binding 4
+ OpMemberDecorate %Light 0 Offset 0
+ OpMemberDecorate %Light 1 Offset 16
+ OpMemberDecorate %Light 2 Offset 32
+ OpMemberDecorate %Light 3 Offset 48
+ OpMemberDecorate %Light 3 MatrixStride 16
+ OpMemberDecorate %Light 3 RowMajor
+ OpDecorate %_arr_Light_uint_3 ArrayStride 112
+ OpMemberDecorate %UBO 0 Offset 0
+ OpMemberDecorate %UBO 1 Offset 16
+ OpMemberDecorate %UBO 2 Offset 352
+ OpMemberDecorate %UBO 3 Offset 356
+ OpMemberDecorate %type_ubo 0 Offset 0
+ OpDecorate %type_ubo Block
+)"
+ R"( %float = OpTypeFloat 32
+%float_0_100000001 = OpConstant %float 0.100000001
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %int_3 = OpConstant %int 3
+ %int_1 = OpConstant %int 1
+ %float_15 = OpConstant %float 15
+ %float_25 = OpConstant %float 25
+ %float_100 = OpConstant %float 100
+ %float_0 = OpConstant %float 0
+ %float_16 = OpConstant %float 16
+ %float_2_5 = OpConstant %float 2.5
+ %int_2 = OpConstant %int 2
+ %float_1 = OpConstant %float 1
+ %uint = OpTypeInt 32 0
+ %uint_0 = OpConstant %uint 0
+ %float_1_5 = OpConstant %float 1.5
+ %float_0_5 = OpConstant %float 0.5
+ %v2float = OpTypeVector %float 2
+ %35 = OpConstantComposite %v2float %float_0_5 %float_0_5
+ %float_n1 = OpConstant %float -1
+ %float_0_25 = OpConstant %float 0.25
+ %uint_32 = OpConstant %uint 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+%type_2d_image_array = OpTypeImage %float 2D 2 1 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image_array = OpTypePointer UniformConstant %type_2d_image_array
+ %v4float = OpTypeVector %float 4
+ %uint_3 = OpConstant %uint 3
+%mat4v4float = OpTypeMatrix %v4float 4
+ %Light = OpTypeStruct %v4float %v4float %v4float %mat4v4float
+%_arr_Light_uint_3 = OpTypeArray %Light %uint_3
+ %UBO = OpTypeStruct %v4float %_arr_Light_uint_3 %int %int
+ %type_ubo = OpTypeStruct %UBO
+%_ptr_Uniform_type_ubo = OpTypePointer Uniform %type_ubo
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %uint_4 = OpConstant %uint 4
+ %uint_2 = OpConstant %uint 2
+ %uint_1 = OpConstant %uint 1
+ %uint_5 = OpConstant %uint 5
+ %uint_37 = OpConstant %uint 37
+ %uint_38 = OpConstant %uint 38
+ %uint_44 = OpConstant %uint 44
+ %uint_47 = OpConstant %uint 47
+ %uint_45 = OpConstant %uint 45
+ %uint_9 = OpConstant %uint 9
+ %uint_40 = OpConstant %uint 40
+ %uint_39 = OpConstant %uint 39
+ %uint_8 = OpConstant %uint 8
+ %uint_49 = OpConstant %uint 49
+ %uint_35 = OpConstant %uint 35
+ %uint_26 = OpConstant %uint 26
+ %uint_54 = OpConstant %uint 54
+ %uint_55 = OpConstant %uint 55
+ %uint_67 = OpConstant %uint 67
+ %uint_69 = OpConstant %uint 69
+ %uint_68 = OpConstant %uint 68
+ %uint_12 = OpConstant %uint 12
+ %uint_66 = OpConstant %uint 66
+ %uint_11 = OpConstant %uint 11
+ %uint_64 = OpConstant %uint 64
+ %uint_6 = OpConstant %uint 6
+ %uint_63 = OpConstant %uint 63
+ %uint_62 = OpConstant %uint 62
+ %uint_60 = OpConstant %uint 60
+ %uint_59 = OpConstant %uint 59
+ %uint_58 = OpConstant %uint 58
+ %uint_56 = OpConstant %uint 56
+ %uint_33 = OpConstant %uint 33
+ %uint_19 = OpConstant %uint 19
+ %uint_7 = OpConstant %uint 7
+ %uint_34 = OpConstant %uint 34
+ %uint_24 = OpConstant %uint 24
+ %uint_78 = OpConstant %uint 78
+ %uint_80 = OpConstant %uint 80
+ %uint_83 = OpConstant %uint 83
+ %uint_81 = OpConstant %uint 81
+ %uint_10 = OpConstant %uint 10
+ %uint_79 = OpConstant %uint 79
+ %uint_22 = OpConstant %uint 22
+ %uint_95 = OpConstant %uint 95
+ %uint_96 = OpConstant %uint 96
+ %uint_145 = OpConstant %uint 145
+ %uint_108 = OpConstant %uint 108
+ %uint_138 = OpConstant %uint 138
+ %uint_137 = OpConstant %uint 137
+ %uint_136 = OpConstant %uint 136
+ %uint_133 = OpConstant %uint 133
+ %uint_132 = OpConstant %uint 132
+ %uint_129 = OpConstant %uint 129
+ %uint_128 = OpConstant %uint 128
+ %uint_127 = OpConstant %uint 127
+ %uint_124 = OpConstant %uint 124
+ %uint_121 = OpConstant %uint 121
+ %uint_120 = OpConstant %uint 120
+ %uint_119 = OpConstant %uint 119
+ %uint_116 = OpConstant %uint 116
+ %uint_112 = OpConstant %uint 112
+ %uint_110 = OpConstant %uint 110
+ %uint_107 = OpConstant %uint 107
+ %uint_105 = OpConstant %uint 105
+ %uint_103 = OpConstant %uint 103
+ %uint_100 = OpConstant %uint 100
+ %uint_99 = OpConstant %uint 99
+ %uint_98 = OpConstant %uint 98
+ %uint_29 = OpConstant %uint 29
+ %uint_21 = OpConstant %uint 21
+ %uint_256 = OpConstant %uint 256
+ %uint_23 = OpConstant %uint 23
+ %uint_384 = OpConstant %uint 384
+ %uint_512 = OpConstant %uint 512
+ %uint_896 = OpConstant %uint 896
+ %uint_2688 = OpConstant %uint 2688
+ %uint_30 = OpConstant %uint 30
+ %uint_2816 = OpConstant %uint 2816
+ %uint_31 = OpConstant %uint 31
+ %uint_2848 = OpConstant %uint 2848
+ %uint_2880 = OpConstant %uint 2880
+ %uint_27 = OpConstant %uint 27
+ %uint_2944 = OpConstant %uint 2944
+ %uint_14 = OpConstant %uint 14
+ %uint_16 = OpConstant %uint 16
+ %321 = OpTypeFunction %void
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+ %uint_150 = OpConstant %uint 150
+ %v3float = OpTypeVector %float 3
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Function_int = OpTypePointer Function %int
+%_ptr_Function_float = OpTypePointer Function %float
+ %uint_42 = OpConstant %uint 42
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+ %uint_65 = OpConstant %uint 65
+ %uint_18 = OpConstant %uint 18
+ %uint_13 = OpConstant %uint 13
+ %uint_15 = OpConstant %uint 15
+ %uint_17 = OpConstant %uint 17
+ %bool = OpTypeBool
+ %uint_25 = OpConstant %uint 25
+%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+ %uint_28 = OpConstant %uint 28
+ %uint_43 = OpConstant %uint 43
+ %uint_113 = OpConstant %uint 113
+ %uint_117 = OpConstant %uint 117
+ %uint_46 = OpConstant %uint 46
+ %uint_76 = OpConstant %uint 76
+ %uint_53 = OpConstant %uint 53
+ %uint_73 = OpConstant %uint 73
+ %uint_48 = OpConstant %uint 48
+ %uint_140 = OpConstant %uint 140
+ %uint_52 = OpConstant %uint 52
+ %uint_93 = OpConstant %uint 93
+ %uint_87 = OpConstant %uint 87
+ %uint_106 = OpConstant %uint 106
+ %uint_36 = OpConstant %uint 36
+ %uint_144 = OpConstant %uint 144
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+ %uint_146 = OpConstant %uint 146
+ %uint_147 = OpConstant %uint 147
+ %uint_149 = OpConstant %uint 149
+ %uint_41 = OpConstant %uint 41
+%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float
+ %uint_77 = OpConstant %uint 77
+ %uint_85 = OpConstant %uint 85
+ %uint_90 = OpConstant %uint 90
+ %uint_92 = OpConstant %uint 92
+ %v2int = OpTypeVector %int 2
+%_ptr_Function_v2int = OpTypePointer Function %v2int
+ %uint_57 = OpConstant %uint 57
+ %v3uint = OpTypeVector %uint 3
+ %uint_72 = OpConstant %uint 72
+ %uint_70 = OpConstant %uint 70
+ %uint_50 = OpConstant %uint 50
+ %uint_61 = OpConstant %uint 61
+ %uint_71 = OpConstant %uint 71
+ %uint_75 = OpConstant %uint 75
+ %uint_82 = OpConstant %uint 82
+%type_sampled_image_0 = OpTypeSampledImage %type_2d_image_array
+ %uint_51 = OpConstant %uint 51
+%textureposition = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+%samplerposition = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%textureNormal = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+%samplerNormal = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%textureAlbedo = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+%samplerAlbedo = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%textureShadowMap = OpVariable %_ptr_UniformConstant_type_2d_image_array UniformConstant
+%samplerShadowMap = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+ %ubo = OpVariable %_ptr_Uniform_type_ubo Uniform
+%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+ %uint_1792 = OpConstant %uint 1792
+ %uint_1869 = OpConstant %uint 1869
+ %uint_2060 = OpConstant %uint 2060
+ %288 = OpExtInst %void %2 DebugInfoNone
+ %243 = OpExtInst %void %2 DebugExpression
+ %57 = OpExtInst %void %2 DebugTypeBasic %55 %uint_32 %uint_3 %uint_0
+ %58 = OpExtInst %void %2 DebugTypeVector %57 %uint_4
+ %60 = OpExtInst %void %2 DebugTypeVector %57 %uint_2
+ %62 = OpExtInst %void %2 DebugTypeFunction %uint_3 %57 %58 %57 %60
+ %64 = OpExtInst %void %2 DebugSource %15 %63
+ %65 = OpExtInst %void %2 DebugCompilationUnit %uint_1 %uint_4 %64 %uint_5
+ %70 = OpExtInst %void %2 DebugFunction %68 %62 %64 %uint_37 %uint_1 %65 %69 %uint_3 %uint_38
+ %73 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_38 %uint_1 %70
+ %74 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_44 %uint_2 %73
+ %76 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_47 %uint_3 %74
+ %79 = OpExtInst %void %2 DebugLocalVariable %78 %57 %64 %uint_45 %uint_9 %74 %uint_4
+ %83 = OpExtInst %void %2 DebugLocalVariable %82 %58 %64 %uint_40 %uint_9 %73 %uint_4
+ %86 = OpExtInst %void %2 DebugLocalVariable %85 %57 %64 %uint_39 %uint_8 %73 %uint_4
+ %90 = OpExtInst %void %2 DebugLocalVariable %89 %60 %64 %uint_37 %uint_49 %70 %uint_4 %uint_3
+ %93 = OpExtInst %void %2 DebugLocalVariable %92 %57 %64 %uint_37 %uint_35 %70 %uint_4 %uint_2
+ %96 = OpExtInst %void %2 DebugLocalVariable %95 %58 %64 %uint_37 %uint_26 %70 %uint_4 %uint_1
+ %98 = OpExtInst %void %2 DebugTypeFunction %uint_3 %57 %58 %57
+ %100 = OpExtInst %void %2 DebugFunction %99 %98 %64 %uint_54 %uint_1 %65 %69 %uint_3 %uint_55
+ %103 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_55 %uint_1 %100
+ %104 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_67 %uint_2 %103
+ %106 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_69 %uint_3 %104
+ %109 = OpExtInst %void %2 DebugTypeBasic %108 %uint_32 %uint_4 %uint_0
+ %111 = OpExtInst %void %2 DebugLocalVariable %110 %109 %64 %uint_68 %uint_12 %104 %uint_4
+ %115 = OpExtInst %void %2 DebugLocalVariable %114 %109 %64 %uint_66 %uint_11 %103 %uint_4
+ %119 = OpExtInst %void %2 DebugLocalVariable %118 %109 %64 %uint_64 %uint_6 %103 %uint_4
+ %123 = OpExtInst %void %2 DebugLocalVariable %122 %109 %64 %uint_63 %uint_6 %103 %uint_4
+ %126 = OpExtInst %void %2 DebugLocalVariable %125 %57 %64 %uint_62 %uint_8 %103 %uint_4
+ %129 = OpExtInst %void %2 DebugLocalVariable %128 %57 %64 %uint_60 %uint_8 %103 %uint_4
+ %132 = OpExtInst %void %2 DebugLocalVariable %131 %57 %64 %uint_59 %uint_8 %103 %uint_4
+ %135 = OpExtInst %void %2 DebugLocalVariable %134 %57 %64 %uint_58 %uint_8 %103 %uint_4
+ %138 = OpExtInst %void %2 DebugLocalVariable %137 %109 %64 %uint_56 %uint_33 %103 %uint_4
+ %142 = OpExtInst %void %2 DebugLocalVariable %141 %109 %64 %uint_56 %uint_19 %103 %uint_4
+ %144 = OpExtInst %void %2 DebugTypeVector %109 %uint_2
+ %146 = OpExtInst %void %2 DebugLocalVariable %145 %144 %64 %uint_56 %uint_7 %103 %uint_4
+ %148 = OpExtInst %void %2 DebugLocalVariable %92 %57 %64 %uint_54 %uint_34 %100 %uint_4 %uint_2
+ %151 = OpExtInst %void %2 DebugLocalVariable %150 %58 %64 %uint_54 %uint_24 %100 %uint_4 %uint_1
+ %153 = OpExtInst %void %2 DebugTypeVector %57 %uint_3
+ %154 = OpExtInst %void %2 DebugTypeFunction %uint_3 %153 %153 %153
+ %155 = OpExtInst %void %2 DebugFunction %85 %154 %64 %uint_78 %uint_1 %65 %69 %uint_3 %uint_78
+ %157 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_78 %uint_49 %155
+ %158 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_80 %uint_2 %157
+ %160 = OpExtInst %void %2 DebugLocalVariable %125 %57 %64 %uint_83 %uint_9 %158 %uint_4
+ %163 = OpExtInst %void %2 DebugLocalVariable %162 %58 %64 %uint_81 %uint_10 %158 %uint_4
+ %167 = OpExtInst %void %2 DebugLocalVariable %166 %109 %64 %uint_79 %uint_11 %157 %uint_4
+ %170 = OpExtInst %void %2 DebugLocalVariable %169 %153 %64 %uint_78 %uint_40 %155 %uint_4 %uint_2
+ %172 = OpExtInst %void %2 DebugLocalVariable %171 %153 %64 %uint_78 %uint_22 %155 %uint_4 %uint_1
+ %174 = OpExtInst %void %2 DebugTypeFunction %uint_3 %58 %60
+ %176 = OpExtInst %void %2 DebugFunction %175 %174 %64 %uint_95 %uint_1 %65 %69 %uint_3 %uint_96
+ %179 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_96 %uint_1 %176
+ %180 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_145 %uint_2 %179
+ %182 = OpExtInst %void %2 DebugLexicalBlock %64 %uint_108 %uint_2 %179
+ %185 = OpExtInst %void %2 DebugLocalVariable %184 %153 %64 %uint_138 %uint_10 %182 %uint_4
+ %188 = OpExtInst %void %2 DebugLocalVariable %187 %57 %64 %uint_137 %uint_9 %182 %uint_4
+ %191 = OpExtInst %void %2 DebugLocalVariable %190 %153 %64 %uint_136 %uint_10 %182 %uint_4
+ %194 = OpExtInst %void %2 DebugLocalVariable %193 %153 %64 %uint_133 %uint_10 %182 %uint_4
+ %197 = OpExtInst %void %2 DebugLocalVariable %196 %57 %64 %uint_132 %uint_9 %182 %uint_4
+ %200 = OpExtInst %void %2 DebugLocalVariable %199 %57 %64 %uint_129 %uint_9 %182 %uint_4
+ %203 = OpExtInst %void %2 DebugLocalVariable %202 %57 %64 %uint_128 %uint_9 %182 %uint_4
+ %206 = OpExtInst %void %2 DebugLocalVariable %205 %57 %64 %uint_127 %uint_9 %182 %uint_4
+ %209 = OpExtInst %void %2 DebugLocalVariable %208 %153 %64 %uint_124 %uint_10 %182 %uint_4
+ %212 = OpExtInst %void %2 DebugLocalVariable %211 %57 %64 %uint_121 %uint_9 %182 %uint_4
+ %215 = OpExtInst %void %2 DebugLocalVariable %214 %57 %64 %uint_120 %uint_9 %182 %uint_4
+ %218 = OpExtInst %void %2 DebugLocalVariable %217 %57 %64 %uint_119 %uint_9 %182 %uint_4
+ %221 = OpExtInst %void %2 DebugLocalVariable %220 %153 %64 %uint_116 %uint_10 %182 %uint_4
+ %223 = OpExtInst %void %2 DebugLocalVariable %78 %57 %64 %uint_112 %uint_9 %182 %uint_4
+ %226 = OpExtInst %void %2 DebugLocalVariable %225 %153 %64 %uint_110 %uint_10 %182 %uint_4
+ %228 = OpExtInst %void %2 DebugLocalVariable %166 %109 %64 %uint_107 %uint_10 %179 %uint_4
+ %231 = OpExtInst %void %2 DebugLocalVariable %230 %153 %64 %uint_105 %uint_9 %179 %uint_4
+ %233 = OpExtInst %void %2 DebugLocalVariable %171 %153 %64 %uint_103 %uint_9 %179 %uint_4
+ %236 = OpExtInst %void %2 DebugLocalVariable %235 %58 %64 %uint_100 %uint_9 %179 %uint_4
+ %239 = OpExtInst %void %2 DebugLocalVariable %238 %153 %64 %uint_99 %uint_9 %179 %uint_4
+ %241 = OpExtInst %void %2 DebugLocalVariable %169 %153 %64 %uint_98 %uint_9 %179 %uint_4
+ %245 = OpExtInst %void %2 DebugLocalVariable %244 %60 %64 %uint_95 %uint_40 %176 %uint_4 %uint_1
+)"
+ R"( %247 = OpExtInst %void %2 DebugTypeMember %246 %58 %64 %uint_29 %uint_9 %uint_0 %uint_128 %uint_3
+ %250 = OpExtInst %void %2 DebugTypeMember %249 %58 %64 %uint_21 %uint_9 %uint_0 %uint_128 %uint_3
+ %253 = OpExtInst %void %2 DebugTypeMember %252 %58 %64 %uint_22 %uint_9 %uint_128 %uint_128 %uint_3
+ %256 = OpExtInst %void %2 DebugTypeMember %254 %58 %64 %uint_23 %uint_9 %uint_256 %uint_128 %uint_3
+ %258 = OpExtInst %void %2 DebugTypeArray %57 %uint_4 %uint_4
+ %262 = OpExtInst %void %2 DebugTypeMember %259 %258 %64 %uint_24 %uint_11 %uint_384 %uint_512 %uint_3
+ %265 = OpExtInst %void %2 DebugTypeComposite %263 %uint_1 %64 %uint_19 %uint_8 %65 %263 %uint_896 %uint_3 %250 %253 %256 %262
+ %266 = OpExtInst %void %2 DebugTypeArray %265 %uint_3
+ %269 = OpExtInst %void %2 DebugTypeMember %267 %266 %64 %uint_30 %uint_8 %uint_128 %uint_2688 %uint_3
+ %273 = OpExtInst %void %2 DebugTypeMember %271 %109 %64 %uint_31 %uint_6 %uint_2816 %uint_32 %uint_3
+ %277 = OpExtInst %void %2 DebugTypeMember %275 %109 %64 %uint_32 %uint_6 %uint_2848 %uint_32 %uint_3
+ %280 = OpExtInst %void %2 DebugTypeComposite %278 %uint_1 %64 %uint_27 %uint_8 %65 %278 %uint_2880 %uint_3 %247 %269 %273 %277
+ %284 = OpExtInst %void %2 DebugTypeMember %282 %280 %64 %uint_35 %uint_34 %uint_0 %uint_2944 %uint_3
+ %286 = OpExtInst %void %2 DebugTypeComposite %285 %uint_1 %64 %uint_35 %uint_9 %65 %285 %uint_2944 %uint_3 %284
+ %287 = OpExtInst %void %2 DebugGlobalVariable %282 %286 %64 %uint_35 %uint_9 %65 %282 %ubo %uint_8
+ %291 = OpExtInst %void %2 DebugTypeComposite %289 %uint_1 %64 %uint_0 %uint_0 %65 %290 %288 %uint_3
+ %293 = OpExtInst %void %2 DebugGlobalVariable %292 %291 %64 %uint_12 %uint_14 %65 %292 %samplerShadowMap %uint_8
+ %297 = OpExtInst %void %2 DebugTypeComposite %295 %uint_0 %64 %uint_0 %uint_0 %65 %296 %288 %uint_3
+ %299 = OpExtInst %void %2 DebugTypeTemplateParameter %298 %58 %288 %64 %uint_0 %uint_0
+ %300 = OpExtInst %void %2 DebugTypeTemplate %297 %299
+ %302 = OpExtInst %void %2 DebugGlobalVariable %301 %300 %64 %uint_11 %uint_16 %65 %301 %textureShadowMap %uint_8
+ %305 = OpExtInst %void %2 DebugGlobalVariable %304 %291 %64 %uint_8 %uint_14 %65 %304 %samplerAlbedo %uint_8
+ %308 = OpExtInst %void %2 DebugTypeComposite %306 %uint_0 %64 %uint_0 %uint_0 %65 %307 %288 %uint_3
+ %309 = OpExtInst %void %2 DebugTypeTemplateParameter %298 %58 %288 %64 %uint_0 %uint_0
+ %310 = OpExtInst %void %2 DebugTypeTemplate %308 %309
+ %312 = OpExtInst %void %2 DebugGlobalVariable %311 %310 %64 %uint_7 %uint_11 %65 %311 %textureAlbedo %uint_8
+ %314 = OpExtInst %void %2 DebugGlobalVariable %313 %291 %64 %uint_6 %uint_14 %65 %313 %samplerNormal %uint_8
+ %316 = OpExtInst %void %2 DebugGlobalVariable %315 %310 %64 %uint_5 %uint_11 %65 %315 %textureNormal %uint_8
+ %318 = OpExtInst %void %2 DebugGlobalVariable %317 %291 %64 %uint_4 %uint_14 %65 %317 %samplerposition %uint_8
+ %320 = OpExtInst %void %2 DebugGlobalVariable %319 %310 %64 %uint_3 %uint_11 %65 %319 %textureposition %uint_8
+ %1803 = OpExtInst %void %2 DebugInlinedAt %uint_1792 %180
+ %1885 = OpExtInst %void %2 DebugInlinedAt %uint_1869 %158 %1803
+ %2085 = OpExtInst %void %2 DebugInlinedAt %uint_2060 %106 %1885
+)"
+ R"( %main = OpFunction %void None %321
+ %322 = OpLabel
+ %2083 = OpVariable %_ptr_Function_float Function
+ %2086 = OpVariable %_ptr_Function_v4float Function
+ %1883 = OpVariable %_ptr_Function_v2int Function
+ %1891 = OpVariable %_ptr_Function_float Function
+ %1892 = OpVariable %_ptr_Function_int Function
+ %1894 = OpVariable %_ptr_Function_int Function
+ %1895 = OpVariable %_ptr_Function_int Function
+ %1896 = OpVariable %_ptr_Function_v4float Function
+ %1801 = OpVariable %_ptr_Function_int Function
+ %1447 = OpVariable %_ptr_Function_v4float Function
+ %1448 = OpVariable %_ptr_Function_v3float Function
+ %1450 = OpVariable %_ptr_Function_int Function
+ %1451 = OpVariable %_ptr_Function_v3float Function
+ %1453 = OpVariable %_ptr_Function_v3float Function
+ %1466 = OpVariable %_ptr_Function_v3float Function
+%param_var_inUV = OpVariable %_ptr_Function_v2float Function
+ %325 = OpExtInst %void %2 DebugFunctionDefinition %176 %main
+ %326 = OpLoad %v2float %in_var_TEXCOORD0
+ OpStore %param_var_inUV %326
+ %2290 = OpExtInst %void %2 DebugScope %176
+ %1620 = OpExtInst %void %2 DebugLine %64 %uint_95 %uint_95 %uint_33 %uint_40
+ %1470 = OpExtInst %void %2 DebugDeclare %245 %param_var_inUV %243
+ %2291 = OpExtInst %void %2 DebugScope %179
+ %1621 = OpExtInst %void %2 DebugLine %64 %uint_98 %uint_98 %uint_19 %uint_19
+ %1471 = OpLoad %type_2d_image %textureposition
+ %1622 = OpExtInst %void %2 DebugLine %64 %uint_98 %uint_98 %uint_42 %uint_42
+ %1472 = OpLoad %type_sampler %samplerposition
+ %1624 = OpExtInst %void %2 DebugLine %64 %uint_98 %uint_98 %uint_19 %uint_63
+ %1474 = OpSampledImage %type_sampled_image %1471 %1472
+ %1475 = OpImageSampleImplicitLod %v4float %1474 %326 None
+ %1626 = OpExtInst %void %2 DebugLine %64 %uint_98 %uint_98 %uint_19 %uint_65
+ %1476 = OpVectorShuffle %v3float %1475 %1475 0 1 2
+ %2241 = OpExtInst %void %2 DebugLine %64 %uint_98 %uint_98 %uint_2 %uint_65
+ %2240 = OpExtInst %void %2 DebugValue %241 %1476 %243
+ %1629 = OpExtInst %void %2 DebugLine %64 %uint_99 %uint_99 %uint_18 %uint_18
+ %1478 = OpLoad %type_2d_image %textureNormal
+ %1630 = OpExtInst %void %2 DebugLine %64 %uint_99 %uint_99 %uint_39 %uint_39
+ %1479 = OpLoad %type_sampler %samplerNormal
+ %1632 = OpExtInst %void %2 DebugLine %64 %uint_99 %uint_99 %uint_18 %uint_58
+ %1481 = OpSampledImage %type_sampled_image %1478 %1479
+ %1482 = OpImageSampleImplicitLod %v4float %1481 %326 None
+ %1634 = OpExtInst %void %2 DebugLine %64 %uint_99 %uint_99 %uint_18 %uint_60
+ %1483 = OpVectorShuffle %v3float %1482 %1482 0 1 2
+ %2244 = OpExtInst %void %2 DebugLine %64 %uint_99 %uint_99 %uint_2 %uint_60
+ %2243 = OpExtInst %void %2 DebugValue %239 %1483 %243
+ %1637 = OpExtInst %void %2 DebugLine %64 %uint_100 %uint_100 %uint_18 %uint_18
+ %1485 = OpLoad %type_2d_image %textureAlbedo
+ %1638 = OpExtInst %void %2 DebugLine %64 %uint_100 %uint_100 %uint_39 %uint_39
+ %1486 = OpLoad %type_sampler %samplerAlbedo
+ %1640 = OpExtInst %void %2 DebugLine %64 %uint_100 %uint_100 %uint_18 %uint_58
+ %1488 = OpSampledImage %type_sampled_image %1485 %1486
+ %1489 = OpImageSampleImplicitLod %v4float %1488 %326 None
+ %1642 = OpExtInst %void %2 DebugLine %64 %uint_100 %uint_100 %uint_2 %uint_58
+ OpStore %1447 %1489
+ %1490 = OpExtInst %void %2 DebugDeclare %236 %1447 %243
+ %1645 = OpExtInst %void %2 DebugLine %64 %uint_103 %uint_103 %uint_22 %uint_29
+ %1492 = OpVectorShuffle %v3float %1489 %1489 0 1 2
+ %1646 = OpExtInst %void %2 DebugLine %64 %uint_103 %uint_103 %uint_22 %uint_35
+ %1493 = OpVectorTimesScalar %v3float %1492 %float_0_100000001
+ %1647 = OpExtInst %void %2 DebugLine %64 %uint_103 %uint_103 %uint_2 %uint_35
+ OpStore %1448 %1493
+ %1494 = OpExtInst %void %2 DebugDeclare %233 %1448 %243
+ %1650 = OpExtInst %void %2 DebugLine %64 %uint_105 %uint_105 %uint_13 %uint_29
+ %1496 = OpExtInst %v3float %1 Normalize %1483
+ %2247 = OpExtInst %void %2 DebugLine %64 %uint_105 %uint_105 %uint_2 %uint_29
+ %2246 = OpExtInst %void %2 DebugValue %231 %1496 %243
+ %1653 = OpExtInst %void %2 DebugLine %64 %uint_107 %uint_107 %uint_6 %uint_14
+ OpStore %1450 %int_0
+ %1498 = OpExtInst %void %2 DebugDeclare %228 %1450 %243
+ %1655 = OpExtInst %void %2 DebugLine %64 %uint_107 %uint_107 %uint_6 %uint_15
+ OpBranch %1499
+ %1499 = OpLabel
+ %2292 = OpExtInst %void %2 DebugScope %179
+ %1656 = OpExtInst %void %2 DebugLine %64 %uint_107 %uint_107 %uint_17 %uint_17
+ %1500 = OpLoad %int %1450
+ %1657 = OpExtInst %void %2 DebugLine %64 %uint_107 %uint_107 %uint_17 %uint_21
+ %1501 = OpSLessThan %bool %1500 %int_3
+ %2293 = OpExtInst %void %2 DebugNoScope
+ OpLoopMerge %1605 %1602 None
+ OpBranchConditional %1501 %1502 %1605
+ %1502 = OpLabel
+ %2294 = OpExtInst %void %2 DebugScope %182
+ %1660 = OpExtInst %void %2 DebugLine %64 %uint_110 %uint_110 %uint_25 %uint_25
+ %1503 = OpLoad %int %1450
+ %1661 = OpExtInst %void %2 DebugLine %64 %uint_110 %uint_110 %uint_14 %uint_37
+ %1504 = OpAccessChain %_ptr_Uniform_UBO %ubo %int_0
+ %1505 = OpAccessChain %_ptr_Uniform_v4float %1504 %int_1 %1503 %int_0
+ %1663 = OpExtInst %void %2 DebugLine %64 %uint_110 %uint_110 %uint_14 %uint_28
+ %1506 = OpLoad %v4float %1505
+ %1664 = OpExtInst %void %2 DebugLine %64 %uint_110 %uint_110 %uint_14 %uint_37
+ %1507 = OpVectorShuffle %v3float %1506 %1506 0 1 2
+ %1666 = OpExtInst %void %2 DebugLine %64 %uint_110 %uint_110 %uint_14 %uint_43
+ %1509 = OpFSub %v3float %1507 %1476
+ %1667 = OpExtInst %void %2 DebugLine %64 %uint_110 %uint_110 %uint_3 %uint_43
+ OpStore %1451 %1509
+ %1510 = OpExtInst %void %2 DebugDeclare %226 %1451 %243
+ %1670 = OpExtInst %void %2 DebugLine %64 %uint_112 %uint_112 %uint_16 %uint_24
+ %1512 = OpExtInst %float %1 Length %1509
+ %2250 = OpExtInst %void %2 DebugLine %64 %uint_112 %uint_112 %uint_3 %uint_24
+ %2249 = OpExtInst %void %2 DebugValue %223 %1512 %243
+ %1674 = OpExtInst %void %2 DebugLine %64 %uint_113 %uint_113 %uint_7 %uint_18
+ %1515 = OpExtInst %v3float %1 Normalize %1509
+ %1675 = OpExtInst %void %2 DebugLine %64 %uint_113 %uint_113 %uint_3 %uint_18
+ OpStore %1451 %1515
+ %1676 = OpExtInst %void %2 DebugLine %64 %uint_116 %uint_116 %uint_14 %uint_26
+ %1516 = OpAccessChain %_ptr_Uniform_UBO %ubo %int_0
+ %1517 = OpAccessChain %_ptr_Uniform_v4float %1516 %int_0
+ %1678 = OpExtInst %void %2 DebugLine %64 %uint_116 %uint_116 %uint_14 %uint_18
+ %1518 = OpLoad %v4float %1517
+ %1679 = OpExtInst %void %2 DebugLine %64 %uint_116 %uint_116 %uint_14 %uint_26
+ %1519 = OpVectorShuffle %v3float %1518 %1518 0 1 2
+ %1681 = OpExtInst %void %2 DebugLine %64 %uint_116 %uint_116 %uint_14 %uint_32
+ %1521 = OpFSub %v3float %1519 %1476
+ %1682 = OpExtInst %void %2 DebugLine %64 %uint_116 %uint_116 %uint_3 %uint_32
+ OpStore %1453 %1521
+ %1522 = OpExtInst %void %2 DebugDeclare %221 %1453 %243
+ %1685 = OpExtInst %void %2 DebugLine %64 %uint_117 %uint_117 %uint_7 %uint_18
+ %1524 = OpExtInst %v3float %1 Normalize %1521
+ %1686 = OpExtInst %void %2 DebugLine %64 %uint_117 %uint_117 %uint_3 %uint_18
+ OpStore %1453 %1524
+ %1687 = OpExtInst %void %2 DebugLine %64 %uint_119 %uint_119 %uint_34 %uint_46
+ %1525 = OpExtInst %float %1 Radians %float_15
+ %1688 = OpExtInst %void %2 DebugLine %64 %uint_119 %uint_119 %uint_30 %uint_47
+ %1526 = OpExtInst %float %1 Cos %1525
+ %2253 = OpExtInst %void %2 DebugLine %64 %uint_119 %uint_119 %uint_3 %uint_47
+ %2252 = OpExtInst %void %2 DebugValue %218 %1526 %243
+ %1691 = OpExtInst %void %2 DebugLine %64 %uint_120 %uint_120 %uint_34 %uint_46
+ %1528 = OpExtInst %float %1 Radians %float_25
+ %1692 = OpExtInst %void %2 DebugLine %64 %uint_120 %uint_120 %uint_30 %uint_47
+ %1529 = OpExtInst %float %1 Cos %1528
+ %2256 = OpExtInst %void %2 DebugLine %64 %uint_120 %uint_120 %uint_3 %uint_47
+ %2255 = OpExtInst %void %2 DebugValue %215 %1529 %243
+ %2259 = OpExtInst %void %2 DebugLine %64 %uint_121 %uint_121 %uint_3 %uint_22
+ %2258 = OpExtInst %void %2 DebugValue %212 %float_100 %243
+ %1698 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_26 %uint_49
+ %1533 = OpAccessChain %_ptr_Uniform_UBO %ubo %int_0
+ %1534 = OpAccessChain %_ptr_Uniform_v4float %1533 %int_1 %1503 %int_0
+ %1700 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_26 %uint_40
+ %1535 = OpLoad %v4float %1534
+ %1701 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_26 %uint_49
+ %1536 = OpVectorShuffle %v3float %1535 %1535 0 1 2
+ %1703 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_55 %uint_76
+ %1538 = OpAccessChain %_ptr_Uniform_UBO %ubo %int_0
+ %1539 = OpAccessChain %_ptr_Uniform_v4float %1538 %int_1 %1503 %int_1
+ %1705 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_55 %uint_69
+ %1540 = OpLoad %v4float %1539
+ %1706 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_55 %uint_76
+ %1541 = OpVectorShuffle %v3float %1540 %1540 0 1 2
+ %1707 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_26 %uint_76
+ %1542 = OpFSub %v3float %1536 %1541
+ %1708 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_16 %uint_79
+ %1543 = OpExtInst %v3float %1 Normalize %1542
+ %2262 = OpExtInst %void %2 DebugLine %64 %uint_124 %uint_124 %uint_3 %uint_79
+ %2261 = OpExtInst %void %2 DebugValue %209 %1543 %243
+ %1713 = OpExtInst %void %2 DebugLine %64 %uint_127 %uint_127 %uint_18 %uint_28
+ %1547 = OpDot %float %1515 %1543
+ %2265 = OpExtInst %void %2 DebugLine %64 %uint_127 %uint_127 %uint_3 %uint_28
+ %2264 = OpExtInst %void %2 DebugValue %206 %1547 %243
+ %1719 = OpExtInst %void %2 DebugLine %64 %uint_128 %uint_128 %uint_22 %uint_79
+ %1552 = OpExtInst %float %1 SmoothStep %1529 %1526 %1547
+ %2268 = OpExtInst %void %2 DebugLine %64 %uint_128 %uint_128 %uint_3 %uint_79
+ %2267 = OpExtInst %void %2 DebugValue %203 %1552 %243
+ %1724 = OpExtInst %void %2 DebugLine %64 %uint_129 %uint_129 %uint_29 %uint_62
+ %1556 = OpExtInst %float %1 SmoothStep %float_100 %float_0 %1512
+ %2271 = OpExtInst %void %2 DebugLine %64 %uint_129 %uint_129 %uint_3 %uint_62
+ %2270 = OpExtInst %void %2 DebugValue %200 %1556 %243
+ %1729 = OpExtInst %void %2 DebugLine %64 %uint_132 %uint_132 %uint_26 %uint_34
+ %1560 = OpDot %float %1496 %1515
+ %1730 = OpExtInst %void %2 DebugLine %64 %uint_132 %uint_132 %uint_17 %uint_35
+ %1561 = OpExtInst %float %1 FMax %float_0 %1560
+ %2274 = OpExtInst %void %2 DebugLine %64 %uint_132 %uint_132 %uint_3 %uint_35
+ %2273 = OpExtInst %void %2 DebugValue %197 %1561 %243
+ %1734 = OpExtInst %void %2 DebugLine %64 %uint_133 %uint_133 %uint_17 %uint_23
+ %1564 = OpCompositeConstruct %v3float %1561 %1561 %1561
+ %2277 = OpExtInst %void %2 DebugLine %64 %uint_133 %uint_133 %uint_3 %uint_23
+ %2276 = OpExtInst %void %2 DebugValue %194 %1564 %243
+ %1738 = OpExtInst %void %2 DebugLine %64 %uint_136 %uint_136 %uint_22 %uint_23
+ %1567 = OpFNegate %v3float %1515
+ %1740 = OpExtInst %void %2 DebugLine %64 %uint_136 %uint_136 %uint_14 %uint_27
+ %1569 = OpExtInst %v3float %1 Reflect %1567 %1496
+ %2280 = OpExtInst %void %2 DebugLine %64 %uint_136 %uint_136 %uint_3 %uint_27
+ %2279 = OpExtInst %void %2 DebugValue %191 %1569 %243
+ %1745 = OpExtInst %void %2 DebugLine %64 %uint_137 %uint_137 %uint_26 %uint_34
+ %1573 = OpDot %float %1569 %1524
+ %1746 = OpExtInst %void %2 DebugLine %64 %uint_137 %uint_137 %uint_17 %uint_35
+ %1574 = OpExtInst %float %1 FMax %float_0 %1573
+ %2283 = OpExtInst %void %2 DebugLine %64 %uint_137 %uint_137 %uint_3 %uint_35
+ %2282 = OpExtInst %void %2 DebugValue %188 %1574 %243
+ %1750 = OpExtInst %void %2 DebugLine %64 %uint_138 %uint_138 %uint_18 %uint_33
+ %1577 = OpExtInst %float %1 Pow %1574 %float_16
+ %1751 = OpExtInst %void %2 DebugLine %64 %uint_138 %uint_138 %uint_37 %uint_44
+ %1578 = OpAccessChain %_ptr_Function_float %1447 %int_3
+ %1579 = OpLoad %float %1578
+ %1753 = OpExtInst %void %2 DebugLine %64 %uint_138 %uint_138 %uint_18 %uint_44
+ %1580 = OpFMul %float %1577 %1579
+ %1754 = OpExtInst %void %2 DebugLine %64 %uint_138 %uint_138 %uint_18 %uint_48
+ %1581 = OpFMul %float %1580 %float_2_5
+ %1755 = OpExtInst %void %2 DebugLine %64 %uint_138 %uint_138 %uint_17 %uint_53
+ %1582 = OpCompositeConstruct %v3float %1581 %1581 %1581
+ %2286 = OpExtInst %void %2 DebugLine %64 %uint_138 %uint_138 %uint_3 %uint_53
+ %2285 = OpExtInst %void %2 DebugValue %185 %1582 %243
+ %1760 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_24 %uint_31
+ %1586 = OpFAdd %v3float %1564 %1582
+ %1762 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_23 %uint_39
+ %1588 = OpVectorTimesScalar %v3float %1586 %1552
+ %1764 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_23 %uint_52
+ %1590 = OpVectorTimesScalar %v3float %1588 %1556
+ %1766 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_73 %uint_93
+ %1592 = OpAccessChain %_ptr_Uniform_UBO %ubo %int_0
+ %1593 = OpAccessChain %_ptr_Uniform_v4float %1592 %int_1 %1503 %int_2
+ %1768 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_73 %uint_87
+ %1594 = OpLoad %v4float %1593
+ %1769 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_73 %uint_93
+ %1595 = OpVectorShuffle %v3float %1594 %1594 0 1 2
+ %1770 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_16 %uint_93
+ %1596 = OpFMul %v3float %1590 %1595
+ %1772 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_99 %uint_106
+ %1598 = OpVectorShuffle %v3float %1489 %1489 0 1 2
+ %1773 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_16 %uint_106
+ %1599 = OpFMul %v3float %1596 %1598
+ %1774 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_3 %uint_3
+ %1600 = OpLoad %v3float %1448
+ %1775 = OpExtInst %void %2 DebugLine %64 %uint_140 %uint_140 %uint_3 %uint_106
+ %1601 = OpFAdd %v3float %1600 %1599
+ OpStore %1448 %1601
+ %2295 = OpExtInst %void %2 DebugScope %179
+ %1777 = OpExtInst %void %2 DebugLine %64 %uint_107 %uint_107 %uint_34 %uint_36
+ OpBranch %1602
+ %1602 = OpLabel
+ %2296 = OpExtInst %void %2 DebugScope %179
+ %1778 = OpExtInst %void %2 DebugLine %64 %uint_107 %uint_107 %uint_34 %uint_36
+ %1603 = OpLoad %int %1450
+ %1604 = OpIAdd %int %1603 %int_1
+ OpStore %1450 %1604
+ OpBranch %1499
+ %1605 = OpLabel
+ %2297 = OpExtInst %void %2 DebugScope %179
+ %1782 = OpExtInst %void %2 DebugLine %64 %uint_144 %uint_144 %uint_6 %uint_10
+ %1606 = OpAccessChain %_ptr_Uniform_UBO %ubo %int_0
+ %1607 = OpAccessChain %_ptr_Uniform_int %1606 %int_2
+ %1608 = OpLoad %int %1607
+ %1785 = OpExtInst %void %2 DebugLine %64 %uint_144 %uint_144 %uint_6 %uint_23
+ %1609 = OpSGreaterThan %bool %1608 %int_0
+ %2298 = OpExtInst %void %2 DebugNoScope
+ OpSelectionMerge %1614 None
+ OpBranchConditional %1609 %1610 %1614
+)"
+ R"( %1610 = OpLabel
+ %2299 = OpExtInst %void %2 DebugScope %180
+ %1788 = OpExtInst %void %2 DebugLine %64 %uint_146 %uint_146 %uint_22 %uint_22
+ %1611 = OpLoad %v3float %1448
+ OpStore %1466 %1611
+ %2300 = OpExtInst %void %2 DebugScope %155 %1803
+ %1842 = OpExtInst %void %2 DebugLine %64 %uint_78 %uint_78 %uint_15 %uint_22
+ %1810 = OpExtInst %void %2 DebugDeclare %172 %1466 %243
+ %2301 = OpExtInst %void %2 DebugScope %180
+ %2289 = OpExtInst %void %2 DebugLine %64 %uint_146 %uint_146 %uint_33 %uint_33
+ %2288 = OpExtInst %void %2 DebugValue %170 %1476 %243
+ %2302 = OpExtInst %void %2 DebugScope %157 %1803
+ %1844 = OpExtInst %void %2 DebugLine %64 %uint_79 %uint_79 %uint_7 %uint_15
+ OpStore %1801 %int_0
+ %1813 = OpExtInst %void %2 DebugDeclare %167 %1801 %243
+ %1846 = OpExtInst %void %2 DebugLine %64 %uint_79 %uint_79 %uint_7 %uint_16
+ OpBranch %1814
+ %1814 = OpLabel
+ %2303 = OpExtInst %void %2 DebugScope %157 %1803
+ %1847 = OpExtInst %void %2 DebugLine %64 %uint_79 %uint_79 %uint_18 %uint_18
+ %1815 = OpLoad %int %1801
+ %1848 = OpExtInst %void %2 DebugLine %64 %uint_79 %uint_79 %uint_18 %uint_22
+ %1816 = OpSLessThan %bool %1815 %int_3
+ %2304 = OpExtInst %void %2 DebugNoScope
+ OpLoopMerge %1840 %1837 None
+ OpBranchConditional %1816 %1817 %1840
+ %1817 = OpLabel
+ %2305 = OpExtInst %void %2 DebugScope %158 %1803
+ %1851 = OpExtInst %void %2 DebugLine %64 %uint_81 %uint_81 %uint_38 %uint_38
+ %1818 = OpLoad %int %1801
+ %1852 = OpExtInst %void %2 DebugLine %64 %uint_81 %uint_81 %uint_27 %uint_41
+ %1819 = OpAccessChain %_ptr_Uniform_UBO %ubo %int_0
+ %1820 = OpAccessChain %_ptr_Uniform_mat4v4float %1819 %int_1 %1818 %int_3
+ %1821 = OpLoad %mat4v4float %1820
+ %1856 = OpExtInst %void %2 DebugLine %64 %uint_81 %uint_81 %uint_60 %uint_68
+ %1823 = OpCompositeExtract %float %1476 0
+ %1824 = OpCompositeExtract %float %1476 1
+ %1825 = OpCompositeExtract %float %1476 2
+ %1859 = OpExtInst %void %2 DebugLine %64 %uint_81 %uint_81 %uint_53 %uint_76
+ %1826 = OpCompositeConstruct %v4float %1823 %1824 %1825 %float_1
+ %1860 = OpExtInst %void %2 DebugLine %64 %uint_81 %uint_81 %uint_23 %uint_77
+ %1827 = OpVectorTimesMatrix %v4float %1826 %1821
+ %2229 = OpExtInst %void %2 DebugLine %64 %uint_81 %uint_81 %uint_3 %uint_77
+ %2228 = OpExtInst %void %2 DebugValue %163 %1827 %243
+ %1867 = OpExtInst %void %2 DebugLine %64 %uint_85 %uint_85 %uint_40 %uint_40
+ %1832 = OpConvertSToF %float %1818
+ %2235 = OpExtInst %void %2 DebugLine %64 %uint_85 %uint_85 %uint_28 %uint_28
+ %2234 = OpExtInst %void %2 DebugValue %151 %1827 %243
+ %2238 = OpExtInst %void %2 DebugLine %64 %uint_85 %uint_85 %uint_40 %uint_40
+ %2237 = OpExtInst %void %2 DebugValue %148 %1832 %243
+ %2306 = OpExtInst %void %2 DebugScope %103 %1885
+ %1983 = OpExtInst %void %2 DebugLine %64 %uint_56 %uint_56 %uint_2 %uint_7
+ %1904 = OpExtInst %void %2 DebugDeclare %146 %1883 %243
+ %1986 = OpExtInst %void %2 DebugLine %64 %uint_57 %uint_57 %uint_2 %uint_2
+ %1907 = OpLoad %type_2d_image_array %textureShadowMap
+)"
+ R"( %1987 = OpExtInst %void %2 DebugLine %64 %uint_57 %uint_57 %uint_2 %uint_72
+ %1908 = OpImageQuerySizeLod %v3uint %1907 %uint_0
+ %1909 = OpCompositeExtract %uint %1908 0
+ %1910 = OpBitcast %int %1909
+ %1911 = OpAccessChain %_ptr_Function_int %1883 %int_0
+ OpStore %1911 %1910
+ %1912 = OpCompositeExtract %uint %1908 1
+ %1913 = OpBitcast %int %1912
+ %1914 = OpAccessChain %_ptr_Function_int %1883 %int_1
+ OpStore %1914 %1913
+ %1915 = OpCompositeExtract %uint %1908 2
+ %1916 = OpBitcast %int %1915
+ %2204 = OpExtInst %void %2 DebugValue %142 %1916 %243
+ %1999 = OpExtInst %void %2 DebugLine %64 %uint_57 %uint_57 %uint_19 %uint_19
+ %1917 = OpImageQueryLevels %uint %1907
+ %2000 = OpExtInst %void %2 DebugLine %64 %uint_57 %uint_57 %uint_2 %uint_72
+ %1918 = OpBitcast %int %1917
+ %2207 = OpExtInst %void %2 DebugValue %138 %1918 %243
+ %2211 = OpExtInst %void %2 DebugLine %64 %uint_58 %uint_58 %uint_2 %uint_16
+ %2210 = OpExtInst %void %2 DebugValue %135 %float_1_5 %243
+ %2005 = OpExtInst %void %2 DebugLine %64 %uint_59 %uint_59 %uint_13 %uint_21
+ %1921 = OpFMul %float %float_1_5 %float_1
+ %2006 = OpExtInst %void %2 DebugLine %64 %uint_59 %uint_59 %uint_33 %uint_40
+ %1922 = OpAccessChain %_ptr_Function_int %1883 %int_0
+ %1923 = OpLoad %int %1922
+ %1924 = OpConvertSToF %float %1923
+ %2009 = OpExtInst %void %2 DebugLine %64 %uint_59 %uint_59 %uint_13 %uint_41
+ %1925 = OpFDiv %float %1921 %1924
+ %2214 = OpExtInst %void %2 DebugLine %64 %uint_59 %uint_59 %uint_2 %uint_41
+ %2213 = OpExtInst %void %2 DebugValue %132 %1925 %243
+ %2013 = OpExtInst %void %2 DebugLine %64 %uint_60 %uint_60 %uint_13 %uint_21
+ %1928 = OpFMul %float %float_1_5 %float_1
+ %2014 = OpExtInst %void %2 DebugLine %64 %uint_60 %uint_60 %uint_33 %uint_40
+ %1929 = OpAccessChain %_ptr_Function_int %1883 %int_1
+ %1930 = OpLoad %int %1929
+ %1931 = OpConvertSToF %float %1930
+ %2017 = OpExtInst %void %2 DebugLine %64 %uint_60 %uint_60 %uint_13 %uint_41
+ %1932 = OpFDiv %float %1928 %1931
+ %2217 = OpExtInst %void %2 DebugLine %64 %uint_60 %uint_60 %uint_2 %uint_41
+ %2216 = OpExtInst %void %2 DebugValue %129 %1932 %243
+ %2020 = OpExtInst %void %2 DebugLine %64 %uint_62 %uint_62 %uint_2 %uint_23
+ OpStore %1891 %float_0
+ %1934 = OpExtInst %void %2 DebugDeclare %126 %1891 %243
+ %2022 = OpExtInst %void %2 DebugLine %64 %uint_63 %uint_63 %uint_2 %uint_14
+ OpStore %1892 %int_0
+ %1935 = OpExtInst %void %2 DebugDeclare %123 %1892 %243
+ %2220 = OpExtInst %void %2 DebugLine %64 %uint_64 %uint_64 %uint_2 %uint_14
+ %2219 = OpExtInst %void %2 DebugValue %119 %int_1 %243
+ %2027 = OpExtInst %void %2 DebugLine %64 %uint_66 %uint_66 %uint_15 %uint_16
+ %1938 = OpSNegate %int %int_1
+ %2028 = OpExtInst %void %2 DebugLine %64 %uint_66 %uint_66 %uint_7 %uint_16
+ OpStore %1894 %1938
+ %1939 = OpExtInst %void %2 DebugDeclare %115 %1894 %243
+ %2030 = OpExtInst %void %2 DebugLine %64 %uint_66 %uint_66 %uint_7 %uint_21
+ OpBranch %1940
+ %1940 = OpLabel
+ %2307 = OpExtInst %void %2 DebugScope %103 %1885
+ %2031 = OpExtInst %void %2 DebugLine %64 %uint_66 %uint_66 %uint_23 %uint_23
+ %1941 = OpLoad %int %1894
+ %2033 = OpExtInst %void %2 DebugLine %64 %uint_66 %uint_66 %uint_23 %uint_28
+ %1943 = OpSLessThanEqual %bool %1941 %int_1
+ %2308 = OpExtInst %void %2 DebugNoScope
+ OpLoopMerge %1976 %1973 None
+ OpBranchConditional %1943 %1944 %1976
+ %1944 = OpLabel
+ %2309 = OpExtInst %void %2 DebugScope %104 %1885
+ %2037 = OpExtInst %void %2 DebugLine %64 %uint_68 %uint_68 %uint_16 %uint_17
+ %1946 = OpSNegate %int %int_1
+ %2038 = OpExtInst %void %2 DebugLine %64 %uint_68 %uint_68 %uint_8 %uint_17
+ OpStore %1895 %1946
+ %1947 = OpExtInst %void %2 DebugDeclare %111 %1895 %243
+ %2040 = OpExtInst %void %2 DebugLine %64 %uint_68 %uint_68 %uint_8 %uint_22
+ OpBranch %1948
+ %1948 = OpLabel
+ %2310 = OpExtInst %void %2 DebugScope %104 %1885
+ %2041 = OpExtInst %void %2 DebugLine %64 %uint_68 %uint_68 %uint_24 %uint_24
+ %1949 = OpLoad %int %1895
+ %2043 = OpExtInst %void %2 DebugLine %64 %uint_68 %uint_68 %uint_24 %uint_29
+ %1951 = OpSLessThanEqual %bool %1949 %int_1
+ %2311 = OpExtInst %void %2 DebugNoScope
+ OpLoopMerge %1972 %1969 None
+ OpBranchConditional %1951 %1952 %1972
+ %1952 = OpLabel
+ %2312 = OpExtInst %void %2 DebugScope %106 %1885
+ %2047 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_32 %uint_32
+ OpStore %1896 %1827
+ %2051 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_53 %uint_53
+ %1956 = OpLoad %int %1894
+ %1957 = OpConvertSToF %float %1956
+ %2053 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_50 %uint_53
+ %1958 = OpFMul %float %1925 %1957
+ %2055 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_59 %uint_59
+ %1960 = OpLoad %int %1895
+ %1961 = OpConvertSToF %float %1960
+ %2057 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_56 %uint_59
+ %1962 = OpFMul %float %1932 %1961
+ %2058 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_43 %uint_60
+ %1963 = OpCompositeConstruct %v2float %1958 %1962
+ %2313 = OpExtInst %void %2 DebugScope %70 %2085
+ %2141 = OpExtInst %void %2 DebugLine %64 %uint_37 %uint_37 %uint_19 %uint_26
+ %2090 = OpExtInst %void %2 DebugDeclare %96 %1896 %243
+ %2314 = OpExtInst %void %2 DebugScope %106 %1885
+ %2223 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_36 %uint_36
+ %2222 = OpExtInst %void %2 DebugValue %93 %1832 %243
+ %2226 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_43 %uint_60
+ %2225 = OpExtInst %void %2 DebugValue %90 %1963 %243
+ %2315 = OpExtInst %void %2 DebugScope %73 %2085
+ %2144 = OpExtInst %void %2 DebugLine %64 %uint_39 %uint_39 %uint_2 %uint_17
+ OpStore %2083 %float_1
+ %2094 = OpExtInst %void %2 DebugDeclare %86 %2083 %243
+ %2147 = OpExtInst %void %2 DebugLine %64 %uint_40 %uint_40 %uint_27 %uint_29
+ %2096 = OpAccessChain %_ptr_Function_float %1896 %int_3
+ %2097 = OpLoad %float %2096
+ %2098 = OpCompositeConstruct %v4float %2097 %2097 %2097 %2097
+ %2150 = OpExtInst %void %2 DebugLine %64 %uint_40 %uint_40 %uint_23 %uint_29
+ %2099 = OpFDiv %v4float %1827 %2098
+ %2151 = OpExtInst %void %2 DebugLine %64 %uint_40 %uint_40 %uint_2 %uint_29
+ OpStore %2086 %2099
+ %2100 = OpExtInst %void %2 DebugDeclare %83 %2086 %243
+ %2154 = OpExtInst %void %2 DebugLine %64 %uint_41 %uint_41 %uint_19 %uint_31
+ %2102 = OpVectorShuffle %v2float %2099 %2099 0 1
+ %2155 = OpExtInst %void %2 DebugLine %64 %uint_41 %uint_41 %uint_19 %uint_36
+ %2103 = OpVectorTimesScalar %v2float %2102 %float_0_5
+ %2156 = OpExtInst %void %2 DebugLine %64 %uint_41 %uint_41 %uint_19 %uint_42
+ %2104 = OpFAdd %v2float %2103 %35
+ %2158 = OpExtInst %void %2 DebugLine %64 %uint_41 %uint_41 %uint_2 %uint_42
+ %2106 = OpVectorShuffle %v4float %2099 %2104 4 5 2 3
+ OpStore %2086 %2106
+ %2160 = OpExtInst %void %2 DebugLine %64 %uint_43 %uint_43 %uint_6 %uint_18
+ %2107 = OpAccessChain %_ptr_Function_float %2086 %int_2
+ %2108 = OpLoad %float %2107
+ %2162 = OpExtInst %void %2 DebugLine %64 %uint_43 %uint_43 %uint_6 %uint_23
+ %2109 = OpFOrdGreaterThan %bool %2108 %float_n1
+ %2163 = OpExtInst %void %2 DebugLine %64 %uint_43 %uint_43 %uint_30 %uint_42
+ %2110 = OpAccessChain %_ptr_Function_float %2086 %int_2
+ %2111 = OpLoad %float %2110
+ %2165 = OpExtInst %void %2 DebugLine %64 %uint_43 %uint_43 %uint_30 %uint_46
+ %2112 = OpFOrdLessThan %bool %2111 %float_1
+ %2166 = OpExtInst %void %2 DebugLine %64 %uint_43 %uint_43 %uint_6 %uint_46
+ %2113 = OpLogicalAnd %bool %2109 %2112
+ %2316 = OpExtInst %void %2 DebugNoScope
+ OpSelectionMerge %2139 None
+ OpBranchConditional %2113 %2114 %2139
+ %2114 = OpLabel
+ %2317 = OpExtInst %void %2 DebugScope %74 %2085
+ %2169 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_16 %uint_16
+ %2115 = OpLoad %type_2d_image_array %textureShadowMap
+ %2170 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_40 %uint_40
+ %2116 = OpLoad %type_sampler %samplerShadowMap
+ %2171 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_65 %uint_65
+ %2117 = OpLoad %v4float %2086
+ %2172 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_65 %uint_77
+ %2118 = OpVectorShuffle %v2float %2117 %2117 0 1
+ %2174 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_65 %uint_82
+ %2120 = OpFAdd %v2float %2118 %1963
+ %2122 = OpCompositeExtract %float %2120 0
+ %2123 = OpCompositeExtract %float %2120 1
+ %2178 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_58 %uint_95
+ %2124 = OpCompositeConstruct %v3float %2122 %2123 %1832
+ %2179 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_16 %uint_96
+ %2125 = OpSampledImage %type_sampled_image_0 %2115 %2116
+ %2126 = OpImageSampleImplicitLod %v4float %2125 %2124 None
+ %2181 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_16 %uint_98
+ %2127 = OpCompositeExtract %float %2126 0
+ %2202 = OpExtInst %void %2 DebugLine %64 %uint_45 %uint_45 %uint_3 %uint_98
+ %2201 = OpExtInst %void %2 DebugValue %79 %2127 %243
+ %2184 = OpExtInst %void %2 DebugLine %64 %uint_46 %uint_46 %uint_7 %uint_19
+ %2129 = OpAccessChain %_ptr_Function_float %2086 %int_3
+ %2130 = OpLoad %float %2129
+ %2186 = OpExtInst %void %2 DebugLine %64 %uint_46 %uint_46 %uint_7 %uint_23
+ %2131 = OpFOrdGreaterThan %bool %2130 %float_0
+ %2188 = OpExtInst %void %2 DebugLine %64 %uint_46 %uint_46 %uint_37 %uint_49
+ %2133 = OpAccessChain %_ptr_Function_float %2086 %int_2
+ %2134 = OpLoad %float %2133
+ %2190 = OpExtInst %void %2 DebugLine %64 %uint_46 %uint_46 %uint_30 %uint_49
+ %2135 = OpFOrdLessThan %bool %2127 %2134
+ %2191 = OpExtInst %void %2 DebugLine %64 %uint_46 %uint_46 %uint_7 %uint_49
+ %2136 = OpLogicalAnd %bool %2131 %2135
+ %2318 = OpExtInst %void %2 DebugNoScope
+ OpSelectionMerge %2138 None
+ OpBranchConditional %2136 %2137 %2138
+ %2137 = OpLabel
+ %2319 = OpExtInst %void %2 DebugScope %76 %2085
+ %2194 = OpExtInst %void %2 DebugLine %64 %uint_48 %uint_48 %uint_4 %uint_13
+ OpStore %2083 %float_0_25
+ %2320 = OpExtInst %void %2 DebugScope %74 %2085
+ %2195 = OpExtInst %void %2 DebugLine %64 %uint_49 %uint_49 %uint_3 %uint_3
+ OpBranch %2138
+ %2138 = OpLabel
+ %2321 = OpExtInst %void %2 DebugScope %73 %2085
+ %2196 = OpExtInst %void %2 DebugLine %64 %uint_50 %uint_50 %uint_2 %uint_2
+ OpBranch %2139
+ %2139 = OpLabel
+ %2322 = OpExtInst %void %2 DebugScope %73 %2085
+ %2197 = OpExtInst %void %2 DebugLine %64 %uint_51 %uint_51 %uint_9 %uint_9
+ %2140 = OpLoad %float %2083
+ %2323 = OpExtInst %void %2 DebugScope %106 %1885
+ %2061 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_4 %uint_4
+ %1965 = OpLoad %float %1891
+ %2062 = OpExtInst %void %2 DebugLine %64 %uint_70 %uint_70 %uint_4 %uint_61
+ %1966 = OpFAdd %float %1965 %2140
+ OpStore %1891 %1966
+ %2064 = OpExtInst %void %2 DebugLine %64 %uint_71 %uint_71 %uint_4 %uint_9
+ %1967 = OpLoad %int %1892
+ %1968 = OpIAdd %int %1967 %int_1
+ OpStore %1892 %1968
+ %2324 = OpExtInst %void %2 DebugScope %104 %1885
+ %2067 = OpExtInst %void %2 DebugLine %64 %uint_68 %uint_68 %uint_36 %uint_37
+ OpBranch %1969
+ %1969 = OpLabel
+ %2325 = OpExtInst %void %2 DebugScope %104 %1885
+ %2068 = OpExtInst %void %2 DebugLine %64 %uint_68 %uint_68 %uint_36 %uint_37
+ %1970 = OpLoad %int %1895
+ %1971 = OpIAdd %int %1970 %int_1
+ OpStore %1895 %1971
+ OpBranch %1948
+ %1972 = OpLabel
+ %2326 = OpExtInst %void %2 DebugScope %103 %1885
+ %2072 = OpExtInst %void %2 DebugLine %64 %uint_66 %uint_66 %uint_35 %uint_36
+ OpBranch %1973
+ %1973 = OpLabel
+ %2327 = OpExtInst %void %2 DebugScope %103 %1885
+ %2073 = OpExtInst %void %2 DebugLine %64 %uint_66 %uint_66 %uint_35 %uint_36
+ %1974 = OpLoad %int %1894
+ %1975 = OpIAdd %int %1974 %int_1
+ OpStore %1894 %1975
+ OpBranch %1940
+ %1976 = OpLabel
+ %2328 = OpExtInst %void %2 DebugScope %103 %1885
+ %2077 = OpExtInst %void %2 DebugLine %64 %uint_75 %uint_75 %uint_9 %uint_9
+ %1977 = OpLoad %float %1891
+ %2078 = OpExtInst %void %2 DebugLine %64 %uint_75 %uint_75 %uint_24 %uint_24
+ %1978 = OpLoad %int %1892
+ %1979 = OpConvertSToF %float %1978
+ %2080 = OpExtInst %void %2 DebugLine %64 %uint_75 %uint_75 %uint_9 %uint_24
+ %1980 = OpFDiv %float %1977 %1979
+ %2329 = OpExtInst %void %2 DebugScope %158 %1803
+ %2232 = OpExtInst %void %2 DebugLine %64 %uint_85 %uint_85 %uint_4 %uint_41
+ %2231 = OpExtInst %void %2 DebugValue %160 %1980 %243
+ %1872 = OpExtInst %void %2 DebugLine %64 %uint_90 %uint_90 %uint_3 %uint_3
+ %1835 = OpLoad %v3float %1466
+ %1873 = OpExtInst %void %2 DebugLine %64 %uint_90 %uint_90 %uint_3 %uint_16
+ %1836 = OpVectorTimesScalar %v3float %1835 %1980
+ OpStore %1466 %1836
+ %2330 = OpExtInst %void %2 DebugScope %157 %1803
+ %1875 = OpExtInst %void %2 DebugLine %64 %uint_79 %uint_79 %uint_35 %uint_37
+ OpBranch %1837
+ %1837 = OpLabel
+ %2331 = OpExtInst %void %2 DebugScope %157 %1803
+ %1876 = OpExtInst %void %2 DebugLine %64 %uint_79 %uint_79 %uint_35 %uint_37
+ %1838 = OpLoad %int %1801
+ %1839 = OpIAdd %int %1838 %int_1
+ OpStore %1801 %1839
+ OpBranch %1814
+ %1840 = OpLabel
+ %2332 = OpExtInst %void %2 DebugScope %157 %1803
+ %1880 = OpExtInst %void %2 DebugLine %64 %uint_92 %uint_92 %uint_9 %uint_9
+ %1841 = OpLoad %v3float %1466
+ %2333 = OpExtInst %void %2 DebugScope %180
+ %1793 = OpExtInst %void %2 DebugLine %64 %uint_146 %uint_146 %uint_3 %uint_40
+ OpStore %1448 %1841
+ %2334 = OpExtInst %void %2 DebugScope %179
+ %1794 = OpExtInst %void %2 DebugLine %64 %uint_147 %uint_147 %uint_2 %uint_2
+ OpBranch %1614
+ %1614 = OpLabel
+;CHECK: %1614 = OpLabel
+;CHECK-NEXT: [[phi:%\w+]] = OpPhi
+;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue %233
+ %2335 = OpExtInst %void %2 DebugScope %179
+ %1795 = OpExtInst %void %2 DebugLine %64 %uint_149 %uint_149 %uint_16 %uint_16
+ %1615 = OpLoad %v3float %1448
+ %1616 = OpCompositeExtract %float %1615 0
+ %1617 = OpCompositeExtract %float %1615 1
+ %1618 = OpCompositeExtract %float %1615 2
+ %1799 = OpExtInst %void %2 DebugLine %64 %uint_149 %uint_149 %uint_9 %uint_28
+ %1619 = OpCompositeConstruct %v4float %1616 %1617 %1618 %float_1
+ %2336 = OpExtInst %void %2 DebugNoLine
+ %2337 = OpExtInst %void %2 DebugNoScope
+ OpStore %out_var_SV_TARGET %1619
+ %329 = OpExtInst %void %2 DebugLine %64 %uint_150 %uint_150 %uint_1 %uint_1
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_2);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// No optimization in the presence of
diff --git a/test/opt/loop_optimizations/unroll_simple.cpp b/test/opt/loop_optimizations/unroll_simple.cpp
index b72305c8..299fb2d5 100644
--- a/test/opt/loop_optimizations/unroll_simple.cpp
+++ b/test/opt/loop_optimizations/unroll_simple.cpp
@@ -3789,6 +3789,40 @@ TEST_F(PassClassTest, PartialUnrollWithPhiReferencesPhi) {
SinglePassRunAndMatch<PartialUnrollerTestPass<2>>(text, true);
}
+TEST_F(PassClassTest, DontUnrollInfiteLoop) {
+ // This is an infinite loop that because the step is 0. We want to make sure
+ // the unroller does not try to unroll it.
+ const std::string text = R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main"
+OpExecutionMode %2 OriginUpperLeft
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%int_50 = OpConstant %int 50
+%bool = OpTypeBool
+%int_0_0 = OpConstant %int 0
+%2 = OpFunction %void None %4
+%10 = OpLabel
+OpBranch %11
+%11 = OpLabel
+%12 = OpPhi %int %int_0 %10 %13 %14
+%15 = OpSLessThan %bool %12 %int_50
+OpLoopMerge %16 %14 Unroll
+OpBranchConditional %15 %14 %16
+%14 = OpLabel
+%13 = OpIAdd %int %12 %int_0_0
+OpBranch %11
+%16 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<LoopUnroller>(text, text, false);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/pass_merge_return_test.cpp b/test/opt/pass_merge_return_test.cpp
index 21960d17..04bd5d9b 100644
--- a/test/opt/pass_merge_return_test.cpp
+++ b/test/opt/pass_merge_return_test.cpp
@@ -2231,7 +2231,7 @@ TEST_F(MergeReturnPassTest, ReturnsInSwitch) {
TEST_F(MergeReturnPassTest, UnreachableMergeAndContinue) {
// Make sure that the pass can handle a single block that is both a merge and
- // a continue.
+ // a continue. Note that this is invalid SPIR-V.
const std::string text =
R"(
OpCapability Shader
@@ -2265,7 +2265,7 @@ TEST_F(MergeReturnPassTest, UnreachableMergeAndContinue) {
)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- auto result = SinglePassRunAndDisassemble<MergeReturnPass>(text, true, true);
+ auto result = SinglePassRunAndDisassemble<MergeReturnPass>(text, true, false);
// Not looking for any particular output. Other tests do that.
// Just want to make sure the check for unreachable blocks does not emit an
diff --git a/test/opt/reduce_load_size_test.cpp b/test/opt/reduce_load_size_test.cpp
index abb5cde6..45467506 100644
--- a/test/opt/reduce_load_size_test.cpp
+++ b/test/opt/reduce_load_size_test.cpp
@@ -498,6 +498,45 @@ TEST_F(ReduceLoadSizeTest, replace_cbuffer_load_fully_used) {
SinglePassRunAndMatch<ReduceLoadSize>(test, false, 1.1);
}
+TEST_F(ReduceLoadSizeTest, replace_array_with_spec_constant_size) {
+ const std::string test =
+ R"(
+ OpCapability ClipDistance
+ OpExtension " "
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 " "
+ OpExecutionMode %1 OriginUpperLeft
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+ %uint = OpTypeInt 32 0
+ %6 = OpSpecConstant %uint 538976288
+ %_arr_int_6 = OpTypeArray %int %6
+ %_struct_8 = OpTypeStruct %_arr_int_6
+ %_struct_9 = OpTypeStruct %_struct_8
+%_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9
+; CHECK: [[var:%\w+]] = OpVariable %_ptr_Uniform__struct_9 Uniform
+ %11 = OpVariable %_ptr_Uniform__struct_9 Uniform
+ %int_0 = OpConstant %int 0
+%_ptr_Uniform__arr_int_6 = OpTypePointer Uniform %_arr_int_6
+ %1 = OpFunction %void None %3
+ %14 = OpLabel
+; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_Uniform__arr_int_6 [[var]] %int_0 %int_0
+; CHECK: [[new_ac:%\w+]] = OpAccessChain %_ptr_Uniform_int [[ac]] %uint_538976288
+; CHECK: [[ld:%\w+]] = OpLoad %int [[new_ac]]
+; CHECK: %18 = OpIAdd %int [[ld]] [[ld]]
+ %15 = OpAccessChain %_ptr_Uniform__arr_int_6 %11 %int_0 %int_0
+ %16 = OpLoad %_arr_int_6 %15
+ %17 = OpCompositeExtract %int %16 538976288
+ %18 = OpIAdd %int %17 %17
+ OpUnreachable
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<ReduceLoadSize>(test, false,
+ kDefaultLoadReductionThreshold);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/remove_dontinline_test.cpp b/test/opt/remove_dontinline_test.cpp
new file mode 100644
index 00000000..c5425e8c
--- /dev/null
+++ b/test/opt/remove_dontinline_test.cpp
@@ -0,0 +1,127 @@
+// Copyright (c) 2017 Google Inc.
+//
+// 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 <vector>
+
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using StrengthReductionBasicTest = PassTest<::testing::Test>;
+
+TEST_F(StrengthReductionBasicTest, ClearDontInline) {
+ const std::vector<const char*> text = {
+ // clang-format off
+ "OpCapability Shader",
+ "%1 = OpExtInstImport \"GLSL.std.450\"",
+ "OpMemoryModel Logical GLSL450",
+ "OpEntryPoint Vertex %main \"main\"",
+ "%void = OpTypeVoid",
+ "%4 = OpTypeFunction %void",
+"; CHECK: OpFunction %void None",
+ "%main = OpFunction %void DontInline %4",
+ "%8 = OpLabel",
+ "OpReturn",
+ "OpFunctionEnd"
+ // clang-format on
+ };
+
+ SinglePassRunAndMatch<RemoveDontInline>(JoinAllInsts(text), true);
+}
+
+TEST_F(StrengthReductionBasicTest, LeaveUnchanged1) {
+ const std::vector<const char*> text = {
+ // clang-format off
+ "OpCapability Shader",
+ "%1 = OpExtInstImport \"GLSL.std.450\"",
+ "OpMemoryModel Logical GLSL450",
+ "OpEntryPoint Vertex %main \"main\"",
+ "%void = OpTypeVoid",
+ "%4 = OpTypeFunction %void",
+ "%main = OpFunction %void None %4",
+ "%8 = OpLabel",
+ "OpReturn",
+ "OpFunctionEnd"
+ // clang-format on
+ };
+
+ EXPECT_EQ(Pass::Status::SuccessWithoutChange,
+ std::get<1>(SinglePassRunAndDisassemble<RemoveDontInline>(
+ JoinAllInsts(text), false, true)));
+}
+
+TEST_F(StrengthReductionBasicTest, LeaveUnchanged2) {
+ const std::vector<const char*> text = {
+ // clang-format off
+ "OpCapability Shader",
+ "%1 = OpExtInstImport \"GLSL.std.450\"",
+ "OpMemoryModel Logical GLSL450",
+ "OpEntryPoint Vertex %main \"main\"",
+ "%void = OpTypeVoid",
+ "%4 = OpTypeFunction %void",
+ "%main = OpFunction %void Inline %4",
+ "%8 = OpLabel",
+ "OpReturn",
+ "OpFunctionEnd"
+ // clang-format on
+ };
+
+ EXPECT_EQ(Pass::Status::SuccessWithoutChange,
+ std::get<1>(SinglePassRunAndDisassemble<RemoveDontInline>(
+ JoinAllInsts(text), false, true)));
+}
+
+TEST_F(StrengthReductionBasicTest, ClearMultipleDontInline) {
+ const std::vector<const char*> text = {
+ // clang-format off
+ "OpCapability Shader",
+ "%1 = OpExtInstImport \"GLSL.std.450\"",
+ "OpMemoryModel Logical GLSL450",
+ "OpEntryPoint Vertex %main1 \"main1\"",
+ "OpEntryPoint Vertex %main2 \"main2\"",
+ "OpEntryPoint Vertex %main3 \"main3\"",
+ "OpEntryPoint Vertex %main4 \"main4\"",
+ "%void = OpTypeVoid",
+ "%4 = OpTypeFunction %void",
+ "; CHECK: OpFunction %void None",
+ "%main1 = OpFunction %void DontInline %4",
+ "%8 = OpLabel",
+ "OpReturn",
+ "OpFunctionEnd",
+ "; CHECK: OpFunction %void Inline",
+ "%main2 = OpFunction %void Inline %4",
+ "%9 = OpLabel",
+ "OpReturn",
+ "OpFunctionEnd",
+ "; CHECK: OpFunction %void Pure",
+ "%main3 = OpFunction %void DontInline|Pure %4",
+ "%10 = OpLabel",
+ "OpReturn",
+ "OpFunctionEnd",
+ "; CHECK: OpFunction %void None",
+ "%main4 = OpFunction %void None %4",
+ "%11 = OpLabel",
+ "OpReturn",
+ "OpFunctionEnd"
+ // clang-format on
+ };
+
+ SinglePassRunAndMatch<RemoveDontInline>(JoinAllInsts(text), true);
+}
+} // namespace
+} // namespace opt
+} // namespace spvtools
diff --git a/test/opt/replace_desc_array_access_using_var_index_test.cpp b/test/opt/replace_desc_array_access_using_var_index_test.cpp
index ca625812..9ab9eb11 100644
--- a/test/opt/replace_desc_array_access_using_var_index_test.cpp
+++ b/test/opt/replace_desc_array_access_using_var_index_test.cpp
@@ -406,6 +406,171 @@ TEST_F(ReplaceDescArrayAccessUsingVarIndexTest,
SinglePassRunAndMatch<ReplaceDescArrayAccessUsingVarIndex>(text, true);
}
+TEST_F(ReplaceDescArrayAccessUsingVarIndexTest, ReplaceMultipleAccessChains) {
+ const std::string text = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "TestFragment" %2
+ OpExecutionMode %1 OriginUpperLeft
+ OpName %11 "type.ConstantBuffer.TestStruct"
+ OpMemberName %11 0 "val1"
+ OpMemberName %11 1 "val2"
+ OpName %3 "TestResources"
+ OpName %13 "type.2d.image"
+ OpName %4 "OutBuffer"
+ OpName %2 "in.var.SV_INSTANCEID"
+ OpName %1 "TestFragment"
+ OpDecorate %2 Flat
+ OpDecorate %2 Location 0
+ OpDecorate %3 DescriptorSet 0
+ OpDecorate %3 Binding 0
+ OpDecorate %4 DescriptorSet 0
+ OpDecorate %4 Binding 1
+ OpMemberDecorate %11 0 Offset 0
+ OpMemberDecorate %11 1 Offset 4
+ OpDecorate %11 Block
+ %9 = OpTypeInt 32 0
+ %10 = OpConstant %9 2
+ %11 = OpTypeStruct %9 %9
+ %8 = OpTypeArray %11 %10
+ %7 = OpTypePointer Uniform %8
+ %13 = OpTypeImage %9 2D 2 0 0 2 R32ui
+ %12 = OpTypePointer UniformConstant %13
+ %14 = OpTypePointer Input %9
+ %15 = OpTypeVoid
+ %16 = OpTypeFunction %15
+ %40 = OpTypeVector %9 2
+ %3 = OpVariable %7 Uniform
+ %4 = OpVariable %12 UniformConstant
+ %2 = OpVariable %14 Input
+ %57 = OpTypePointer Uniform %11
+ %61 = OpTypePointer Uniform %9
+ %62 = OpConstant %9 0
+ %1 = OpFunction %15 None %16
+ %17 = OpLabel
+ %20 = OpLoad %9 %2
+ %47 = OpAccessChain %57 %3 %20
+ %63 = OpAccessChain %61 %47 %62
+ %64 = OpLoad %9 %63
+
+; CHECK: [[null_value:%\w+]] = OpConstantNull %uint
+
+; CHECK: [[var_index:%\w+]] = OpLoad %uint %in_var_SV_INSTANCEID
+; CHECK: OpSelectionMerge [[merge:%\w+]] None
+; CHECK: OpSwitch [[var_index]] [[default:%\w+]] 0 [[case0:%\w+]] 1 [[case1:%\w+]]
+; CHECK: [[case0]] = OpLabel
+; CHECK: OpAccessChain
+; CHECK: OpAccessChain
+; CHECK: [[result0:%\w+]] = OpLoad
+; CHECK: OpBranch [[merge]]
+; CHECK: [[case1]] = OpLabel
+; CHECK: OpAccessChain
+; CHECK: OpAccessChain
+; CHECK: [[result1:%\w+]] = OpLoad
+; CHECK: OpBranch [[merge]]
+; CHECK: [[default]] = OpLabel
+; CHECK: OpBranch [[merge]]
+; CHECK: [[merge]] = OpLabel
+; CHECK: OpPhi %uint [[result0]] [[case0]] [[result1]] [[case1]] [[null_value]] [[default]]
+
+ %55 = OpCompositeConstruct %40 %20 %20
+ %56 = OpLoad %13 %4
+ OpImageWrite %56 %55 %64 None
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<ReplaceDescArrayAccessUsingVarIndex>(text, true);
+}
+
+TEST_F(ReplaceDescArrayAccessUsingVarIndexTest,
+ ReplaceAccessChainToTextureArrayWithNonUniformIndex) {
+ const std::string text = R"(
+ OpCapability Shader
+ OpCapability ShaderNonUniform
+ OpCapability SampledImageArrayNonUniformIndexing
+ OpExtension "SPV_EXT_descriptor_indexing"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %PSMain "PSMain" %in_var_TEXCOORD0 %in_var_MATERIAL_ID %out_var_SV_TARGET
+ OpExecutionMode %PSMain OriginUpperLeft
+ OpSource HLSL 610
+ OpName %type_sampler "type.sampler"
+ OpName %sampler_ "sampler_"
+ OpName %type_2d_image "type.2d.image"
+ OpName %texture_2d "texture_2d"
+ OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0"
+ OpName %in_var_MATERIAL_ID "in.var.MATERIAL_ID"
+ OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+ OpName %PSMain "PSMain"
+ OpName %type_sampled_image "type.sampled.image"
+ OpDecorate %in_var_MATERIAL_ID Flat
+ OpDecorate %in_var_TEXCOORD0 Location 0
+ OpDecorate %in_var_MATERIAL_ID Location 1
+ OpDecorate %out_var_SV_TARGET Location 0
+ OpDecorate %sampler_ DescriptorSet 1
+ OpDecorate %sampler_ Binding 1
+ OpDecorate %texture_2d DescriptorSet 0
+ OpDecorate %texture_2d Binding 0
+
+; CHECK: OpDecorate [[v0:%\w+]] NonUniform
+; CHECK: OpDecorate [[v1:%\w+]] NonUniform
+; CHECK: OpDecorate [[v2:%\w+]] NonUniform
+; CHECK: OpDecorate [[v3:%\w+]] NonUniform
+
+ OpDecorate %10 NonUniform
+ OpDecorate %11 NonUniform
+ OpDecorate %12 NonUniform
+ OpDecorate %13 NonUniform
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+ %uint = OpTypeInt 32 0
+ %uint_4 = OpConstant %uint 4
+ %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_arr_type_2d_image_uint_4 = OpTypeArray %type_2d_image %uint_4
+%_ptr_UniformConstant__arr_type_2d_image_uint_4 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_4
+ %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+%_ptr_Input_uint = OpTypePointer Input %uint
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %26 = OpTypeFunction %void
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+ %sampler_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+ %texture_2d = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_4 UniformConstant
+%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v2float Input
+%in_var_MATERIAL_ID = OpVariable %_ptr_Input_uint Input
+%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+
+; CHECK: %uint_0 = OpConstant %uint 0
+; CHECK: %uint_1 = OpConstant %uint 1
+; CHECK: %uint_2 = OpConstant %uint 2
+; CHECK: %uint_3 = OpConstant %uint 3
+
+ %PSMain = OpFunction %void None %26
+ %28 = OpLabel
+ %29 = OpLoad %v2float %in_var_TEXCOORD0
+ %30 = OpLoad %uint %in_var_MATERIAL_ID
+; CHECK: [[v0]] = OpCopyObject %uint {{%\w+}}
+ %10 = OpCopyObject %uint %30
+; CHECK: [[v1]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %texture_2d [[v0]]
+ %11 = OpAccessChain %_ptr_UniformConstant_type_2d_image %texture_2d %10
+; CHECK: [[v2]] = OpLoad %type_2d_image [[v1]]
+ %12 = OpLoad %type_2d_image %11
+ %31 = OpLoad %type_sampler %sampler_
+; CHECK: [[v3]] = OpSampledImage %type_sampled_image [[v2]] {{%\w+}}
+ %13 = OpSampledImage %type_sampled_image %12 %31
+ %32 = OpImageSampleImplicitLod %v4float %13 %29 None
+ OpStore %out_var_SV_TARGET %32
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<ReplaceDescArrayAccessUsingVarIndex>(text, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/scalar_analysis.cpp b/test/opt/scalar_analysis.cpp
index 598d8c7b..df2aa8f8 100644
--- a/test/opt/scalar_analysis.cpp
+++ b/test/opt/scalar_analysis.cpp
@@ -1202,7 +1202,6 @@ TEST_F(ScalarAnalysisTest, InductionWithVariantStep) {
EXPECT_EQ(phis.size(), 2u);
SENode* phi_node_1 = analysis.AnalyzeInstruction(phis[0]);
SENode* phi_node_2 = analysis.AnalyzeInstruction(phis[1]);
- phi_node_1->DumpDot(std::cout, true);
EXPECT_NE(phi_node_1, nullptr);
EXPECT_NE(phi_node_2, nullptr);
diff --git a/test/opt/scalar_replacement_test.cpp b/test/opt/scalar_replacement_test.cpp
index 8cb888c9..0c97c80b 100644
--- a/test/opt/scalar_replacement_test.cpp
+++ b/test/opt/scalar_replacement_test.cpp
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "source/opt/scalar_replacement_pass.h"
+
#include <string>
#include "gmock/gmock.h"
@@ -23,6 +25,18 @@ namespace spvtools {
namespace opt {
namespace {
+using ScalarReplacementPassName = ::testing::Test;
+
+TEST_F(ScalarReplacementPassName, Default) {
+ auto srp = ScalarReplacementPass();
+ EXPECT_STREQ(srp.name(), "scalar-replacement=100");
+}
+
+TEST_F(ScalarReplacementPassName, Large) {
+ auto srp = ScalarReplacementPass(0xffffffffu);
+ EXPECT_STREQ(srp.name(), "scalar-replacement=4294967295");
+}
+
using ScalarReplacementTest = PassTest<::testing::Test>;
TEST_F(ScalarReplacementTest, SimpleStruct) {
@@ -470,9 +484,9 @@ TEST_F(ScalarReplacementTest, NonUniformCompositeInitialization) {
; CHECK: [[const_array:%\w+]] = OpConstantComposite [[array]]
; CHECK: [[const_matrix:%\w+]] = OpConstantNull [[matrix]]
; CHECK: [[const_struct1:%\w+]] = OpConstantComposite [[struct1]]
-; CHECK: OpConstantNull [[uint]]
-; CHECK: OpConstantNull [[vector]]
-; CHECK: OpConstantNull [[long]]
+; CHECK: OpUndef [[uint]]
+; CHECK: OpUndef [[vector]]
+; CHECK: OpUndef [[long]]
; CHECK: OpFunction
; CHECK-NOT: OpVariable [[struct2_ptr]] Function
; CHECK: OpVariable [[uint_ptr]] Function
@@ -654,11 +668,10 @@ TEST_F(ScalarReplacementTest, ReplaceWholeLoadCopyMemoryAccess) {
; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[uint]]
; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]]
-; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0
-; CHECK: [[null:%\w+]] = OpConstantNull [[uint]]
+; CHECK: [[undef:%\w+]] = OpUndef [[uint]]
; CHECK: [[var0:%\w+]] = OpVariable [[uint_ptr]] Function
; CHECK: [[l0:%\w+]] = OpLoad [[uint]] [[var0]] Nontemporal
-; CHECK: OpCompositeConstruct [[struct1]] [[l0]] [[null]]
+; CHECK: OpCompositeConstruct [[struct1]] [[l0]] [[undef]]
;
OpCapability Shader
OpCapability Linkage
@@ -1267,16 +1280,16 @@ TEST_F(ScalarReplacementTest, ReplaceWholeLoadAndStore) {
; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[uint]]
; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]]
; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0
-; CHECK: [[null:%\w+]] = OpConstantNull [[uint]]
+; CHECK: [[undef:%\w+]] = OpUndef [[uint]]
; CHECK: [[var0:%\w+]] = OpVariable [[uint_ptr]] Function
; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function
; CHECK-NOT: OpVariable
; CHECK: [[l0:%\w+]] = OpLoad [[uint]] [[var0]]
-; CHECK: [[c0:%\w+]] = OpCompositeConstruct [[struct1]] [[l0]] [[null]]
+; CHECK: [[c0:%\w+]] = OpCompositeConstruct [[struct1]] [[l0]] [[undef]]
; CHECK: [[e0:%\w+]] = OpCompositeExtract [[uint]] [[c0]] 0
; CHECK: OpStore [[var1]] [[e0]]
; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]]
-; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[null]]
+; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[undef]]
; CHECK: [[e1:%\w+]] = OpCompositeExtract [[uint]] [[c1]] 0
;
OpCapability Shader
@@ -1314,7 +1327,7 @@ TEST_F(ScalarReplacementTest, ReplaceWholeLoadAndStore2) {
; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[uint]]
; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]]
; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0
-; CHECK: [[null:%\w+]] = OpConstantNull [[uint]]
+; CHECK: [[undef:%\w+]] = OpUndef [[uint]]
; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function
; CHECK: [[var0a:%\w+]] = OpVariable [[uint_ptr]] Function
; CHECK: [[var0b:%\w+]] = OpVariable [[uint_ptr]] Function
@@ -1325,7 +1338,7 @@ TEST_F(ScalarReplacementTest, ReplaceWholeLoadAndStore2) {
; CHECK: [[e0:%\w+]] = OpCompositeExtract [[uint]] [[c0]] 0
; CHECK: OpStore [[var1]] [[e0]]
; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]]
-; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[null]]
+; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[undef]]
; CHECK: [[e1:%\w+]] = OpCompositeExtract [[uint]] [[c1]] 0
;
OpCapability Shader
@@ -1362,14 +1375,14 @@ TEST_F(ScalarReplacementTest, CreateAmbiguousNullConstant1) {
; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[struct_member:%\w+]]
; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]]
; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0
-; CHECK: [[null:%\w+]] = OpConstantNull [[struct_member]]
+; CHECK: [[undef:%\w+]] = OpUndef [[struct_member]]
; CHECK: [[var0a:%\w+]] = OpVariable [[uint_ptr]] Function
; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function
; CHECK: [[var0b:%\w+]] = OpVariable [[uint_ptr]] Function
; CHECK-NOT: OpVariable
; CHECK: OpStore [[var1]]
; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]]
-; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[null]]
+; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[undef]]
; CHECK: [[e1:%\w+]] = OpCompositeExtract [[uint]] [[c1]] 0
;
OpCapability Shader
@@ -1444,13 +1457,13 @@ TEST_F(ScalarReplacementTest, CreateAmbiguousNullConstant2) {
; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[struct_member:%\w+]]
; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]]
; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0
-; CHECK: [[null:%\w+]] = OpConstantNull [[struct_member]]
+; CHECK: [[undef:%\w+]] = OpUndef [[struct_member]]
; CHECK: [[var0a:%\w+]] = OpVariable [[uint_ptr]] Function
; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function
; CHECK: [[var0b:%\w+]] = OpVariable [[uint_ptr]] Function
; CHECK: OpStore [[var1]]
; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]]
-; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[null]]
+; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[undef]]
; CHECK: [[e1:%\w+]] = OpCompositeExtract [[uint]] [[c1]] 0
;
OpCapability Shader
@@ -2263,6 +2276,40 @@ OpFunctionEnd
SinglePassRunAndCheck<ScalarReplacementPass>(text, text, false);
}
+TEST_F(ScalarReplacementTest, UndefImageMember) {
+ // Test that scalar replacement creates an undef for a type that cannot have
+ // and OpConstantNull.
+ const std::string text = R"(
+; CHECK: [[image_type:%\w+]] = OpTypeSampledImage {{%\w+}}
+; CHECK: [[struct_type:%\w+]] = OpTypeStruct [[image_type]]
+; CHECK: [[undef:%\w+]] = OpUndef [[image_type]]
+; CHECK: {{%\w+}} = OpCompositeConstruct [[struct_type]] [[undef]]
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ %void = OpTypeVoid
+ %4 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %6 = OpTypeImage %float 2D 0 0 0 1 Unknown
+ %7 = OpTypeSampledImage %6
+ %_struct_8 = OpTypeStruct %7
+ %9 = OpTypeFunction %_struct_8
+ %10 = OpUndef %_struct_8
+%_ptr_Function__struct_8 = OpTypePointer Function %_struct_8
+ %2 = OpFunction %void None %4
+ %11 = OpLabel
+ %16 = OpVariable %_ptr_Function__struct_8 Function
+ OpStore %16 %10
+ %12 = OpLoad %_struct_8 %16
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<ScalarReplacementPass>(text, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/spread_volatile_semantics_test.cpp b/test/opt/spread_volatile_semantics_test.cpp
index 83b2dcfa..dbb889c0 100644
--- a/test/opt/spread_volatile_semantics_test.cpp
+++ b/test/opt/spread_volatile_semantics_test.cpp
@@ -54,6 +54,7 @@ OpSource GLSL 460
OpSourceExtension "GL_EXT_nonuniform_qualifier"
OpSourceExtension "GL_KHR_ray_tracing"
OpName %main "main"
+OpName %fn "fn"
OpName %StorageBuffer "StorageBuffer"
OpMemberName %StorageBuffer 0 "index"
OpMemberName %StorageBuffer 1 "red"
@@ -109,6 +110,11 @@ OpDecorate %var BuiltIn )") + built_in + std::string(R"(
%29 = OpCompositeExtract %float %27 0
%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
OpStore %31 %29
+%32 = OpFunctionCall %void %fn
+OpReturn
+OpFunctionEnd
+%fn = OpFunction %void None %3
+%33 = OpLabel
OpReturn
OpFunctionEnd
)");
@@ -782,12 +788,7 @@ OpReturn
OpFunctionEnd
)";
- EXPECT_EQ(RunPass(text), Pass::Status::Failure);
- const char expected_error[] =
- "ERROR: 0: Functions of SPIR-V for spread-volatile-semantics pass "
- "input must be inlined except entry points";
- EXPECT_STREQ(GetErrorMessage().substr(0, sizeof(expected_error) - 1).c_str(),
- expected_error);
+ EXPECT_EQ(RunPass(text), Pass::Status::SuccessWithoutChange);
}
TEST_F(VolatileSpreadErrorTest, VarNotUsedInEntryPointForVolatile) {
@@ -1113,6 +1114,154 @@ OpFunctionEnd
SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true);
}
+TEST_F(VolatileSpreadTest, SkipIfItHasNoExecutionModel) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ Pass::Status status;
+ std::tie(std::ignore, status) =
+ SinglePassRunToBinary<SpreadVolatileSemantics>(text,
+ /* skip_nop = */ false);
+ EXPECT_EQ(status, Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(VolatileSpreadTest, NoInlinedfuncCalls) {
+ const std::string text = R"(
+OpCapability RayTracingNV
+OpCapability VulkanMemoryModel
+OpCapability GroupNonUniform
+OpExtension "SPV_NV_ray_tracing"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpMemoryModel Logical Vulkan
+OpEntryPoint RayGenerationNV %main "main" %SubgroupSize
+OpSource HLSL 630
+OpName %main "main"
+OpName %src_main "src.main"
+OpName %bb_entry "bb.entry"
+OpName %func0 "func0"
+OpName %bb_entry_0 "bb.entry"
+OpName %func2 "func2"
+OpName %bb_entry_1 "bb.entry"
+OpName %param_var_count "param.var.count"
+OpName %func1 "func1"
+OpName %bb_entry_2 "bb.entry"
+OpName %func3 "func3"
+OpName %count "count"
+OpName %bb_entry_3 "bb.entry"
+OpDecorate %SubgroupSize BuiltIn SubgroupSize
+%uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%void = OpTypeVoid
+%6 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+%25 = OpTypeFunction %void %_ptr_Function_uint
+%SubgroupSize = OpVariable %_ptr_Input_uint Input
+%main = OpFunction %void None %6
+%7 = OpLabel
+%8 = OpFunctionCall %void %src_main
+OpReturn
+OpFunctionEnd
+%src_main = OpFunction %void None %6
+%bb_entry = OpLabel
+%11 = OpFunctionCall %void %func0
+OpReturn
+OpFunctionEnd
+%func0 = OpFunction %void DontInline %6
+%bb_entry_0 = OpLabel
+%14 = OpFunctionCall %void %func2
+%16 = OpFunctionCall %void %func1
+OpReturn
+OpFunctionEnd
+%func2 = OpFunction %void DontInline %6
+%bb_entry_1 = OpLabel
+%param_var_count = OpVariable %_ptr_Function_uint Function
+; CHECK: {{%\w+}} = OpLoad %uint %SubgroupSize Volatile
+%21 = OpLoad %uint %SubgroupSize
+OpStore %param_var_count %21
+%22 = OpFunctionCall %void %func3 %param_var_count
+OpReturn
+OpFunctionEnd
+%func1 = OpFunction %void DontInline %6
+%bb_entry_2 = OpLabel
+OpReturn
+OpFunctionEnd
+%func3 = OpFunction %void DontInline %25
+%count = OpFunctionParameter %_ptr_Function_uint
+%bb_entry_3 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+ SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true);
+}
+
+TEST_F(VolatileSpreadErrorTest, NoInlinedMultiEntryfuncCalls) {
+ const std::string text = R"(
+OpCapability RayTracingNV
+OpCapability SubgroupBallotKHR
+OpExtension "SPV_NV_ray_tracing"
+OpExtension "SPV_KHR_shader_ballot"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint RayGenerationNV %main "main" %SubgroupSize
+OpEntryPoint GLCompute %main2 "main2" %gl_LocalInvocationIndex %SubgroupSize
+OpSource HLSL 630
+OpName %main "main"
+OpName %bb_entry "bb.entry"
+OpName %main2 "main2"
+OpName %bb_entry_0 "bb.entry"
+OpName %func "func"
+OpName %count "count"
+OpName %bb_entry_1 "bb.entry"
+OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex
+OpDecorate %SubgroupSize BuiltIn SubgroupSize
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%void = OpTypeVoid
+%12 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%29 = OpTypeFunction %void %_ptr_Function_v4float
+%34 = OpTypeFunction %void %_ptr_Function_uint
+%SubgroupSize = OpVariable %_ptr_Input_uint Input
+%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
+%main = OpFunction %void None %12
+%bb_entry = OpLabel
+%20 = OpFunctionCall %void %func
+OpReturn
+OpFunctionEnd
+%main2 = OpFunction %void None %12
+%bb_entry_0 = OpLabel
+%33 = OpFunctionCall %void %func
+OpReturn
+OpFunctionEnd
+%func = OpFunction %void DontInline %12
+%bb_entry_1 = OpLabel
+%count = OpVariable %_ptr_Function_uint Function
+%35 = OpLoad %uint %SubgroupSize
+OpStore %count %35
+OpReturn
+OpFunctionEnd
+)";
+ EXPECT_EQ(RunPass(text), Pass::Status::Failure);
+ const char expected_error[] =
+ "ERROR: 0: Variable is a target for Volatile semantics for an entry "
+ "point, but it is not for another entry point";
+ EXPECT_STREQ(GetErrorMessage().substr(0, sizeof(expected_error) - 1).c_str(),
+ expected_error);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/type_manager_test.cpp b/test/opt/type_manager_test.cpp
index fdae2efc..df216bc9 100644
--- a/test/opt/type_manager_test.cpp
+++ b/test/opt/type_manager_test.cpp
@@ -167,10 +167,25 @@ std::vector<std::unique_ptr<Type>> GenerateAllTypes() {
types.emplace_back(new NamedBarrier());
types.emplace_back(new AccelerationStructureNV());
types.emplace_back(new CooperativeMatrixNV(f32, 24, 24, 24));
+ types.emplace_back(new RayQueryKHR());
return types;
}
+TEST(TypeManager, GenerateAllTypesGeneratesAllTypes) {
+ std::set<Type::Kind> generated_types;
+ for (auto& type : GenerateAllTypes()) {
+ generated_types.insert(type->kind());
+ }
+
+ std::vector<Type::Kind> all_types;
+ for (uint32_t kind = 0; kind != Type::Kind::kLast; ++kind) {
+ all_types.push_back(static_cast<Type::Kind>(kind));
+ }
+
+ EXPECT_THAT(generated_types, testing::UnorderedElementsAreArray(all_types));
+}
+
TEST(TypeManager, TypeStrings) {
const std::string text = R"(
OpDecorate %spec_const_with_id SpecId 99
@@ -1065,6 +1080,7 @@ TEST(TypeManager, GetTypeInstructionAllTypes) {
; CHECK: OpTypeNamedBarrier
; CHECK: OpTypeAccelerationStructureKHR
; CHECK: OpTypeCooperativeMatrixNV [[f32]] [[uint24]] [[uint24]] [[uint24]]
+; CHECK: OpTypeRayQueryKHR
OpCapability Shader
OpCapability Int64
OpCapability Linkage
diff --git a/test/opt/types_test.cpp b/test/opt/types_test.cpp
index 82e40405..552ad97c 100644
--- a/test/opt/types_test.cpp
+++ b/test/opt/types_test.cpp
@@ -266,6 +266,67 @@ TEST(Types, AllTypes) {
}
}
+TEST(Types, TestNumberOfComponentsOnArrays) {
+ Float f32(32);
+ EXPECT_EQ(f32.NumberOfComponents(), 0);
+
+ Array array_size_42(
+ &f32, Array::LengthInfo{99u, {Array::LengthInfo::kConstant, 42u}});
+ EXPECT_EQ(array_size_42.NumberOfComponents(), 42);
+
+ Array array_size_0xDEADBEEF00C0FFEE(
+ &f32, Array::LengthInfo{
+ 99u, {Array::LengthInfo::kConstant, 0xC0FFEE, 0xDEADBEEF}});
+ EXPECT_EQ(array_size_0xDEADBEEF00C0FFEE.NumberOfComponents(),
+ 0xDEADBEEF00C0FFEEull);
+
+ Array array_size_unknown(
+ &f32,
+ Array::LengthInfo{99u, {Array::LengthInfo::kConstantWithSpecId, 10}});
+ EXPECT_EQ(array_size_unknown.NumberOfComponents(), UINT64_MAX);
+
+ RuntimeArray runtime_array(&f32);
+ EXPECT_EQ(runtime_array.NumberOfComponents(), UINT64_MAX);
+}
+
+TEST(Types, TestNumberOfComponentsOnVectors) {
+ Float f32(32);
+ EXPECT_EQ(f32.NumberOfComponents(), 0);
+
+ for (uint32_t vector_size = 1; vector_size < 4; ++vector_size) {
+ Vector vector(&f32, vector_size);
+ EXPECT_EQ(vector.NumberOfComponents(), vector_size);
+ }
+}
+
+TEST(Types, TestNumberOfComponentsOnMatrices) {
+ Float f32(32);
+ Vector vector(&f32, 2);
+
+ for (uint32_t number_of_columns = 1; number_of_columns < 4;
+ ++number_of_columns) {
+ Matrix matrix(&vector, number_of_columns);
+ EXPECT_EQ(matrix.NumberOfComponents(), number_of_columns);
+ }
+}
+
+TEST(Types, TestNumberOfComponentsOnStructs) {
+ Float f32(32);
+ Vector vector(&f32, 2);
+
+ Struct empty_struct({});
+ EXPECT_EQ(empty_struct.NumberOfComponents(), 0);
+
+ Struct struct_f32({&f32});
+ EXPECT_EQ(struct_f32.NumberOfComponents(), 1);
+
+ Struct struct_f32_vec({&f32, &vector});
+ EXPECT_EQ(struct_f32_vec.NumberOfComponents(), 2);
+
+ Struct struct_100xf32(std::vector<const Type*>(100, &f32));
+ EXPECT_EQ(struct_100xf32.NumberOfComponents(), 100);
+}
+
TEST(Types, IntSignedness) {
std::vector<bool> signednesses = {true, false, false, true};
std::vector<std::unique_ptr<Integer>> types;
diff --git a/test/reduce/structured_loop_to_selection_test.cpp b/test/reduce/structured_loop_to_selection_test.cpp
index 0cfcfdff..d203f3ea 100644
--- a/test/reduce/structured_loop_to_selection_test.cpp
+++ b/test/reduce/structured_loop_to_selection_test.cpp
@@ -2957,7 +2957,7 @@ TEST(StructuredLoopToSelectionReductionPassTest,
OpLoopMerge %12 %13 None
OpBranch %12
%13 = OpLabel
- OpBranchConditional %6 %9 %11
+ OpBranch %11
%12 = OpLabel
OpBranch %10
%10 = OpLabel
@@ -2999,7 +2999,7 @@ TEST(StructuredLoopToSelectionReductionPassTest,
OpLoopMerge %12 %13 None
OpBranch %12
%13 = OpLabel
- OpBranchConditional %6 %9 %11
+ OpBranch %11
%12 = OpLabel
OpBranch %9
%10 = OpLabel
@@ -3036,7 +3036,7 @@ TEST(StructuredLoopToSelectionReductionPassTest,
OpSelectionMerge %12 None
OpBranchConditional %6 %12 %12
%13 = OpLabel
- OpBranchConditional %6 %9 %11
+ OpBranch %11
%12 = OpLabel
OpBranch %9
%10 = OpLabel
@@ -3050,8 +3050,7 @@ TEST(StructuredLoopToSelectionReductionPassTest,
TEST(StructuredLoopToSelectionReductionPassTest,
UnreachableInnerLoopContinueBranchingToOuterLoopMerge2) {
- // In this test, the branch to the outer loop merge from the inner loop's
- // continue is part of a structured selection.
+ // In this test, the unreachable continue is composed of multiple blocks.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@@ -3073,8 +3072,7 @@ TEST(StructuredLoopToSelectionReductionPassTest,
OpLoopMerge %12 %13 None
OpBranch %12
%13 = OpLabel
- OpSelectionMerge %14 None
- OpBranchConditional %6 %9 %14
+ OpBranch %14
%14 = OpLabel
OpBranch %11
%12 = OpLabel
@@ -3118,8 +3116,7 @@ TEST(StructuredLoopToSelectionReductionPassTest,
OpLoopMerge %12 %13 None
OpBranch %12
%13 = OpLabel
- OpSelectionMerge %14 None
- OpBranchConditional %6 %9 %14
+ OpBranch %14
%14 = OpLabel
OpBranch %11
%12 = OpLabel
@@ -3158,8 +3155,7 @@ TEST(StructuredLoopToSelectionReductionPassTest,
OpSelectionMerge %12 None
OpBranchConditional %6 %12 %12
%13 = OpLabel
- OpSelectionMerge %14 None
- OpBranchConditional %6 %9 %14
+ OpBranch %14
%14 = OpLabel
OpBranch %11
%12 = OpLabel
diff --git a/test/text_advance_test.cpp b/test/text_advance_test.cpp
index 9de77a83..0d23ab1c 100644
--- a/test/text_advance_test.cpp
+++ b/test/text_advance_test.cpp
@@ -130,5 +130,14 @@ TEST(TextAdvance, SkipOverCRLFs) {
EXPECT_EQ(2u, pos.line);
EXPECT_EQ(4u, pos.index);
}
+
+TEST(TextAdvance, HandleLotsOfWhitespace) {
+ std::string lots_of_spaces(10000, ' ');
+ lots_of_spaces += "Word";
+ const auto pos = PositionAfterAdvance(lots_of_spaces.c_str());
+ EXPECT_EQ(10000u, pos.column);
+ EXPECT_EQ(0u, pos.line);
+ EXPECT_EQ(10000u, pos.index);
+}
} // namespace
} // namespace spvtools
diff --git a/test/text_to_binary.annotation_test.cpp b/test/text_to_binary.annotation_test.cpp
index 61bdf64c..76776de9 100644
--- a/test/text_to_binary.annotation_test.cpp
+++ b/test/text_to_binary.annotation_test.cpp
@@ -398,7 +398,8 @@ TEST_F(TextToBinaryTest, GroupMemberDecorateGoodTwoTargets) {
TEST_F(TextToBinaryTest, GroupMemberDecorateMissingGroupId) {
EXPECT_THAT(CompileFailure("OpGroupMemberDecorate"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpGroupMemberDecorate instruction, but "
+ "found the end of the stream."));
}
TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidGroupId) {
@@ -413,7 +414,8 @@ TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidTargetId) {
TEST_F(TextToBinaryTest, GroupMemberDecorateMissingTargetMemberNumber) {
EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id0"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpGroupMemberDecorate instruction, but "
+ "found the end of the stream."));
}
TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidTargetMemberNumber) {
@@ -428,7 +430,8 @@ TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidSecondTargetId) {
TEST_F(TextToBinaryTest, GroupMemberDecorateMissingSecondTargetMemberNumber) {
EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id0 42 %id1"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpGroupMemberDecorate instruction, but "
+ "found the end of the stream."));
}
TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidSecondTargetMemberNumber) {
diff --git a/test/text_to_binary.barrier_test.cpp b/test/text_to_binary.barrier_test.cpp
index 545d26ff..f1cb4fbe 100644
--- a/test/text_to_binary.barrier_test.cpp
+++ b/test/text_to_binary.barrier_test.cpp
@@ -44,7 +44,8 @@ TEST_F(OpMemoryBarrier, Good) {
TEST_F(OpMemoryBarrier, BadMissingScopeId) {
const std::string input = "OpMemoryBarrier\n";
EXPECT_THAT(CompileFailure(input),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpMemoryBarrier instruction, but found "
+ "the end of the stream."));
}
TEST_F(OpMemoryBarrier, BadInvalidScopeId) {
@@ -55,7 +56,8 @@ TEST_F(OpMemoryBarrier, BadInvalidScopeId) {
TEST_F(OpMemoryBarrier, BadMissingMemorySemanticsId) {
const std::string input = "OpMemoryBarrier %scope\n";
EXPECT_THAT(CompileFailure(input),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpMemoryBarrier instruction, but found "
+ "the end of the stream."));
}
TEST_F(OpMemoryBarrier, BadInvalidMemorySemanticsId) {
@@ -92,13 +94,16 @@ TEST_F(NamedMemoryBarrierTest, OpcodeAssemblesInV10) {
TEST_F(NamedMemoryBarrierTest, ArgumentCount) {
EXPECT_THAT(CompileFailure("OpMemoryNamedBarrier", SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpMemoryNamedBarrier instruction, but "
+ "found the end of the stream."));
EXPECT_THAT(
CompileFailure("OpMemoryNamedBarrier %bar", SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpMemoryNamedBarrier instruction, but found the "
+ "end of the stream."));
EXPECT_THAT(
CompileFailure("OpMemoryNamedBarrier %bar %scope", SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpMemoryNamedBarrier instruction, but found the "
+ "end of the stream."));
EXPECT_THAT(
CompiledInstructions("OpMemoryNamedBarrier %bar %scope %semantics",
SPV_ENV_UNIVERSAL_1_1),
@@ -151,10 +156,12 @@ TEST_F(NamedBarrierInitializeTest, OpcodeAssemblesInV10) {
TEST_F(NamedBarrierInitializeTest, ArgumentCount) {
EXPECT_THAT(
CompileFailure("%bar = OpNamedBarrierInitialize", SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpNamedBarrierInitialize instruction, but found "
+ "the end of the stream."));
EXPECT_THAT(CompileFailure("%bar = OpNamedBarrierInitialize %ype",
SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpNamedBarrierInitialize instruction, "
+ "but found the end of the stream."));
EXPECT_THAT(
CompiledInstructions("%bar = OpNamedBarrierInitialize %type %count",
SPV_ENV_UNIVERSAL_1_1),
diff --git a/test/text_to_binary.control_flow_test.cpp b/test/text_to_binary.control_flow_test.cpp
index abae6a22..472cb6da 100644
--- a/test/text_to_binary.control_flow_test.cpp
+++ b/test/text_to_binary.control_flow_test.cpp
@@ -163,7 +163,8 @@ TEST_F(TextToBinaryTest, SwitchGoodTwoTargets) {
TEST_F(TextToBinaryTest, SwitchBadMissingSelector) {
EXPECT_THAT(CompileFailure("OpSwitch"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpSwitch instruction, but found the end "
+ "of the stream."));
}
TEST_F(TextToBinaryTest, SwitchBadInvalidSelector) {
@@ -173,7 +174,8 @@ TEST_F(TextToBinaryTest, SwitchBadInvalidSelector) {
TEST_F(TextToBinaryTest, SwitchBadMissingDefault) {
EXPECT_THAT(CompileFailure("OpSwitch %selector"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpSwitch instruction, but found the end "
+ "of the stream."));
}
TEST_F(TextToBinaryTest, SwitchBadInvalidDefault) {
@@ -195,7 +197,8 @@ TEST_F(TextToBinaryTest, SwitchBadMissingTarget) {
EXPECT_THAT(CompileFailure("%1 = OpTypeInt 32 0\n"
"%2 = OpConstant %1 52\n"
"OpSwitch %2 %default 12"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpSwitch instruction, but found the end "
+ "of the stream."));
}
// A test case for an OpSwitch.
diff --git a/test/text_to_binary.device_side_enqueue_test.cpp b/test/text_to_binary.device_side_enqueue_test.cpp
index 03d7e741..2f4dd705 100644
--- a/test/text_to_binary.device_side_enqueue_test.cpp
+++ b/test/text_to_binary.device_side_enqueue_test.cpp
@@ -83,7 +83,8 @@ TEST_F(OpKernelEnqueueBad, MissingLastOperand) {
CompileFailure(
"%result = OpEnqueueKernel %type %queue %flags %NDRange %num_events"
" %wait_events %ret_event %invoke %param %param_size"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpEnqueueKernel instruction, but found the end "
+ "of the stream."));
}
TEST_F(OpKernelEnqueueBad, InvalidLastOperand) {
diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp
index e5f152e4..cf4919ac 100644
--- a/test/text_to_binary.extension_test.cpp
+++ b/test/text_to_binary.extension_test.cpp
@@ -1034,5 +1034,68 @@ INSTANTIATE_TEST_SUITE_P(
{SpvCapabilityBitInstructions})},
})));
+// SPV_KHR_uniform_group_instructions
+
+INSTANTIATE_TEST_SUITE_P(
+ SPV_KHR_uniform_group_instructions, ExtensionRoundTripTest,
+ Combine(
+ Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5,
+ SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1,
+ SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3),
+ ValuesIn(std::vector<AssemblyCase>{
+ {"OpExtension \"SPV_KHR_uniform_group_instructions\"\n",
+ MakeInstruction(SpvOpExtension,
+ MakeVector("SPV_KHR_uniform_group_instructions"))},
+ {"OpCapability GroupUniformArithmeticKHR\n",
+ MakeInstruction(SpvOpCapability,
+ {SpvCapabilityGroupUniformArithmeticKHR})},
+ {"%2 = OpGroupIMulKHR %1 %3 Reduce %4\n",
+ MakeInstruction(SpvOpGroupIMulKHR,
+ {1, 2, 3, SpvGroupOperationReduce, 4})},
+ {"%2 = OpGroupFMulKHR %1 %3 Reduce %4\n",
+ MakeInstruction(SpvOpGroupFMulKHR,
+ {1, 2, 3, SpvGroupOperationReduce, 4})},
+ {"%2 = OpGroupBitwiseAndKHR %1 %3 Reduce %4\n",
+ MakeInstruction(SpvOpGroupBitwiseAndKHR,
+ {1, 2, 3, SpvGroupOperationReduce, 4})},
+ {"%2 = OpGroupBitwiseOrKHR %1 %3 Reduce %4\n",
+ MakeInstruction(SpvOpGroupBitwiseOrKHR,
+ {1, 2, 3, SpvGroupOperationReduce, 4})},
+ {"%2 = OpGroupBitwiseXorKHR %1 %3 Reduce %4\n",
+ MakeInstruction(SpvOpGroupBitwiseXorKHR,
+ {1, 2, 3, SpvGroupOperationReduce, 4})},
+ {"%2 = OpGroupLogicalAndKHR %1 %3 Reduce %4\n",
+ MakeInstruction(SpvOpGroupLogicalAndKHR,
+ {1, 2, 3, SpvGroupOperationReduce, 4})},
+ {"%2 = OpGroupLogicalOrKHR %1 %3 Reduce %4\n",
+ MakeInstruction(SpvOpGroupLogicalOrKHR,
+ {1, 2, 3, SpvGroupOperationReduce, 4})},
+ {"%2 = OpGroupLogicalXorKHR %1 %3 Reduce %4\n",
+ MakeInstruction(SpvOpGroupLogicalXorKHR,
+ {1, 2, 3, SpvGroupOperationReduce, 4})},
+ })));
+
+// SPV_KHR_subgroup_rotate
+
+INSTANTIATE_TEST_SUITE_P(
+ SPV_KHR_subgroup_rotate, ExtensionRoundTripTest,
+ Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6,
+ SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2,
+ SPV_ENV_VULKAN_1_3, SPV_ENV_OPENCL_2_1),
+ ValuesIn(std::vector<AssemblyCase>{
+ {"OpExtension \"SPV_KHR_subgroup_rotate\"\n",
+ MakeInstruction(SpvOpExtension,
+ MakeVector("SPV_KHR_subgroup_rotate"))},
+ {"OpCapability GroupNonUniformRotateKHR\n",
+ MakeInstruction(SpvOpCapability,
+ {SpvCapabilityGroupNonUniformRotateKHR})},
+ {"%2 = OpGroupNonUniformRotateKHR %1 %3 %4 %5\n",
+ MakeInstruction(SpvOpGroupNonUniformRotateKHR,
+ {1, 2, 3, 4, 5})},
+ {"%2 = OpGroupNonUniformRotateKHR %1 %3 %4 %5 %6\n",
+ MakeInstruction(SpvOpGroupNonUniformRotateKHR,
+ {1, 2, 3, 4, 5, 6})},
+ })));
+
} // namespace
} // namespace spvtools
diff --git a/test/text_to_binary.image_test.cpp b/test/text_to_binary.image_test.cpp
index d445369c..8d8ff432 100644
--- a/test/text_to_binary.image_test.cpp
+++ b/test/text_to_binary.image_test.cpp
@@ -123,7 +123,8 @@ TEST_F(OpImageTest, InvalidTypeOperand) {
TEST_F(OpImageTest, MissingSampledImageOperand) {
EXPECT_THAT(CompileFailure("%2 = OpImage %1"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpImage instruction, but found the end "
+ "of the stream."));
}
TEST_F(OpImageTest, InvalidSampledImageOperand) {
@@ -222,7 +223,8 @@ TEST_F(OpImageSparseReadTest, InvalidTypeOperand) {
TEST_F(OpImageSparseReadTest, MissingImageOperand) {
EXPECT_THAT(CompileFailure("%2 = OpImageSparseRead %1"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpImageSparseRead instruction, but "
+ "found the end of the stream."));
}
TEST_F(OpImageSparseReadTest, InvalidImageOperand) {
@@ -232,7 +234,8 @@ TEST_F(OpImageSparseReadTest, InvalidImageOperand) {
TEST_F(OpImageSparseReadTest, MissingCoordinateOperand) {
EXPECT_THAT(CompileFailure("%2 = OpImageSparseRead %1 %2"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpImageSparseRead instruction, but "
+ "found the end of the stream."));
}
TEST_F(OpImageSparseReadTest, InvalidCoordinateOperand) {
diff --git a/test/text_to_binary.memory_test.cpp b/test/text_to_binary.memory_test.cpp
index 7b09ed58..f94c134a 100644
--- a/test/text_to_binary.memory_test.cpp
+++ b/test/text_to_binary.memory_test.cpp
@@ -166,7 +166,8 @@ TEST_F(MemoryRoundTripTest, OpCopyMemoryNoMemAccessGood) {
TEST_F(MemoryRoundTripTest, OpCopyMemoryTooFewArgsBad) {
std::string spirv = "OpCopyMemory %1\n";
std::string err = CompileFailure(spirv);
- EXPECT_THAT(err, HasSubstr("Expected operand, found end of stream"));
+ EXPECT_THAT(err, HasSubstr("Expected operand for OpCopyMemory instruction, "
+ "but found the end of the stream."));
}
TEST_F(MemoryRoundTripTest, OpCopyMemoryTooManyArgsBad) {
@@ -295,7 +296,8 @@ TEST_F(MemoryRoundTripTest, OpCopyMemorySizedNoMemAccessGood) {
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooFewArgsBad) {
std::string spirv = "OpCopyMemorySized %1 %2\n";
std::string err = CompileFailure(spirv);
- EXPECT_THAT(err, HasSubstr("Expected operand, found end of stream"));
+ EXPECT_THAT(err, HasSubstr("Expected operand for OpCopyMemorySized "
+ "instruction, but found the end of the stream."));
}
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooManyArgsBad) {
diff --git a/test/text_to_binary.mode_setting_test.cpp b/test/text_to_binary.mode_setting_test.cpp
index 647bb3d9..7f15c8b4 100644
--- a/test/text_to_binary.mode_setting_test.cpp
+++ b/test/text_to_binary.mode_setting_test.cpp
@@ -290,7 +290,8 @@ using TextToBinaryCapability = spvtest::TextToBinaryTest;
TEST_F(TextToBinaryCapability, BadMissingCapability) {
EXPECT_THAT(CompileFailure("OpCapability"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpCapability instruction, but found the "
+ "end of the stream."));
}
TEST_F(TextToBinaryCapability, BadInvalidCapability) {
diff --git a/test/text_to_binary.pipe_storage_test.cpp b/test/text_to_binary.pipe_storage_test.cpp
index f74dbcfd..955f5ef8 100644
--- a/test/text_to_binary.pipe_storage_test.cpp
+++ b/test/text_to_binary.pipe_storage_test.cpp
@@ -59,10 +59,12 @@ TEST_F(OpConstantPipeStorageTest, ArgumentCount) {
"'OpConstantPipeStorage'."));
EXPECT_THAT(
CompileFailure("%1 = OpConstantPipeStorage", SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpConstantPipeStorage instruction, but found "
+ "the end of the stream."));
EXPECT_THAT(CompileFailure("%1 = OpConstantPipeStorage %2 3 4",
SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpConstantPipeStorage instruction, but "
+ "found the end of the stream."));
EXPECT_THAT(CompiledInstructions("%1 = OpConstantPipeStorage %2 3 4 5",
SPV_ENV_UNIVERSAL_1_1),
Eq(MakeInstruction(SpvOpConstantPipeStorage, {1, 2, 3, 4, 5})));
@@ -101,10 +103,12 @@ TEST_F(OpCreatePipeFromPipeStorageTest, ArgumentCount) {
"'OpCreatePipeFromPipeStorage'."));
EXPECT_THAT(
CompileFailure("%1 = OpCreatePipeFromPipeStorage", SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpCreatePipeFromPipeStorage instruction, but "
+ "found the end of the stream."));
EXPECT_THAT(CompileFailure("%1 = OpCreatePipeFromPipeStorage %2 OpNop",
SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found next instruction instead."));
+ Eq("Expected operand for OpCreatePipeFromPipeStorage "
+ "instruction, but found the next instruction instead."));
EXPECT_THAT(CompiledInstructions("%1 = OpCreatePipeFromPipeStorage %2 %3",
SPV_ENV_UNIVERSAL_1_1),
Eq(MakeInstruction(SpvOpCreatePipeFromPipeStorage, {1, 2, 3})));
diff --git a/test/text_to_binary.subgroup_dispatch_test.cpp b/test/text_to_binary.subgroup_dispatch_test.cpp
index 967e3c38..8c404457 100644
--- a/test/text_to_binary.subgroup_dispatch_test.cpp
+++ b/test/text_to_binary.subgroup_dispatch_test.cpp
@@ -46,11 +46,13 @@ TEST_F(OpGetKernelLocalSizeForSubgroupCountTest, ArgumentCount) {
"found 'OpGetKernelLocalSizeForSubgroupCount'."));
EXPECT_THAT(CompileFailure("%res = OpGetKernelLocalSizeForSubgroupCount",
SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpGetKernelLocalSizeForSubgroupCount "
+ "instruction, but found the end of the stream."));
EXPECT_THAT(
CompileFailure("%1 = OpGetKernelLocalSizeForSubgroupCount %2 %3 %4 %5 %6",
SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpGetKernelLocalSizeForSubgroupCount "
+ "instruction, but found the end of the stream."));
EXPECT_THAT(
CompiledInstructions("%res = OpGetKernelLocalSizeForSubgroupCount %type "
"%sgcount %invoke %param %param_size %param_align",
@@ -93,10 +95,12 @@ TEST_F(OpGetKernelMaxNumSubgroupsTest, ArgumentCount) {
"'OpGetKernelMaxNumSubgroups'."));
EXPECT_THAT(CompileFailure("%res = OpGetKernelMaxNumSubgroups",
SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpGetKernelMaxNumSubgroups instruction, "
+ "but found the end of the stream."));
EXPECT_THAT(CompileFailure("%1 = OpGetKernelMaxNumSubgroups %2 %3 %4 %5",
SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpGetKernelMaxNumSubgroups instruction, "
+ "but found the end of the stream."));
EXPECT_THAT(
CompiledInstructions("%res = OpGetKernelMaxNumSubgroups %type "
"%invoke %param %param_size %param_align",
diff --git a/test/text_to_binary.type_declaration_test.cpp b/test/text_to_binary.type_declaration_test.cpp
index 1589188f..65a23554 100644
--- a/test/text_to_binary.type_declaration_test.cpp
+++ b/test/text_to_binary.type_declaration_test.cpp
@@ -223,12 +223,14 @@ TEST_F(OpTypeForwardPointerTest, ValidStorageClass) {
TEST_F(OpTypeForwardPointerTest, MissingType) {
EXPECT_THAT(CompileFailure("OpTypeForwardPointer"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpTypeForwardPointer instruction, but "
+ "found the end of the stream."));
}
TEST_F(OpTypeForwardPointerTest, MissingClass) {
EXPECT_THAT(CompileFailure("OpTypeForwardPointer %pt"),
- Eq("Expected operand, found end of stream."));
+ Eq("Expected operand for OpTypeForwardPointer instruction, but "
+ "found the end of the stream."));
}
TEST_F(OpTypeForwardPointerTest, WrongClass) {
@@ -252,7 +254,8 @@ TEST_F(OpSizeOfTest, ArgumentCount) {
Eq("Expected <result-id> at the beginning of an instruction, found "
"'OpSizeOf'."));
EXPECT_THAT(CompileFailure("%res = OpSizeOf OpNop", SPV_ENV_UNIVERSAL_1_1),
- Eq("Expected operand, found next instruction instead."));
+ Eq("Expected operand for OpSizeOf instruction, but found the "
+ "next instruction instead."));
EXPECT_THAT(
CompiledInstructions("%1 = OpSizeOf %2 %3", SPV_ENV_UNIVERSAL_1_1),
Eq(MakeInstruction(SpvOpSizeOf, {1, 2, 3})));
diff --git a/test/util/CMakeLists.txt b/test/util/CMakeLists.txt
index 6679dba7..20038f76 100644
--- a/test/util/CMakeLists.txt
+++ b/test/util/CMakeLists.txt
@@ -16,6 +16,7 @@ add_spvtools_unittest(TARGET utils
SRCS ilist_test.cpp
bit_vector_test.cpp
bitutils_test.cpp
+ hash_combine_test.cpp
small_vector_test.cpp
LIBS SPIRV-Tools-opt
)
diff --git a/test/util/hash_combine_test.cpp b/test/util/hash_combine_test.cpp
new file mode 100644
index 00000000..9cd1d870
--- /dev/null
+++ b/test/util/hash_combine_test.cpp
@@ -0,0 +1,43 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+//
+// 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 <utility>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "source/util/hash_combine.h"
+
+namespace spvtools {
+namespace utils {
+namespace {
+
+using HashCombineTest = ::testing::Test;
+
+TEST(HashCombineTest, Identity) { EXPECT_EQ(hash_combine(0), 0); }
+
+TEST(HashCombineTest, Variadic) {
+ // Expect manual and variadic template versions be the same.
+ EXPECT_EQ(hash_combine(hash_combine(hash_combine(0, 1), 2), 3),
+ hash_combine(0, 1, 2, 3));
+}
+
+TEST(HashCombineTest, Vector) {
+ // Expect variadic and vector versions be the same.
+ EXPECT_EQ(hash_combine(0, std::vector<uint32_t>({1, 2, 3})),
+ hash_combine(0, 1, 2, 3));
+}
+
+} // namespace
+} // namespace utils
+} // namespace spvtools
diff --git a/test/util/small_vector_test.cpp b/test/util/small_vector_test.cpp
index 01d7df18..b8f068eb 100644
--- a/test/util/small_vector_test.cpp
+++ b/test/util/small_vector_test.cpp
@@ -56,6 +56,18 @@ TEST(SmallVectorTest, Initialize_list2) {
}
}
+TEST(SmallVectorTest, Initialize_list3) {
+ std::vector<uint32_t> result = {0, 1, 2, 3};
+ SmallVector<uint32_t, 6> vec(result.begin(), result.end());
+
+ EXPECT_FALSE(vec.empty());
+ EXPECT_EQ(vec.size(), 4);
+
+ for (uint32_t i = 0; i < vec.size(); ++i) {
+ EXPECT_EQ(vec[i], result[i]);
+ }
+}
+
TEST(SmallVectorTest, Initialize_copy1) {
SmallVector<uint32_t, 6> vec1 = {0, 1, 2, 3};
SmallVector<uint32_t, 6> vec2(vec1);
@@ -593,6 +605,68 @@ TEST(SmallVectorTest, Resize6) {
EXPECT_EQ(vec, result);
}
+TEST(SmallVectorTest, Pop_back) {
+ SmallVector<uint32_t, 8> vec = {0, 1, 2, 10, 10, 10};
+ SmallVector<uint32_t, 8> result = {0, 1, 2};
+
+ EXPECT_EQ(vec.size(), 6);
+ vec.pop_back();
+ vec.pop_back();
+ vec.pop_back();
+ EXPECT_EQ(vec.size(), 3);
+ EXPECT_EQ(vec, result);
+}
+
+TEST(SmallVectorTest, Pop_back_TestDestructor) {
+ // Tracks number of constructions and destructions to ensure they are called.
+ struct TracksDtor {
+ TracksDtor& operator=(TracksDtor&&) = delete;
+ TracksDtor& operator=(const TracksDtor&) = delete;
+
+ TracksDtor(int& num_ctors, int& num_dtors)
+ : num_ctors_(num_ctors), num_dtors_(num_dtors) {
+ num_ctors_++;
+ }
+ TracksDtor(const TracksDtor& that)
+ : TracksDtor(that.num_ctors_, that.num_dtors_) {}
+ TracksDtor(TracksDtor&& that)
+ : TracksDtor(that.num_ctors_, that.num_dtors_) {}
+ ~TracksDtor() { num_dtors_++; }
+
+ int& num_ctors_;
+ int& num_dtors_;
+ };
+
+ constexpr int capacity = 4;
+ SmallVector<TracksDtor, capacity> vec;
+
+ int num_ctors = 0;
+ int num_dtors = 0;
+
+ // Make sure it works when staying within the smallvector capacity
+ for (int i = 0; i < capacity; ++i) {
+ vec.emplace_back(num_ctors, num_dtors);
+ }
+
+ EXPECT_EQ(num_ctors, capacity);
+ while (!vec.empty()) {
+ vec.pop_back();
+ }
+
+ EXPECT_EQ(num_ctors, capacity);
+ EXPECT_EQ(num_dtors, num_ctors);
+
+ // And when larger than builtin capacity
+ for (int i = 0; i < capacity * 2; ++i) {
+ vec.emplace_back(num_ctors, num_dtors);
+ }
+
+ while (!vec.empty()) {
+ vec.pop_back();
+ }
+ EXPECT_EQ(num_dtors, num_ctors);
+}
+
} // namespace
} // namespace utils
} // namespace spvtools
diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt
index 64eba446..de89b93f 100644
--- a/test/val/CMakeLists.txt
+++ b/test/val/CMakeLists.txt
@@ -36,15 +36,16 @@ add_spvtools_unittest(TARGET val_abcde
val_data_test.cpp
val_decoration_test.cpp
val_derivatives_test.cpp
- val_entry_point.cpp
+ val_entry_point_test.cpp
val_explicit_reserved_test.cpp
val_extensions_test.cpp
- val_extension_spv_khr_expect_assume.cpp
- val_extension_spv_khr_linkonce_odr.cpp
- val_extension_spv_khr_subgroup_uniform_control_flow.cpp
- val_extension_spv_khr_integer_dot_product.cpp
- val_extension_spv_khr_bit_instructions.cpp
- val_extension_spv_khr_terminate_invocation.cpp
+ val_extension_spv_khr_expect_assume_test.cpp
+ val_extension_spv_khr_linkonce_odr_test.cpp
+ val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp
+ val_extension_spv_khr_integer_dot_product_test.cpp
+ val_extension_spv_khr_bit_instructions_test.cpp
+ val_extension_spv_khr_terminate_invocation_test.cpp
+ val_extension_spv_khr_subgroup_rotate_test.cpp
val_ext_inst_test.cpp
val_ext_inst_debug_test.cpp
${VAL_TEST_COMMON_SRCS}
@@ -76,6 +77,7 @@ add_spvtools_unittest(TARGET val_fghijklmnop
val_literals_test.cpp
val_logicals_test.cpp
val_memory_test.cpp
+ val_mesh_shading_test.cpp
val_misc_test.cpp
val_modes_test.cpp
val_non_semantic_test.cpp
@@ -87,8 +89,10 @@ add_spvtools_unittest(TARGET val_fghijklmnop
PCH_FILE pch_test_val
)
-add_spvtools_unittest(TARGET val_stuvw
+add_spvtools_unittest(TARGET val_rstuvw
SRCS
+ val_ray_query_test.cpp
+ val_ray_tracing_test.cpp
val_small_type_uses_test.cpp
val_ssa_test.cpp
val_state_test.cpp
diff --git a/test/val/val_annotation_test.cpp b/test/val/val_annotation_test.cpp
index 889c76ca..bb30de0a 100644
--- a/test/val/val_annotation_test.cpp
+++ b/test/val/val_annotation_test.cpp
@@ -754,6 +754,8 @@ OpFunctionEnd
CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Location-06672"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("decoration must not be applied to this storage class"));
@@ -794,6 +796,8 @@ OpFunctionEnd
CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("VUID-StandaloneSpirv-DescriptorSet-06491"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("must be in the StorageBuffer, Uniform, or "
"UniformConstant storage class"));
}
diff --git a/test/val/val_atomics_test.cpp b/test/val/val_atomics_test.cpp
index a0308d59..a7e4055a 100644
--- a/test/val/val_atomics_test.cpp
+++ b/test/val/val_atomics_test.cpp
@@ -778,8 +778,8 @@ OpAtomicStore %f32_var_function %device %relaxed %f32_1
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("AtomicStore: Vulkan spec only allows storage classes for "
- "atomic to be: Uniform, Workgroup, Image, StorageBuffer, or "
- "PhysicalStorageBuffer."));
+ "atomic to be: Uniform, Workgroup, Image, StorageBuffer, "
+ "PhysicalStorageBuffer or TaskPayloadWorkgroupEXT."));
}
TEST_F(ValidateAtomics, AtomicStoreFunctionPointerStorageType) {
diff --git a/test/val/val_barriers_test.cpp b/test/val/val_barriers_test.cpp
index 1178ca02..073a0f69 100644
--- a/test/val/val_barriers_test.cpp
+++ b/test/val/val_barriers_test.cpp
@@ -254,8 +254,9 @@ OpControlBarrier %device %device %none
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_2));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("OpControlBarrier requires one of the following Execution "
- "Models: TessellationControl, GLCompute or Kernel"));
+ HasSubstr("OpControlBarrier requires one of the following "
+ "Execution Models: TessellationControl, GLCompute, Kernel, "
+ "MeshNV or TaskNV"));
}
TEST_F(ValidateBarriers, OpControlBarrierExecutionModelFragmentSpirv13) {
@@ -360,11 +361,26 @@ OpControlBarrier %subgroup %subgroup %none
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
- AnyVUID("VUID-StandaloneSpirv-None-04638"));
+ AnyVUID("VUID-StandaloneSpirv-SubgroupVoteKHR-06997"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("ControlBarrier: in Vulkan 1.0 environment Memory Scope is "
- "limited to Device, Workgroup and Invocation"));
+ HasSubstr(
+ "ControlBarrier: in Vulkan 1.0 environment Memory Scope is can not "
+ "be Subgroup without SubgroupBallotKHR or SubgroupVoteKHR declared"));
+}
+
+TEST_F(ValidateBarriers, OpControlBarrierVulkanMemoryScopeSubgroupVoteKHR) {
+ const std::string capabilities = R"(
+OpCapability SubgroupVoteKHR
+OpExtension "SPV_KHR_subgroup_vote"
+)";
+ const std::string body = R"(
+OpControlBarrier %subgroup %subgroup %none
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, capabilities),
+ SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
}
TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1MemoryScopeSubgroup) {
@@ -386,8 +402,9 @@ OpControlBarrier %subgroup %cross_device %none
EXPECT_THAT(getDiagnosticString(),
AnyVUID("VUID-StandaloneSpirv-None-04638"));
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("ControlBarrier: in Vulkan environment, Memory Scope "
- "cannot be CrossDevice"));
+ HasSubstr("ControlBarrier: in Vulkan environment Memory Scope is "
+ "limited to Device, QueueFamily, Workgroup, "
+ "ShaderCallKHR, Subgroup, or Invocation"));
}
TEST_F(ValidateBarriers,
@@ -399,10 +416,12 @@ OpControlBarrier %subgroup %workgroup %acquire
CompileSuccessfully(GenerateVulkanVertexShaderCode(body), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
- AnyVUID("VUID-StandaloneSpirv-None-04639"));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Workgroup Memory Scope is limited to MeshNV, TaskNV, "
- "and GLCompute execution model"));
+ AnyVUID("VUID-StandaloneSpirv-None-07321"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Workgroup Memory Scope is limited to MeshNV, "
+ "TaskNV, MeshEXT, TaskEXT, TessellationControl, and GLCompute "
+ "execution model"));
}
TEST_F(ValidateBarriers,
@@ -417,8 +436,8 @@ OpControlBarrier %workgroup %subgroup %acquire
AnyVUID("VUID-StandaloneSpirv-None-04637"));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("in Vulkan environment, Workgroup execution scope is "
- "only for TaskNV, MeshNV, TessellationControl, and "
- "GLCompute execution models"));
+ "only for TaskNV, MeshNV, TaskEXT, MeshEXT, "
+ "TessellationControl, and GLCompute execution models"));
}
TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupComputeSuccess) {
@@ -528,10 +547,11 @@ OpControlBarrier %subgroup %workgroup %acquire_release
CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"),
SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("OpControlBarrier requires one of the following Execution "
- "Models: TessellationControl, GLCompute or Kernel"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("OpControlBarrier requires one of the following "
+ "Execution "
+ "Models: TessellationControl, GLCompute, Kernel, "
+ "MeshNV or TaskNV"));
}
TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionVertex1p1) {
@@ -572,8 +592,9 @@ OpControlBarrier %subgroup %workgroup %acquire_release
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("OpControlBarrier requires one of the following Execution "
- "Models: TessellationControl, GLCompute or Kernel"));
+ HasSubstr("OpControlBarrier requires one of the following "
+ "Execution Models: TessellationControl, GLCompute, Kernel, "
+ "MeshNV or TaskNV"));
}
TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionGeometry1p1) {
@@ -615,10 +636,11 @@ OpControlBarrier %subgroup %workgroup %acquire_release
GenerateShaderCode(body, "OpCapability Geometry\n", "Geometry"),
SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("OpControlBarrier requires one of the following Execution "
- "Models: TessellationControl, GLCompute or Kernel"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("OpControlBarrier requires one of the following "
+ "Execution "
+ "Models: TessellationControl, GLCompute, Kernel, "
+ "MeshNV or TaskNV"));
}
TEST_F(ValidateBarriers,
@@ -663,10 +685,11 @@ OpControlBarrier %subgroup %workgroup %acquire_release
"TessellationEvaluation"),
SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("OpControlBarrier requires one of the following Execution "
- "Models: TessellationControl, GLCompute or Kernel"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("OpControlBarrier requires one of the following "
+ "Execution "
+ "Models: TessellationControl, GLCompute, Kernel, "
+ "MeshNV or TaskNV"));
}
TEST_F(ValidateBarriers, OpMemoryBarrierSuccess) {
@@ -752,11 +775,12 @@ OpMemoryBarrier %subgroup %acquire_release_uniform_workgroup
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
- AnyVUID("VUID-StandaloneSpirv-None-04638"));
+ AnyVUID("VUID-StandaloneSpirv-SubgroupVoteKHR-06997"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("MemoryBarrier: in Vulkan 1.0 environment Memory Scope is "
- "limited to Device, Workgroup and Invocation"));
+ HasSubstr(
+ "MemoryBarrier: in Vulkan 1.0 environment Memory Scope is can not be "
+ "Subgroup without SubgroupBallotKHR or SubgroupVoteKHR declared"));
}
TEST_F(ValidateBarriers, OpMemoryBarrierVulkan1p1MemoryScopeSubgroup) {
diff --git a/test/val/val_bitwise_test.cpp b/test/val/val_bitwise_test.cpp
index 1001def8..bebaa84f 100644
--- a/test/val/val_bitwise_test.cpp
+++ b/test/val/val_bitwise_test.cpp
@@ -340,6 +340,16 @@ TEST_F(ValidateBitwise, OpBitFieldInsertSuccess) {
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
+TEST_F(ValidateBitwise, OpBitFieldInsertVulkanSuccess) {
+ const std::string body = R"(
+%val1 = OpBitFieldInsert %u32 %u32_1 %u32_2 %s32_1 %s32_2
+%val2 = OpBitFieldInsert %s32vec2 %s32vec2_12 %s32vec2_12 %s32_1 %u32_2
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
TEST_F(ValidateBitwise, OpBitFieldInsertWrongResultType) {
const std::string body = R"(
%val1 = OpBitFieldInsert %bool %u64_1 %u64_2 %s32_1 %s32_2
@@ -350,7 +360,7 @@ TEST_F(ValidateBitwise, OpBitFieldInsertWrongResultType) {
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
- "Expected int scalar or vector type as Result Type: BitFieldInsert"));
+ "Expected Base Type to be equal to Result Type: BitFieldInsert"));
}
TEST_F(ValidateBitwise, OpBitFieldInsertWrongBaseType) {
@@ -403,6 +413,20 @@ TEST_F(ValidateBitwise, OpBitFieldInsertCountNotInt) {
HasSubstr("Expected Count Type to be int scalar: BitFieldInsert"));
}
+TEST_F(ValidateBitwise, OpBitFieldInsertNot32Vulkan) {
+ const std::string body = R"(
+%val1 = OpBitFieldInsert %u64 %u64_1 %u64_2 %s32_1 %s32_2
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Base-04781"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Expected 32-bit int type for Base operand: BitFieldInsert"));
+}
+
TEST_F(ValidateBitwise, OpBitFieldSExtractSuccess) {
const std::string body = R"(
%val1 = OpBitFieldSExtract %u64 %u64_1 %s32_1 %s32_2
@@ -413,6 +437,16 @@ TEST_F(ValidateBitwise, OpBitFieldSExtractSuccess) {
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
+TEST_F(ValidateBitwise, OpBitFieldSExtractVulkanSuccess) {
+ const std::string body = R"(
+%val1 = OpBitFieldSExtract %u32 %u32_1 %s32_1 %s32_2
+%val2 = OpBitFieldSExtract %s32vec2 %s32vec2_12 %s32_1 %u32_2
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
TEST_F(ValidateBitwise, OpBitFieldSExtractWrongResultType) {
const std::string body = R"(
%val1 = OpBitFieldSExtract %bool %u64_1 %s32_1 %s32_2
@@ -420,9 +454,10 @@ TEST_F(ValidateBitwise, OpBitFieldSExtractWrongResultType) {
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Expected int scalar or vector type as Result Type: "
- "BitFieldSExtract"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Expected Base Type to be equal to Result Type: BitFieldSExtract"));
}
TEST_F(ValidateBitwise, OpBitFieldSExtractWrongBaseType) {
@@ -462,6 +497,20 @@ TEST_F(ValidateBitwise, OpBitFieldSExtractCountNotInt) {
HasSubstr("Expected Count Type to be int scalar: BitFieldSExtract"));
}
+TEST_F(ValidateBitwise, OpBitFieldSExtractNot32Vulkan) {
+ const std::string body = R"(
+%val1 = OpBitFieldSExtract %u64 %u64_1 %s32_1 %s32_2
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Base-04781"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Expected 32-bit int type for Base operand: BitFieldSExtract"));
+}
+
TEST_F(ValidateBitwise, OpBitReverseSuccess) {
const std::string body = R"(
%val1 = OpBitReverse %u64 %u64_1
@@ -472,6 +521,16 @@ TEST_F(ValidateBitwise, OpBitReverseSuccess) {
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
+TEST_F(ValidateBitwise, OpBitReverseVulkanSuccess) {
+ const std::string body = R"(
+%val1 = OpBitReverse %u32 %u32_1
+%val2 = OpBitReverse %s32vec2 %s32vec2_12
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
TEST_F(ValidateBitwise, OpBitReverseWrongResultType) {
const std::string body = R"(
%val1 = OpBitReverse %bool %u64_1
@@ -481,8 +540,7 @@ TEST_F(ValidateBitwise, OpBitReverseWrongResultType) {
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr(
- "Expected int scalar or vector type as Result Type: BitReverse"));
+ HasSubstr("Expected Base Type to be equal to Result Type: BitReverse"));
}
TEST_F(ValidateBitwise, OpBitReverseWrongBaseType) {
@@ -497,16 +555,41 @@ TEST_F(ValidateBitwise, OpBitReverseWrongBaseType) {
HasSubstr("Expected Base Type to be equal to Result Type: BitReverse"));
}
+TEST_F(ValidateBitwise, OpBitReverseNot32Vulkan) {
+ const std::string body = R"(
+%val1 = OpBitReverse %u64 %u64_1
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Base-04781"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Expected 32-bit int type for Base operand: BitReverse"));
+}
+
TEST_F(ValidateBitwise, OpBitCountSuccess) {
const std::string body = R"(
%val1 = OpBitCount %s32 %u64_1
%val2 = OpBitCount %u32vec2 %s32vec2_12
+%val3 = OpBitCount %s64 %s64_1
)";
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
+TEST_F(ValidateBitwise, OpBitCountVulkanSuccess) {
+ const std::string body = R"(
+%val1 = OpBitCount %s32 %u32_1
+%val2 = OpBitCount %u32vec2 %s32vec2_12
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
TEST_F(ValidateBitwise, OpBitCountWrongResultType) {
const std::string body = R"(
%val1 = OpBitCount %bool %u64_1
@@ -524,11 +607,14 @@ TEST_F(ValidateBitwise, OpBitCountBaseNotInt) {
%val1 = OpBitCount %u32 %f64_1
)";
- CompileSuccessfully(GenerateShaderCode(body).c_str());
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Base-04781"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("Expected Base Type to be int scalar or vector: BitCount"));
+ HasSubstr(
+ "Expected int scalar or vector type for Base operand: BitCount"));
}
TEST_F(ValidateBitwise, OpBitCountBaseWrongDimension) {
@@ -544,6 +630,19 @@ TEST_F(ValidateBitwise, OpBitCountBaseWrongDimension) {
"BitCount"));
}
+TEST_F(ValidateBitwise, OpBitCountNot32Vulkan) {
+ const std::string body = R"(
+%val1 = OpBitCount %s64 %s64_1
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Base-04781"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Expected 32-bit int type for Base operand: BitCount"));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp
index d749c5a8..241eae4a 100644
--- a/test/val/val_builtins_test.cpp
+++ b/test/val/val_builtins_test.cpp
@@ -60,8 +60,9 @@ using ValidateVulkanSubgroupBuiltIns =
using ValidateVulkanCombineBuiltInExecutionModelDataTypeResult =
spvtest::ValidateBase<std::tuple<const char*, const char*, const char*,
const char*, const char*, TestResult>>;
-using ValidateVulkanCombineBuiltInArrayedVariable = spvtest::ValidateBase<
- std::tuple<const char*, const char*, const char*, const char*, TestResult>>;
+using ValidateVulkanCombineBuiltInArrayedVariable =
+ spvtest::ValidateBase<std::tuple<const char*, const char*, const char*,
+ const char*, const char*, TestResult>>;
using ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult =
spvtest::ValidateBase<
std::tuple<const char*, const char*, const char*, const char*,
@@ -394,6 +395,11 @@ CodeGenerator GetVariableCodeGenerator(const char* const built_in,
generator.before_types_ = "OpDecorate %built_in_var BuiltIn ";
generator.before_types_ += built_in;
generator.before_types_ += "\n";
+ if ((0 == std::strcmp(storage_class, "Input")) &&
+ (0 == std::strcmp(execution_model, "Fragment"))) {
+ // ensure any needed input types that might require Flat
+ generator.before_types_ += "OpDecorate %built_in_var Flat\n";
+ }
std::ostringstream after_types;
if (InitializerRequired(storage_class)) {
@@ -779,8 +785,8 @@ INSTANTIATE_TEST_SUITE_P(
"VUID-NumWorkgroups-NumWorkgroups-04296 "
"VUID-WorkgroupId-WorkgroupId-04422"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
- "to be used only with GLCompute, MeshNV, or "
- "TaskNV execution model"))));
+ "to be used only with GLCompute, MeshNV, "
+ "TaskNV, MeshEXT or TaskEXT execution model"))));
INSTANTIATE_TEST_SUITE_P(
ComputeShaderInputInt32Vec3NotInput,
@@ -1001,7 +1007,7 @@ INSTANTIATE_TEST_SUITE_P(
Values("VUID-Layer-Layer-04274 VUID-ViewportIndex-ViewportIndex-04406"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"Input storage class if execution model is Vertex, "
- "TessellationEvaluation, Geometry, or MeshNV",
+ "TessellationEvaluation, Geometry, MeshNV or MeshEXT",
"which is called with execution model"))));
INSTANTIATE_TEST_SUITE_P(
@@ -1306,14 +1312,14 @@ INSTANTIATE_TEST_SUITE_P(
INSTANTIATE_TEST_SUITE_P(
PrimitiveIdInvalidExecutionModel,
ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
- Combine(Values("PrimitiveId"), Values("Vertex", "GLCompute"),
- Values("Input"), Values("%u32"),
- Values("VUID-PrimitiveId-PrimitiveId-04330"),
- Values(TestResult(
- SPV_ERROR_INVALID_DATA,
- "to be used only with Fragment, TessellationControl, "
- "TessellationEvaluation, Geometry, MeshNV, IntersectionKHR, "
- "AnyHitKHR, and ClosestHitKHR execution models"))));
+ Combine(
+ Values("PrimitiveId"), Values("Vertex", "GLCompute"), Values("Input"),
+ Values("%u32"), Values("VUID-PrimitiveId-PrimitiveId-04330"),
+ Values(TestResult(SPV_ERROR_INVALID_DATA,
+ "to be used only with Fragment, TessellationControl, "
+ "TessellationEvaluation, Geometry, MeshNV, MeshEXT, "
+ "IntersectionKHR, "
+ "AnyHitKHR, and ClosestHitKHR execution models"))));
INSTANTIATE_TEST_SUITE_P(
PrimitiveIdFragmentNotInput,
@@ -1862,16 +1868,18 @@ INSTANTIATE_TEST_SUITE_P(
INSTANTIATE_TEST_SUITE_P(
DrawIndexInvalidExecutionModel,
ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
- Combine(Values("DrawIndex"),
- Values("Fragment", "GLCompute", "Geometry", "TessellationControl",
- "TessellationEvaluation"),
- Values("Input"), Values("%u32"),
- Values("OpCapability DrawParameters\n"),
- Values("OpExtension \"SPV_KHR_shader_draw_parameters\"\n"),
- Values("VUID-DrawIndex-DrawIndex-04207"),
- Values(TestResult(SPV_ERROR_INVALID_DATA,
- "to be used only with Vertex, MeshNV, or TaskNV "
- "execution model"))));
+ Combine(
+ Values("DrawIndex"),
+ Values("Fragment", "GLCompute", "Geometry", "TessellationControl",
+ "TessellationEvaluation"),
+ Values("Input"), Values("%u32"),
+ Values("OpCapability DrawParameters\n"),
+ Values("OpExtension \"SPV_KHR_shader_draw_parameters\"\n"),
+ Values("VUID-DrawIndex-DrawIndex-04207"),
+ Values(TestResult(
+ SPV_ERROR_INVALID_DATA,
+ "to be used only with Vertex, MeshNV, TaskNV , MeshEXT or TaskEXT "
+ "execution model"))));
INSTANTIATE_TEST_SUITE_P(
DrawIndexNotInput,
@@ -2159,17 +2167,17 @@ INSTANTIATE_TEST_SUITE_P(
INSTANTIATE_TEST_SUITE_P(
PrimitiveIdRTNotExecutionMode,
ValidateGenericCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
- Combine(Values(SPV_ENV_VULKAN_1_2), Values("PrimitiveId"),
- Values("RayGenerationKHR", "MissKHR", "CallableKHR"),
- Values("Input"), Values("%u32"),
- Values("OpCapability RayTracingKHR\n"),
- Values("OpExtension \"SPV_KHR_ray_tracing\"\n"),
- Values("VUID-PrimitiveId-PrimitiveId-04330"),
- Values(TestResult(
- SPV_ERROR_INVALID_DATA,
- "to be used only with Fragment, TessellationControl, "
- "TessellationEvaluation, Geometry, MeshNV, IntersectionKHR, "
- "AnyHitKHR, and ClosestHitKHR execution models"))));
+ Combine(
+ Values(SPV_ENV_VULKAN_1_2), Values("PrimitiveId"),
+ Values("RayGenerationKHR", "MissKHR", "CallableKHR"), Values("Input"),
+ Values("%u32"), Values("OpCapability RayTracingKHR\n"),
+ Values("OpExtension \"SPV_KHR_ray_tracing\"\n"),
+ Values("VUID-PrimitiveId-PrimitiveId-04330"),
+ Values(TestResult(SPV_ERROR_INVALID_DATA,
+ "to be used only with Fragment, TessellationControl, "
+ "TessellationEvaluation, Geometry, MeshNV, MeshEXT, "
+ "IntersectionKHR, "
+ "AnyHitKHR, and ClosestHitKHR execution models"))));
INSTANTIATE_TEST_SUITE_P(
PrimitiveIdRTNotInput,
@@ -2372,6 +2380,67 @@ INSTANTIATE_TEST_SUITE_P(
"needs to be a 32-bit int scalar",
"is not an int scalar"))));
+// CullMaskKHR is valid
+// in IS, AH, CH, MS shaders as an input i32 scalar
+INSTANTIATE_TEST_SUITE_P(
+ CullMaskSuccess,
+ ValidateGenericCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ Combine(Values(SPV_ENV_VULKAN_1_2), Values("CullMaskKHR"),
+ Values("AnyHitKHR", "ClosestHitKHR", "IntersectionKHR", "MissKHR"),
+ Values("Input"), Values("%u32"),
+ Values("OpCapability RayTracingKHR\nOpCapability RayCullMaskKHR\n"),
+ Values("OpExtension \"SPV_KHR_ray_tracing\"\nOpExtension "
+ "\"SPV_KHR_ray_cull_mask\"\n"),
+ Values(nullptr), Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+ CullMaskNotExecutionMode,
+ ValidateGenericCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ Combine(Values(SPV_ENV_VULKAN_1_2), Values("CullMaskKHR"),
+ Values("Vertex", "Fragment", "TessellationControl",
+ "TessellationEvaluation", "Geometry", "Fragment",
+ "GLCompute", "RayGenerationKHR", "CallableKHR"),
+ Values("Input"), Values("%u32"),
+ Values("OpCapability RayTracingKHR\nOpCapability RayCullMaskKHR\n"),
+ Values("OpExtension \"SPV_KHR_ray_tracing\"\nOpExtension "
+ "\"SPV_KHR_ray_cull_mask\"\n"),
+ Values("VUID-CullMaskKHR-CullMaskKHR-06735 "
+ "VUID-RayTmaxKHR-RayTmaxKHR-04348 "
+ "VUID-RayTminKHR-RayTminKHR-04351 "),
+ Values(TestResult(SPV_ERROR_INVALID_DATA,
+ "Vulkan spec does not allow BuiltIn",
+ "to be used with the execution model"))));
+
+INSTANTIATE_TEST_SUITE_P(
+ ICullMaskNotInput,
+ ValidateGenericCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ Combine(Values(SPV_ENV_VULKAN_1_2), Values("CullMaskKHR"),
+ Values("AnyHitKHR", "ClosestHitKHR", "IntersectionKHR", "MissKHR"),
+ Values("Output"), Values("%u32"),
+ Values("OpCapability RayTracingKHR\nOpCapability RayCullMaskKHR\n"),
+ Values("OpExtension \"SPV_KHR_ray_tracing\"\nOpExtension "
+ "\"SPV_KHR_ray_cull_mask\"\n"),
+ Values("VUID-CullMaskKHR-CullMaskKHR-06736 "
+ "VUID-RayTmaxKHR-RayTmaxKHR-04349 "
+ "VUID-RayTminKHR-RayTminKHR-04352 "),
+ Values(TestResult(SPV_ERROR_INVALID_DATA, "Vulkan spec allows",
+ "used for variables with Input storage class"))));
+INSTANTIATE_TEST_SUITE_P(
+ CullMaskNotIntScalar,
+ ValidateGenericCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ Combine(Values(SPV_ENV_VULKAN_1_2), Values("CullMaskKHR"),
+ Values("AnyHitKHR", "ClosestHitKHR", "IntersectionKHR", "MissKHR"),
+ Values("Input"), Values("%f32", "%u32vec3"),
+ Values("OpCapability RayTracingKHR\nOpCapability RayCullMaskKHR\n"),
+ Values("OpExtension \"SPV_KHR_ray_tracing\"\nOpExtension "
+ "\"SPV_KHR_ray_cull_mask\"\n"),
+ Values("VUID-CullMaskKHR-CullMaskKHR-06737 "
+ "VUID-RayTmaxKHR-RayTmaxKHR-04350 "
+ "VUID-RayTminKHR-RayTminKHR-04353 "),
+ Values(TestResult(SPV_ERROR_INVALID_DATA,
+ "needs to be a 32-bit int scalar",
+ "is not an int scalar"))));
+
// RayTmaxKHR, RayTminKHR are all valid
// in IS, AH, CH, MS shaders as input f32 scalars
INSTANTIATE_TEST_SUITE_P(
@@ -2610,7 +2679,8 @@ TEST_P(ValidateVulkanCombineBuiltInArrayedVariable, Variable) {
const char* const execution_model = std::get<1>(GetParam());
const char* const storage_class = std::get<2>(GetParam());
const char* const data_type = std::get<3>(GetParam());
- const TestResult& test_result = std::get<4>(GetParam());
+ const char* const vuid = std::get<4>(GetParam());
+ const TestResult& test_result = std::get<5>(GetParam());
CodeGenerator generator = GetArrayedVariableCodeGenerator(
built_in, execution_model, storage_class, data_type);
@@ -2624,18 +2694,20 @@ TEST_P(ValidateVulkanCombineBuiltInArrayedVariable, Variable) {
if (test_result.error_str2) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2));
}
+ if (vuid) {
+ EXPECT_THAT(getDiagnosticString(), AnyVUID(vuid));
+ }
}
-INSTANTIATE_TEST_SUITE_P(PointSizeArrayedF32TessControl,
- ValidateVulkanCombineBuiltInArrayedVariable,
- Combine(Values("PointSize"),
- Values("TessellationControl"), Values("Input"),
- Values("%f32"), Values(TestResult())));
+INSTANTIATE_TEST_SUITE_P(
+ PointSizeArrayedF32TessControl, ValidateVulkanCombineBuiltInArrayedVariable,
+ Combine(Values("PointSize"), Values("TessellationControl"), Values("Input"),
+ Values("%f32"), Values(nullptr), Values(TestResult())));
INSTANTIATE_TEST_SUITE_P(
PointSizeArrayedF64TessControl, ValidateVulkanCombineBuiltInArrayedVariable,
Combine(Values("PointSize"), Values("TessellationControl"), Values("Input"),
- Values("%f64"),
+ Values("%f64"), Values("VUID-PointSize-PointSize-04317"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"needs to be a 32-bit float scalar",
"has bit width 64"))));
@@ -2643,7 +2715,7 @@ INSTANTIATE_TEST_SUITE_P(
INSTANTIATE_TEST_SUITE_P(
PointSizeArrayedF32Vertex, ValidateVulkanCombineBuiltInArrayedVariable,
Combine(Values("PointSize"), Values("Vertex"), Values("Output"),
- Values("%f32"),
+ Values("%f32"), Values("VUID-PointSize-PointSize-04317"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"needs to be a 32-bit float scalar",
"is not a float scalar"))));
@@ -2652,13 +2724,14 @@ INSTANTIATE_TEST_SUITE_P(PositionArrayedF32Vec4TessControl,
ValidateVulkanCombineBuiltInArrayedVariable,
Combine(Values("Position"),
Values("TessellationControl"), Values("Input"),
- Values("%f32vec4"), Values(TestResult())));
+ Values("%f32vec4"), Values(nullptr),
+ Values(TestResult())));
INSTANTIATE_TEST_SUITE_P(
PositionArrayedF32Vec3TessControl,
ValidateVulkanCombineBuiltInArrayedVariable,
Combine(Values("Position"), Values("TessellationControl"), Values("Input"),
- Values("%f32vec3"),
+ Values("%f32vec3"), Values("VUID-Position-Position-04321"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"needs to be a 4-component 32-bit float vector",
"has 3 components"))));
@@ -2666,7 +2739,7 @@ INSTANTIATE_TEST_SUITE_P(
INSTANTIATE_TEST_SUITE_P(
PositionArrayedF32Vec4Vertex, ValidateVulkanCombineBuiltInArrayedVariable,
Combine(Values("Position"), Values("Vertex"), Values("Output"),
- Values("%f32vec4"),
+ Values("%f32vec4"), Values("VUID-Position-Position-04321"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"needs to be a 4-component 32-bit float vector",
"is not a float vector"))));
@@ -2676,13 +2749,15 @@ INSTANTIATE_TEST_SUITE_P(
ValidateVulkanCombineBuiltInArrayedVariable,
Combine(Values("ClipDistance", "CullDistance"),
Values("Geometry", "TessellationControl", "TessellationEvaluation"),
- Values("Output"), Values("%f32arr2", "%f32arr4"),
+ Values("Output"), Values("%f32arr2", "%f32arr4"), Values(nullptr),
Values(TestResult())));
INSTANTIATE_TEST_SUITE_P(
ClipAndCullDistanceVertexInput, ValidateVulkanCombineBuiltInArrayedVariable,
Combine(Values("ClipDistance", "CullDistance"), Values("Fragment"),
Values("Input"), Values("%f32arr4"),
+ Values("VUID-ClipDistance-ClipDistance-04191 "
+ "VUID-CullDistance-CullDistance-04200"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"needs to be a 32-bit float array",
"components are not float scalar"))));
@@ -2692,6 +2767,8 @@ INSTANTIATE_TEST_SUITE_P(
Combine(Values("ClipDistance", "CullDistance"),
Values("Geometry", "TessellationControl", "TessellationEvaluation"),
Values("Input"), Values("%f32vec2", "%f32vec4"),
+ Values("VUID-ClipDistance-ClipDistance-04191 "
+ "VUID-CullDistance-CullDistance-04200"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"needs to be a 32-bit float array",
"components are not float scalar"))));
@@ -2830,10 +2907,10 @@ TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeFragment) {
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Vulkan spec allows BuiltIn WorkgroupSize to be used "
- "only with GLCompute, MeshNV, or TaskNV execution model"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Vulkan spec allows BuiltIn WorkgroupSize to be used "
+ "only with GLCompute, MeshNV, TaskNV, MeshEXT or "
+ "TaskEXT execution model"));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("is referencing ID <2> (OpConstantComposite) which is "
"decorated with BuiltIn WorkgroupSize in function <1> "
@@ -3290,7 +3367,7 @@ OpReturn
OpFunctionEnd
)";
- generator.add_at_the_end_ = function_body;
+ generator.add_at_the_end_ = function_body;
return generator;
}
@@ -3353,7 +3430,7 @@ OpReturn
OpFunctionEnd
)";
- generator.add_at_the_end_ = function_body;
+ generator.add_at_the_end_ = function_body;
return generator;
}
@@ -3371,7 +3448,6 @@ TEST_F(ValidateBuiltIns,
HasSubstr("VUID-FragDepth-FragDepth-04216"));
}
-
TEST_F(ValidateBuiltIns, AllowInstanceIdWithIntersectionShader) {
CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
generator.capabilities_ += R"(
@@ -3700,8 +3776,8 @@ INSTANTIATE_TEST_SUITE_P(
Values("VUID-SubgroupId-SubgroupId-04367 "
"VUID-NumSubgroups-NumSubgroups-04293"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
- "to be used only with GLCompute, MeshNV, or "
- "TaskNV execution model"))));
+ "to be used only with GLCompute, MeshNV, "
+ "TaskNV, MeshEXT or TaskEXT execution model"))));
INSTANTIATE_TEST_SUITE_P(
SubgroupNumAndIdNotU32, ValidateVulkanSubgroupBuiltIns,
@@ -4065,6 +4141,71 @@ INSTANTIATE_TEST_SUITE_P(
"According to the Vulkan spec BuiltIn FullyCoveredEXT variable "
"needs to be a bool scalar."))));
+INSTANTIATE_TEST_SUITE_P(
+ BaryCoordNotFragment,
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ Combine(
+ Values("BaryCoordKHR", "BaryCoordNoPerspKHR"), Values("Vertex"),
+ Values("Input"), Values("%f32vec3"),
+ Values("OpCapability FragmentBarycentricKHR\n"),
+ Values("OpExtension \"SPV_KHR_fragment_shader_barycentric\"\n"),
+ Values("VUID-BaryCoordKHR-BaryCoordKHR-04154 "
+ "VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04160 "),
+ Values(TestResult(SPV_ERROR_INVALID_DATA, "Vulkan spec allows BuiltIn",
+ "to be used only with Fragment execution model"))));
+
+INSTANTIATE_TEST_SUITE_P(
+ BaryCoordNotInput,
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ Combine(Values("BaryCoordKHR", "BaryCoordNoPerspKHR"), Values("Fragment"),
+ Values("Output"), Values("%f32vec3"),
+ Values("OpCapability FragmentBarycentricKHR\n"),
+ Values("OpExtension \"SPV_KHR_fragment_shader_barycentric\"\n"),
+ Values("VUID-BaryCoordKHR-BaryCoordKHR-04155 "
+ "VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04161 "),
+ Values(TestResult(
+ SPV_ERROR_INVALID_DATA, "Vulkan spec allows BuiltIn",
+ "to be only used for variables with Input storage class"))));
+
+INSTANTIATE_TEST_SUITE_P(
+ BaryCoordNotFloatVector,
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ Combine(
+ Values("BaryCoordKHR", "BaryCoordNoPerspKHR"), Values("Fragment"),
+ Values("Output"), Values("%f32arr3", "%u32vec4"),
+ Values("OpCapability FragmentBarycentricKHR\n"),
+ Values("OpExtension \"SPV_KHR_fragment_shader_barycentric\"\n"),
+ Values("VUID-BaryCoordKHR-BaryCoordKHR-04156 "
+ "VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04162 "),
+ Values(TestResult(SPV_ERROR_INVALID_DATA,
+ "needs to be a 3-component 32-bit float vector"))));
+
+INSTANTIATE_TEST_SUITE_P(
+ BaryCoordNotFloatVec3,
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ Combine(
+ Values("BaryCoordKHR", "BaryCoordNoPerspKHR"), Values("Fragment"),
+ Values("Output"), Values("%f32vec2"),
+ Values("OpCapability FragmentBarycentricKHR\n"),
+ Values("OpExtension \"SPV_KHR_fragment_shader_barycentric\"\n"),
+ Values("VUID-BaryCoordKHR-BaryCoordKHR-04156 "
+ "VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04162 "),
+ Values(TestResult(SPV_ERROR_INVALID_DATA,
+ "needs to be a 3-component 32-bit float vector"))));
+
+INSTANTIATE_TEST_SUITE_P(
+ BaryCoordNotF32Vec3,
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ Combine(
+ Values("BaryCoordKHR", "BaryCoordNoPerspKHR"), Values("Fragment"),
+ Values("Output"), Values("%f64vec3"),
+ Values("OpCapability FragmentBarycentricKHR\n"),
+ Values("OpExtension \"SPV_KHR_fragment_shader_barycentric\"\n"),
+ Values("VUID-BaryCoordKHR-BaryCoordKHR-04156 "
+ "VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04162 "),
+ Values(TestResult(SPV_ERROR_INVALID_DATA,
+ "needs to be a 3-component 32-bit float vector"))));
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp
index c432c3cf..0d84caa1 100644
--- a/test/val/val_capability_test.cpp
+++ b/test/val/val_capability_test.cpp
@@ -2918,8 +2918,8 @@ std::vector<CapabilityExtensionVersionCase> CapVersionCases1_5() {
"StorageTexelBufferArrayNonUniformIndexing",
"SPV_EXT_descriptor_indexing"),
// SPV_EXT_physical_storage_buffer
- IN15("PhysicalStorageBufferAddressesEXT",
- "PhysicalStorageBufferAddresses", "SPV_EXT_physical_storage_buffer"),
+ IN15("PhysicalStorageBufferAddresses", "PhysicalStorageBufferAddresses",
+ "SPV_EXT_physical_storage_buffer"),
// SPV_KHR_vulkan_memory_model
IN15("VulkanMemoryModelKHR", "VulkanMemoryModel",
"SPV_KHR_vulkan_memory_model"),
diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp
index 76477468..ede51a9e 100644
--- a/test/val/val_cfg_test.cpp
+++ b/test/val/val_cfg_test.cpp
@@ -637,41 +637,6 @@ TEST_P(ValidateCFG, BranchToBlockInOtherFunctionBad) {
" %Main = OpFunction %void None %9\n"));
}
-TEST_P(ValidateCFG, HeaderDoesntDominatesMergeBad) {
- bool is_shader = GetParam() == SpvCapabilityShader;
- Block entry("entry");
- Block head("head", SpvOpBranchConditional);
- Block f("f");
- Block merge("merge", SpvOpReturn);
-
- head.SetBody("%cond = OpSLessThan %boolt %one %two\n");
-
- if (is_shader) head.AppendBody("OpSelectionMerge %merge None\n");
-
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("head", "merge", std::make_pair("func", "Main")) +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
-
- str += entry >> merge;
- str += head >> std::vector<Block>({merge, f});
- str += f >> merge;
- str += merge;
- str += "OpFunctionEnd\n";
-
- CompileSuccessfully(str);
- if (is_shader) {
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- MatchesRegex("The selection construct with the selection header "
- ".\\[%head\\] does not dominate the merge block "
- ".\\[%merge\\]\n %merge = OpLabel\n"));
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
-}
-
TEST_P(ValidateCFG, HeaderDoesntStrictlyDominateMergeBad) {
// If a merge block is reachable, then it must be strictly dominated by
// its header block.
@@ -698,7 +663,8 @@ TEST_P(ValidateCFG, HeaderDoesntStrictlyDominateMergeBad) {
EXPECT_THAT(
getDiagnosticString(),
MatchesRegex("The selection construct with the selection header "
- ".\\[%head\\] does not strictly dominate the merge block "
+ ".\\[%head\\] does not strictly structurally dominate the "
+ "merge block "
".\\[%head\\]\n %head = OpLabel\n"));
} else {
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << str;
@@ -907,16 +873,7 @@ std::string GetUnreachableContinueUnreachableLoopInst(SpvCapability cap) {
TEST_P(ValidateCFG, UnreachableContinueUnreachableLoopInst) {
CompileSuccessfully(GetUnreachableContinueUnreachableLoopInst(GetParam()));
- if (GetParam() == SpvCapabilityShader) {
- // Shader causes additional structured CFG checks that cause a failure.
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Back-edges (1[%branch] -> 3[%target]) can only be "
- "formed between a block and a loop header."));
-
- } else {
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
- }
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
std::string GetUnreachableMergeWithComplexBody(SpvCapability cap) {
@@ -1070,12 +1027,10 @@ std::string GetUnreachableContinueWithBranchUse(SpvCapability cap) {
std::string header = GetDefaultHeader(cap);
Block entry("entry");
- Block foo("foo", SpvOpBranch);
Block branch("branch", SpvOpBranch);
Block merge("merge", SpvOpReturn);
Block target("target", SpvOpBranch);
- foo >> target;
target >> branch;
entry.AppendBody("%placeholder = OpVariable %intptrt Function\n");
@@ -1092,7 +1047,6 @@ std::string GetUnreachableContinueWithBranchUse(SpvCapability cap) {
str += branch >> std::vector<Block>({merge});
str += merge;
str += target;
- str += foo;
str += "OpFunctionEnd\n";
return str;
@@ -1156,6 +1110,7 @@ std::string GetUnreachableMergeAndContinue(SpvCapability cap) {
Block body("body", SpvOpBranchConditional);
Block t("t", SpvOpReturn);
Block f("f", SpvOpReturn);
+ Block pre_target("pre_target", SpvOpBranch);
target >> branch;
body.SetBody("%cond = OpSLessThan %boolt %one %two\n");
@@ -1163,10 +1118,10 @@ std::string GetUnreachableMergeAndContinue(SpvCapability cap) {
std::string str = header;
if (cap == SpvCapabilityShader) {
branch.AppendBody("OpLoopMerge %merge %target None\n");
- body.AppendBody("OpSelectionMerge %target None\n");
+ body.AppendBody("OpSelectionMerge %pre_target None\n");
}
- str += nameOps("branch", "merge", "target", "body", "t", "f",
+ str += nameOps("branch", "merge", "pre_target", "target", "body", "t", "f",
std::make_pair("func", "Main"));
str += types_consts();
str += "%func = OpFunction %voidt None %funct\n";
@@ -1176,6 +1131,7 @@ std::string GetUnreachableMergeAndContinue(SpvCapability cap) {
str += t;
str += f;
str += merge;
+ str += pre_target >> target;
str += target;
str += "OpFunctionEnd\n";
@@ -1296,9 +1252,10 @@ TEST_P(ValidateCFG, NestedLoops) {
loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n");
}
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("loop2", "loop2_merge") + types_consts() +
- "%func = OpFunction %voidt None %funct\n";
+ std::string str =
+ GetDefaultHeader(GetParam()) +
+ nameOps("loop1", "loop1_cont_break_block", "loop2", "loop2_merge") +
+ types_consts() + "%func = OpFunction %voidt None %funct\n";
str += entry >> loop1;
str += loop1 >> loop1_cont_break_block;
@@ -1389,11 +1346,13 @@ TEST_P(ValidateCFG, BackEdgeBlockDoesntPostDominateContinueTargetBad) {
CompileSuccessfully(str);
if (GetParam() == SpvCapabilityShader) {
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- MatchesRegex("The continue construct with the continue target "
- ".\\[%loop1_cont\\] is not post dominated by the "
- "back-edge block .\\[%be_block\\]\n"
- " %be_block = OpLabel\n"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ MatchesRegex(
+ "The continue construct with the continue target "
+ ".\\[%loop1_cont\\] is not structurally post dominated by the "
+ "back-edge block .\\[%be_block\\]\n"
+ " %be_block = OpLabel\n"));
} else {
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
@@ -1529,10 +1488,11 @@ TEST_P(ValidateCFG, ContinueTargetMustBePostDominatedByBackEdge) {
if (is_shader) {
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
- MatchesRegex("The continue construct with the continue target "
- ".\\[%cheader\\] is not post dominated by the "
- "back-edge block .\\[%be_block\\]\n"
- " %be_block = OpLabel\n"));
+ MatchesRegex(
+ "The continue construct with the continue target "
+ ".\\[%cheader\\] is not structurally post dominated by the "
+ "back-edge block .\\[%be_block\\]\n"
+ " %be_block = OpLabel\n"));
} else {
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
@@ -1561,11 +1521,12 @@ TEST_P(ValidateCFG, BranchOutOfConstructToMergeBad) {
CompileSuccessfully(str);
if (is_shader) {
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- MatchesRegex("The continue construct with the continue target "
- ".\\[%loop\\] is not post dominated by the "
- "back-edge block .\\[%cont\\]\n"
- " %cont = OpLabel\n"))
+ EXPECT_THAT(
+ getDiagnosticString(),
+ MatchesRegex("The continue construct with the continue target "
+ ".\\[%loop\\] is not structurally post dominated by the "
+ "back-edge block .\\[%cont\\]\n"
+ " %cont = OpLabel\n"))
<< str;
} else {
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
@@ -1597,11 +1558,12 @@ TEST_P(ValidateCFG, BranchOutOfConstructBad) {
CompileSuccessfully(str);
if (is_shader) {
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- MatchesRegex("The continue construct with the continue target "
- ".\\[%loop\\] is not post dominated by the "
- "back-edge block .\\[%cont\\]\n"
- " %cont = OpLabel\n"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ MatchesRegex("The continue construct with the continue target "
+ ".\\[%loop\\] is not structurally post dominated by the "
+ "back-edge block .\\[%cont\\]\n"
+ " %cont = OpLabel\n"));
} else {
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
@@ -1824,40 +1786,6 @@ TEST_P(ValidateCFG, SingleLatchBlockMultipleBranchesToLoopHeader) {
<< str << getDiagnosticString();
}
-TEST_P(ValidateCFG, SingleLatchBlockHeaderContinueTargetIsItselfGood) {
- // This test case ensures we don't count a Continue Target from a loop
- // header to itself as a self-loop when computing back edges.
- // Also, it detects that there is an edge from %latch to the pseudo-exit
- // node, rather than from %loop. In particular, it detects that we
- // have used the *reverse* textual order of blocks when computing
- // predecessor traversal roots.
- bool is_shader = GetParam() == SpvCapabilityShader;
- Block entry("entry");
- Block loop("loop");
- Block latch("latch");
- Block merge("merge", SpvOpReturn);
-
- entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
- if (is_shader) {
- loop.SetBody("OpLoopMerge %merge %loop None\n");
- }
-
- std::string str = GetDefaultHeader(GetParam()) +
- nameOps("entry", "loop", "latch", "merge") +
- types_consts() +
- "%func = OpFunction %voidt None %funct\n";
-
- str += entry >> loop;
- str += loop >> latch;
- str += latch >> loop;
- str += merge;
- str += "OpFunctionEnd";
-
- CompileSuccessfully(str);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions())
- << str << getDiagnosticString();
-}
-
// Unit test to check the case where a basic block is the entry block of 2
// different constructs. In this case, the basic block is the entry block of a
// continue construct as well as a selection construct. See issue# 517 for more
@@ -2872,8 +2800,8 @@ OpFunctionEnd
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("block <ID> 9 branches to the loop construct, but not "
- "to the loop header <ID> 7"));
+ HasSubstr("Back-edges (10[%10] -> 9[%9]) can only be formed "
+ "between a block and a loop header"));
}
TEST_F(ValidateCFG, LoopMergeMergeBlockNotLabel) {
@@ -3275,9 +3203,10 @@ OpFunctionEnd
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("block <ID> 13[%13] exits the selection headed by <ID> "
- "9[%9], but not via a structured exit"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("The continue construct with the continue target 9[%9] is not "
+ "structurally post dominated by the back-edge block 13[%13]"));
}
TEST_F(ValidateCFG, BreakFromSwitch) {
@@ -4285,9 +4214,11 @@ TEST_F(ValidateCFG, StructuredSelections_RegisterBothTrueAndFalse) {
CompileSuccessfully(text);
EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("The selection construct with the selection header "
- "8[%8] does not dominate the merge block 10[%10]\n"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "The selection construct with the selection header "
+ "8[%8] does not structurally dominate the merge block 10[%10]\n"));
}
TEST_F(ValidateCFG, UnreachableIsStaticallyReachable) {
@@ -4624,6 +4555,36 @@ OpFunctionEnd
"must be different labels"));
}
+TEST_F(ValidateCFG, BadBackEdgeUnreachableContinue) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%1 = OpTypeVoid
+%2 = OpTypeFunction %1
+%3 = OpFunction %1 None %2
+%4 = OpLabel
+OpBranch %5
+%5 = OpLabel
+OpLoopMerge %6 %7 None
+OpBranch %8
+%8 = OpLabel
+OpBranch %5
+%7 = OpLabel
+OpUnreachable
+%6 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("The continue construct with the continue target 7[%7] "
+ "does not structurally dominate the back-edge block 8[%8]"));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_conversion_test.cpp b/test/val/val_conversion_test.cpp
index b9802ece..f6f37b3f 100644
--- a/test/val/val_conversion_test.cpp
+++ b/test/val/val_conversion_test.cpp
@@ -1557,7 +1557,7 @@ OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 RestrictPointerEXT
+OpDecorate %val1 RestrictPointer
%uint64 = OpTypeInt 64 0
%u64_1 = OpConstant %uint64 1
%ptr = OpTypePointer PhysicalStorageBuffer %uint64
@@ -1615,7 +1615,7 @@ OpExtension "SPV_EXT_physical_storage_buffer"
OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 RestrictPointerEXT
+OpDecorate %val1 RestrictPointer
%uint32 = OpTypeInt 32 0
%uint64 = OpTypeInt 64 0
%ptr = OpTypePointer PhysicalStorageBuffer %uint64
@@ -1641,6 +1641,131 @@ OpFunctionEnd
"integer type to have a 64-bit width for Vulkan environment."));
}
+TEST_F(ValidateConversion, ConvertUToAccelerationStructureU32Vec2) {
+ const std::string extensions = R"(
+OpCapability RayQueryKHR
+OpExtension "SPV_KHR_ray_query"
+)";
+ const std::string types = R"(
+%u32vec2ptr_func = OpTypePointer Function %u32vec2
+%typeAS = OpTypeAccelerationStructureKHR
+)";
+ const std::string body = R"(
+%asHandle = OpVariable %u32vec2ptr_func Function
+%load = OpLoad %u32vec2 %asHandle
+%val = OpConvertUToAccelerationStructureKHR %typeAS %load
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str());
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateConversion, ConvertUToAccelerationStructureSuccessU64) {
+ const std::string extensions = R"(
+OpCapability RayQueryKHR
+OpExtension "SPV_KHR_ray_query"
+)";
+ const std::string types = R"(
+%u64_func = OpTypePointer Function %u64
+%typeAS = OpTypeAccelerationStructureKHR
+)";
+ const std::string body = R"(
+%asHandle = OpVariable %u64_func Function
+%load = OpLoad %u64 %asHandle
+%val = OpConvertUToAccelerationStructureKHR %typeAS %load
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str());
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateConversion, ConvertUToAccelerationStructureResult) {
+ const std::string extensions = R"(
+OpCapability RayQueryKHR
+OpExtension "SPV_KHR_ray_query"
+)";
+ const std::string types = R"(
+%u32vec2ptr_func = OpTypePointer Function %u32vec2
+%typeRQ = OpTypeRayQueryKHR
+)";
+ const std::string body = R"(
+%asHandle = OpVariable %u32vec2ptr_func Function
+%load = OpLoad %u32vec2 %asHandle
+%val = OpConvertUToAccelerationStructureKHR %typeRQ %load
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str());
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Expected Result Type to be a Acceleration Structure"));
+}
+
+TEST_F(ValidateConversion, ConvertUToAccelerationStructureU32) {
+ const std::string extensions = R"(
+OpCapability RayQueryKHR
+OpExtension "SPV_KHR_ray_query"
+)";
+ const std::string types = R"(
+%u32ptr_func = OpTypePointer Function %u32
+%typeAS = OpTypeAccelerationStructureKHR
+)";
+ const std::string body = R"(
+%asHandle = OpVariable %u32ptr_func Function
+%load = OpLoad %u32 %asHandle
+%val = OpConvertUToAccelerationStructureKHR %typeAS %load
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str());
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Expected 64-bit uint scalar or 2-component 32-bit "
+ "uint vector as input"));
+}
+
+TEST_F(ValidateConversion, ConvertUToAccelerationStructureS64) {
+ const std::string extensions = R"(
+OpCapability RayQueryKHR
+OpExtension "SPV_KHR_ray_query"
+)";
+ const std::string types = R"(
+%s64ptr_func = OpTypePointer Function %s64
+%typeAS = OpTypeAccelerationStructureKHR
+)";
+ const std::string body = R"(
+%asHandle = OpVariable %s64ptr_func Function
+%load = OpLoad %s64 %asHandle
+%val = OpConvertUToAccelerationStructureKHR %typeAS %load
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str());
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Expected 64-bit uint scalar or 2-component 32-bit "
+ "uint vector as input"));
+}
+
+TEST_F(ValidateConversion, ConvertUToAccelerationStructureS32Vec2) {
+ const std::string extensions = R"(
+OpCapability RayQueryKHR
+OpExtension "SPV_KHR_ray_query"
+)";
+ const std::string types = R"(
+%s32vec2ptr_func = OpTypePointer Function %s32vec2
+%typeAS = OpTypeAccelerationStructureKHR
+)";
+ const std::string body = R"(
+%asHandle = OpVariable %s32vec2ptr_func Function
+%load = OpLoad %s32vec2 %asHandle
+%val = OpConvertUToAccelerationStructureKHR %typeAS %load
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str());
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Expected 64-bit uint scalar or 2-component 32-bit "
+ "uint vector as input"));
+}
+
using ValidateSmallConversions = spvtest::ValidateBase<std::string>;
CodeGenerator GetSmallConversionsCodeGenerator() {
diff --git a/test/val/val_data_test.cpp b/test/val/val_data_test.cpp
index 1d4c0e04..eef08a1e 100644
--- a/test/val/val_data_test.cpp
+++ b/test/val/val_data_test.cpp
@@ -767,6 +767,8 @@ TEST_F(ValidateData, vulkan_RTA_not_at_end_of_struct) {
CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("In Vulkan, OpTypeRuntimeArray must only be used for "
"the last member of an OpTypeStruct\n %_struct_3 = "
"OpTypeStruct %_runtimearr_uint %uint\n"));
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index 6f5a2461..77526bf9 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -42,6 +42,7 @@ struct TestResult {
};
using ValidateDecorations = spvtest::ValidateBase<bool>;
+using ValidateDecorationString = spvtest::ValidateBase<std::string>;
using ValidateVulkanCombineDecorationResult =
spvtest::ValidateBase<std::tuple<const char*, const char*, TestResult>>;
@@ -50,20 +51,20 @@ TEST_F(ValidateDecorations, ValidateOpDecorateRegistration) {
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
- OpDecorate %1 ArrayStride 4
- OpDecorate %1 RelaxedPrecision
+ OpDecorate %1 Location 4
+ OpDecorate %1 Centroid
%2 = OpTypeFloat 32
- %1 = OpTypeRuntimeArray %2
+ %3 = OpTypePointer Output %2
+ %1 = OpVariable %3 Output
; Since %1 is used first in Decoration, it gets id 1.
)";
const uint32_t id = 1;
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
// Must have 2 decorations.
- EXPECT_THAT(
- vstate_->id_decorations(id),
- Eq(std::vector<Decoration>{Decoration(SpvDecorationArrayStride, {4}),
- Decoration(SpvDecorationRelaxedPrecision)}));
+ EXPECT_THAT(vstate_->id_decorations(id),
+ Eq(std::set<Decoration>{Decoration(SpvDecorationLocation, {4}),
+ Decoration(SpvDecorationCentroid)}));
}
TEST_F(ValidateDecorations, ValidateOpMemberDecorateRegistration) {
@@ -88,15 +89,15 @@ TEST_F(ValidateDecorations, ValidateOpMemberDecorateRegistration) {
const uint32_t arr_id = 1;
EXPECT_THAT(
vstate_->id_decorations(arr_id),
- Eq(std::vector<Decoration>{Decoration(SpvDecorationArrayStride, {4})}));
+ Eq(std::set<Decoration>{Decoration(SpvDecorationArrayStride, {4})}));
// The struct must have 3 decorations.
const uint32_t struct_id = 2;
EXPECT_THAT(
vstate_->id_decorations(struct_id),
- Eq(std::vector<Decoration>{Decoration(SpvDecorationNonReadable, {}, 2),
- Decoration(SpvDecorationOffset, {2}, 2),
- Decoration(SpvDecorationBufferBlock)}));
+ Eq(std::set<Decoration>{Decoration(SpvDecorationNonReadable, {}, 2),
+ Decoration(SpvDecorationOffset, {2}, 2),
+ Decoration(SpvDecorationBufferBlock)}));
}
TEST_F(ValidateDecorations, ValidateOpMemberDecorateOutOfBound) {
@@ -151,9 +152,9 @@ TEST_F(ValidateDecorations, ValidateGroupDecorateRegistration) {
// Decoration group has 3 decorations.
auto expected_decorations =
- std::vector<Decoration>{Decoration(SpvDecorationDescriptorSet, {0}),
- Decoration(SpvDecorationRelaxedPrecision),
- Decoration(SpvDecorationRestrict)};
+ std::set<Decoration>{Decoration(SpvDecorationDescriptorSet, {0}),
+ Decoration(SpvDecorationRelaxedPrecision),
+ Decoration(SpvDecorationRestrict)};
// Decoration group is applied to id 1, 2, 3, and 4. Note that id 1 (which is
// the decoration group id) also has all the decorations.
@@ -181,7 +182,7 @@ TEST_F(ValidateDecorations, ValidateGroupMemberDecorateRegistration) {
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
// Decoration group has 1 decoration.
auto expected_decorations =
- std::vector<Decoration>{Decoration(SpvDecorationOffset, {3}, 3)};
+ std::set<Decoration>{Decoration(SpvDecorationOffset, {3}, 3)};
// Decoration group is applied to id 2, 3, and 4.
EXPECT_THAT(vstate_->id_decorations(2), Eq(expected_decorations));
@@ -2687,6 +2688,8 @@ TEST_F(ValidateDecorations, VulkanBufferBlockOnStorageBufferBad) {
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-PushConstant-06675"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("In Vulkan, BufferBlock is disallowed on variables in "
"the StorageBuffer storage class"));
}
@@ -2881,8 +2884,10 @@ TEST_F(ValidateDecorations, VulkanPushConstantMissingBlockBad) {
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-PushConstant-06675"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("PushConstant id '2' is missing Block decoration.\n"
- "From Vulkan spec, section 14.5.1:\n"
+ "From Vulkan spec:\n"
"Such variables must be identified with a Block "
"decoration"));
}
@@ -3033,11 +3038,13 @@ TEST_F(ValidateDecorations, VulkanMultiplePushConstantsSingleEntryPointBad) {
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-06674"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"Entry point id '1' uses more than one PushConstant interface.\n"
- "From Vulkan spec, section 14.5.1:\n"
+ "From Vulkan spec:\n"
"There must be no more than one push constant block "
"statically used per shader entry point."));
}
@@ -3144,11 +3151,13 @@ TEST_F(ValidateDecorations,
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-06674"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"Entry point id '1' uses more than one PushConstant interface.\n"
- "From Vulkan spec, section 14.5.1:\n"
+ "From Vulkan spec:\n"
"There must be no more than one push constant block "
"statically used per shader entry point."));
}
@@ -3186,8 +3195,10 @@ TEST_F(ValidateDecorations, VulkanUniformMissingDescriptorSetBad) {
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("Uniform id '3' is missing DescriptorSet decoration.\n"
- "From Vulkan spec, section 14.5.2:\n"
+ "From Vulkan spec:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
@@ -3225,8 +3236,10 @@ TEST_F(ValidateDecorations, VulkanUniformMissingBindingBad) {
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("Uniform id '3' is missing Binding decoration.\n"
- "From Vulkan spec, section 14.5.2:\n"
+ "From Vulkan spec:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
@@ -3256,10 +3269,12 @@ TEST_F(ValidateDecorations, VulkanUniformConstantMissingDescriptorSetBad) {
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("UniformConstant id '2' is missing DescriptorSet decoration.\n"
- "From Vulkan spec, section 14.5.2:\n"
+ "From Vulkan spec:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
@@ -3289,10 +3304,12 @@ TEST_F(ValidateDecorations, VulkanUniformConstantMissingBindingBad) {
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("UniformConstant id '2' is missing Binding decoration.\n"
- "From Vulkan spec, section 14.5.2:\n"
+ "From Vulkan spec:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
@@ -3329,10 +3346,12 @@ TEST_F(ValidateDecorations, VulkanStorageBufferMissingDescriptorSetBad) {
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("StorageBuffer id '3' is missing DescriptorSet decoration.\n"
- "From Vulkan spec, section 14.5.2:\n"
+ "From Vulkan spec:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
@@ -3370,8 +3389,10 @@ TEST_F(ValidateDecorations, VulkanStorageBufferMissingBindingBad) {
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("StorageBuffer id '3' is missing Binding decoration.\n"
- "From Vulkan spec, section 14.5.2:\n"
+ "From Vulkan spec:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
@@ -3414,10 +3435,12 @@ TEST_F(ValidateDecorations,
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("StorageBuffer id '3' is missing DescriptorSet decoration.\n"
- "From Vulkan spec, section 14.5.2:\n"
+ "From Vulkan spec:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
@@ -5943,16 +5966,16 @@ OpFunctionEnd
TEST_F(ValidateDecorations, PSBAliasedRestrictPointerSuccess) {
const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 RestrictPointerEXT
+OpDecorate %val1 RestrictPointer
%uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
@@ -5969,15 +5992,15 @@ OpFunctionEnd
TEST_F(ValidateDecorations, PSBAliasedRestrictPointerMissing) {
const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
%uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
@@ -5991,23 +6014,23 @@ OpFunctionEnd
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected AliasedPointerEXT or RestrictPointerEXT for "
- "PhysicalStorageBufferEXT pointer"));
+ HasSubstr("expected AliasedPointer or RestrictPointer for "
+ "PhysicalStorageBuffer pointer"));
}
TEST_F(ValidateDecorations, PSBAliasedRestrictPointerBoth) {
const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 RestrictPointerEXT
-OpDecorate %val1 AliasedPointerEXT
+OpDecorate %val1 RestrictPointer
+OpDecorate %val1 AliasedPointer
%uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
@@ -6020,24 +6043,23 @@ OpFunctionEnd
CompileSuccessfully(body.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("can't specify both AliasedPointerEXT and RestrictPointerEXT "
- "for PhysicalStorageBufferEXT pointer"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("can't specify both AliasedPointer and RestrictPointer "
+ "for PhysicalStorageBuffer pointer"));
}
TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamSuccess) {
const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpDecorate %fparam Restrict
%uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%fnptr = OpTypeFunction %void %ptr
@@ -6058,15 +6080,15 @@ OpFunctionEnd
TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamMissing) {
const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
%uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%fnptr = OpTypeFunction %void %ptr
@@ -6085,22 +6107,22 @@ OpFunctionEnd
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("expected Aliased or Restrict for "
- "PhysicalStorageBufferEXT pointer"));
+ "PhysicalStorageBuffer pointer"));
}
TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamBoth) {
const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpDecorate %fparam Restrict
OpDecorate %fparam Aliased
%uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%fnptr = OpTypeFunction %void %ptr
@@ -6119,12 +6141,12 @@ OpFunctionEnd
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("can't specify both Aliased and Restrict for "
- "PhysicalStorageBufferEXT pointer"));
+ "PhysicalStorageBuffer pointer"));
}
TEST_F(ValidateDecorations, PSBFPRoundingModeSuccess) {
std::string spirv = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
OpCapability Shader
OpCapability Linkage
OpCapability StorageBuffer16BitAccess
@@ -6132,14 +6154,14 @@ OpExtension "SPV_EXT_physical_storage_buffer"
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpExtension "SPV_KHR_variable_pointers"
OpExtension "SPV_KHR_16bit_storage"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint GLCompute %main "main"
OpDecorate %_ FPRoundingMode RTE
-OpDecorate %half_ptr_var AliasedPointerEXT
+OpDecorate %half_ptr_var AliasedPointer
%half = OpTypeFloat 16
%float = OpTypeFloat 32
%float_1_25 = OpConstant %float 1.25
-%half_ptr = OpTypePointer PhysicalStorageBufferEXT %half
+%half_ptr = OpTypePointer PhysicalStorageBuffer %half
%half_pptr_f = OpTypePointer Function %half_ptr
%void = OpTypeVoid
%func = OpTypeFunction %void
@@ -7159,7 +7181,9 @@ OpFunctionEnd
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables "
+ AnyVUID("VUID-StandaloneSpirv-PushConstant-06675"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("From Vulkan spec:\nSuch variables "
"must be identified with a Block decoration"));
}
@@ -7187,7 +7211,9 @@ OpFunctionEnd
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables "
+ AnyVUID("VUID-StandaloneSpirv-PushConstant-06675"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("From Vulkan spec:\nSuch variables "
"must be identified with a Block decoration"));
}
@@ -7216,7 +7242,9 @@ OpFunctionEnd
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables "
+ AnyVUID("VUID-StandaloneSpirv-PushConstant-06675"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("From Vulkan spec:\nSuch variables "
"must be identified with a Block decoration"));
}
@@ -7288,10 +7316,11 @@ OpFunctionEnd
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be "
- "identified with a Block or BufferBlock decoration"));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06676"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("From Vulkan spec:\nSuch variables must be "
+ "identified with a Block or BufferBlock decoration"));
}
TEST_F(ValidateDecorations, VulkanUniformArrayMissingBlock) {
@@ -7316,10 +7345,11 @@ OpFunctionEnd
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be "
- "identified with a Block or BufferBlock decoration"));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06676"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("From Vulkan spec:\nSuch variables must be "
+ "identified with a Block or BufferBlock decoration"));
}
TEST_F(ValidateDecorations, VulkanUniformRuntimeArrayMissingBlock) {
@@ -7345,10 +7375,11 @@ OpFunctionEnd
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be "
- "identified with a Block or BufferBlock decoration"));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06676"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("From Vulkan spec:\nSuch variables must be "
+ "identified with a Block or BufferBlock decoration"));
}
TEST_F(ValidateDecorations, VulkanArrayStrideZero) {
@@ -7992,6 +8023,7 @@ OpExecutionMode %main LocalSize 1 1 1
OpDecorate %block Block
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 0 MatrixStride 3
+OpMemberDecorate %block 0 ColMajor
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
@@ -8028,6 +8060,7 @@ OpExecutionMode %main LocalSize 1 1 1
OpDecorate %block Block
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 0 MatrixStride 3
+OpMemberDecorate %block 0 ColMajor
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
@@ -8063,6 +8096,7 @@ OpExecutionMode %main LocalSize 1 1 1
OpDecorate %block Block
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 0 MatrixStride 3
+OpMemberDecorate %block 0 ColMajor
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
@@ -8099,6 +8133,7 @@ OpExecutionMode %main LocalSize 1 1 1
OpDecorate %block Block
OpMemberDecorate %block 0 Offset 0
OpMemberDecorate %block 0 MatrixStride 3
+OpMemberDecorate %block 0 RowMajor
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
@@ -8190,6 +8225,785 @@ OpFunctionEnd
"Offset decorations"));
}
+TEST_F(ValidateDecorations, PerVertexVulkanGood) {
+ const std::string spirv = R"(
+ OpCapability Shader
+ OpCapability FragmentBarycentricKHR
+ OpExtension "SPV_KHR_fragment_shader_barycentric"
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %vertexIDs
+ OpExecutionMode %main OriginUpperLeft
+ OpDecorate %vertexIDs Location 0
+ OpDecorate %vertexIDs PerVertexKHR
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %uint = OpTypeInt 32 0
+%ptrFloat = OpTypePointer Input %float
+ %uint_3 = OpConstant %uint 3
+%floatArray = OpTypeArray %float %uint_3
+%ptrFloatArray = OpTypePointer Input %floatArray
+ %vertexIDs = OpVariable %ptrFloatArray Input
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %main = OpFunction %void None %func
+ %label = OpLabel
+ %access = OpAccessChain %ptrFloat %vertexIDs %int_0
+ %load = OpLoad %float %access
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateDecorations, PerVertexVulkanOutput) {
+ const std::string spirv = R"(
+ OpCapability Shader
+ OpCapability FragmentBarycentricKHR
+ OpExtension "SPV_KHR_fragment_shader_barycentric"
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %vertexIDs
+ OpExecutionMode %main OriginUpperLeft
+ OpDecorate %vertexIDs Location 0
+ OpDecorate %vertexIDs PerVertexKHR
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %uint = OpTypeInt 32 0
+%ptrFloat = OpTypePointer Output %float
+ %uint_3 = OpConstant %uint 3
+%floatArray = OpTypeArray %float %uint_3
+%ptrFloatArray = OpTypePointer Output %floatArray
+ %vertexIDs = OpVariable %ptrFloatArray Output
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %main = OpFunction %void None %func
+ %label = OpLabel
+ %access = OpAccessChain %ptrFloat %vertexIDs %int_0
+ %load = OpLoad %float %access
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-PerVertexKHR-06777"));
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("storage class must be Input"));
+}
+
+TEST_F(ValidateDecorations, PerVertexVulkanNonFragment) {
+ const std::string spirv = R"(
+ OpCapability Shader
+ OpCapability FragmentBarycentricKHR
+ OpExtension "SPV_KHR_fragment_shader_barycentric"
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %vertexIDs
+ OpDecorate %vertexIDs Location 0
+ OpDecorate %vertexIDs PerVertexKHR
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %uint = OpTypeInt 32 0
+%ptrFloat = OpTypePointer Input %float
+ %uint_3 = OpConstant %uint 3
+%floatArray = OpTypeArray %float %uint_3
+%ptrFloatArray = OpTypePointer Input %floatArray
+ %vertexIDs = OpVariable %ptrFloatArray Input
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %main = OpFunction %void None %func
+ %label = OpLabel
+ %access = OpAccessChain %ptrFloat %vertexIDs %int_0
+ %load = OpLoad %float %access
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-PerVertexKHR-06777"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "PerVertexKHR can only be applied to Fragment Execution Models"));
+}
+
+TEST_F(ValidateDecorations, PerVertexVulkanNonArray) {
+ const std::string spirv = R"(
+ OpCapability Shader
+ OpCapability FragmentBarycentricKHR
+ OpExtension "SPV_KHR_fragment_shader_barycentric"
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %vertexIDs
+ OpExecutionMode %main OriginUpperLeft
+ OpDecorate %vertexIDs Location 0
+ OpDecorate %vertexIDs PerVertexKHR
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %ptrFloat = OpTypePointer Input %float
+ %vertexIDs = OpVariable %ptrFloat Input
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %main = OpFunction %void None %func
+ %label = OpLabel
+ %load = OpLoad %float %vertexIDs
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Input-06778"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("PerVertexKHR must be declared as arrays"));
+}
+
+TEST_F(ValidateDecorations, RelaxedPrecisionDecorationOnNumericTypeBad) {
+ const spv_target_env env = SPV_ENV_VULKAN_1_0;
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpDecorate %float RelaxedPrecision
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %main = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, env);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("RelaxPrecision decoration cannot be applied to a type"));
+}
+
+TEST_F(ValidateDecorations, RelaxedPrecisionDecorationOnStructMember) {
+ const spv_target_env env = SPV_ENV_VULKAN_1_0;
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpMemberDecorate %struct 0 RelaxedPrecision
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %struct = OpTypeStruct %float
+ %main = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, env);
+ EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
+}
+
+TEST_F(ValidateDecorations, VulkanFlatMultipleInterfaceGood) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpCapability Geometry
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %layer %gl_Layer
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %layer Location 0
+ OpDecorate %gl_Layer Flat
+ OpDecorate %gl_Layer BuiltIn Layer
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+%_ptr_Output_int = OpTypePointer Output %int
+ %layer = OpVariable %_ptr_Output_int Output
+%_ptr_Input_int = OpTypePointer Input %int
+ %gl_Layer = OpVariable %_ptr_Input_int Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %11 = OpLoad %int %gl_Layer
+ OpStore %layer %11
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS,
+ ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateDecorations, VulkanFlatMultipleInterfaceBad) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpCapability Geometry
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %layer %gl_Layer
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %layer Location 0
+ OpDecorate %gl_Layer BuiltIn Layer
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+%_ptr_Output_int = OpTypePointer Output %int
+ %layer = OpVariable %_ptr_Output_int Output
+%_ptr_Input_int = OpTypePointer Input %int
+ %gl_Layer = OpVariable %_ptr_Input_int Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %11 = OpLoad %int %gl_Layer
+ OpStore %layer %11
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID,
+ ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Flat-04744"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Fragment OpEntryPoint operand 4 with Input interfaces with integer "
+ "or float type must have a Flat decoration for Entry Point id 2."));
+}
+
+TEST_F(ValidateDecorations, VulkanNoFlatFloat32) {
+ std::string spirv = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %in
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %in Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%_ptr_Input_float = OpTypePointer Input %float
+ %in = OpVariable %_ptr_Input_float Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %b = OpVariable %_ptr_Function_float Function
+ %11 = OpLoad %float %in
+ OpStore %b %11
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS,
+ ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateDecorations, VulkanNoFlatFloat64) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpCapability Float64
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %in
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %in Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %double = OpTypeFloat 64
+%_ptr_Function_double = OpTypePointer Function %double
+%_ptr_Input_double = OpTypePointer Input %double
+ %in = OpVariable %_ptr_Input_double Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %b = OpVariable %_ptr_Function_double Function
+ %11 = OpLoad %double %in
+ OpStore %b %11
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID,
+ ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Flat-04744"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Fragment OpEntryPoint operand 3 with Input interfaces with integer "
+ "or float type must have a Flat decoration for Entry Point id 2."));
+}
+
+TEST_F(ValidateDecorations, VulkanNoFlatVectorFloat64) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpCapability Float64
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %in
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %in Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %double = OpTypeFloat 64
+ %v2double = OpTypeVector %double 2
+%_ptr_Function_v2double = OpTypePointer Function %v2double
+%_ptr_Input_v2double = OpTypePointer Input %v2double
+ %in = OpVariable %_ptr_Input_v2double Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %b = OpVariable %_ptr_Function_v2double Function
+ %11 = OpLoad %v2double %in
+ OpStore %b %11
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS,
+ ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateDecorations, VulkanNoFlatIntVector) {
+ std::string spirv = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %in
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %in Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+ %v2int = OpTypeVector %int 2
+%_ptr_Function_v2int = OpTypePointer Function %v2int
+%_ptr_Input_v2int = OpTypePointer Input %v2int
+ %in = OpVariable %_ptr_Input_v2int Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %b = OpVariable %_ptr_Function_v2int Function
+ %12 = OpLoad %v2int %in
+ OpStore %b %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID,
+ ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Flat-04744"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Fragment OpEntryPoint operand 3 with Input interfaces with integer "
+ "or float type must have a Flat decoration for Entry Point id 2."));
+}
+
+TEST_P(ValidateDecorationString, VulkanOutputInvalidInterface) {
+ const std::string decoration = GetParam();
+ std::stringstream ss;
+ ss << R"(
+ OpCapability Shader
+ OpCapability SampleRateShading
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %out
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %out )"
+ << decoration << R"(
+ OpDecorate %out Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+%_ptr_Output_int = OpTypePointer Output %int
+ %out = OpVariable %_ptr_Output_int Output
+ %int_1 = OpConstant %int 1
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpStore %out %int_1
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(ss.str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Flat-06201"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "OpEntryPoint interfaces variable must not be fragment execution "
+ "model with an output storage class for Entry Point id 2."));
+}
+
+TEST_P(ValidateDecorationString, VulkanVertexInputInvalidInterface) {
+ const std::string decoration = GetParam();
+ std::stringstream ss;
+ ss << R"(
+ OpCapability Shader
+ OpCapability SampleRateShading
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %out %in
+ OpSource GLSL 450
+ OpDecorate %in )"
+ << decoration << R"(
+ OpDecorate %out Location 0
+ OpDecorate %in Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+%_ptr_Output_int = OpTypePointer Output %int
+ %out = OpVariable %_ptr_Output_int Output
+%_ptr_Input_int = OpTypePointer Input %int
+ %in = OpVariable %_ptr_Input_int Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %11 = OpLoad %int %in
+ OpStore %out %11
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(ss.str(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Flat-06202"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("OpEntryPoint interfaces variable must not be vertex execution "
+ "model with an input storage class for Entry Point id 2."));
+}
+
+INSTANTIATE_TEST_SUITE_P(FragmentInputInterface, ValidateDecorationString,
+ ::testing::Values("Flat", "NoPerspective", "Sample",
+ "Centroid"));
+
+TEST_F(ValidateDecorations, NVBindlessSamplerArrayInBlock) {
+ const std::string spirv = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 64
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %UBO "UBO"
+ OpMemberName %UBO 0 "uboSampler"
+ OpName %_ ""
+ OpDecorate %array ArrayStride 16
+ OpMemberDecorate %UBO 0 Offset 0
+ OpDecorate %UBO Block
+ OpDecorate %_ DescriptorSet 0
+ OpDecorate %_ Binding 2
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %7 = OpTypeImage %float 2D 0 0 0 1 Unknown
+ %8 = OpTypeSampledImage %7
+ %uint = OpTypeInt 32 0
+ %uint_3 = OpConstant %uint 3
+ %array = OpTypeArray %8 %uint_3
+ %UBO = OpTypeStruct %array
+ %pointer = OpTypePointer Uniform %UBO
+ %_ = OpVariable %pointer Uniform
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
+TEST_F(ValidateDecorations, Std140ColMajorMat2x2) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 ColMajor
+OpMemberDecorate %block 0 MatrixStride 8
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer Uniform %block
+%var = OpVariable %ptr_block Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "member 0 is a matrix with stride 8 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateDecorations, Std140RowMajorMat2x2) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 RowMajor
+OpMemberDecorate %block 0 MatrixStride 8
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer Uniform %block
+%var = OpVariable %ptr_block Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "member 0 is a matrix with stride 8 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateDecorations, Std140ColMajorMat4x2) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 ColMajor
+OpMemberDecorate %block 0 MatrixStride 8
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 4
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer Uniform %block
+%var = OpVariable %ptr_block Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "member 0 is a matrix with stride 8 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateDecorations, Std140ColMajorMat2x3) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 ColMajor
+OpMemberDecorate %block 0 MatrixStride 12
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float3 = OpTypeVector %float 3
+%matrix = OpTypeMatrix %float3 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer Uniform %block
+%var = OpVariable %ptr_block Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("member 0 is a matrix with stride 12 not satisfying "
+ "alignment to 16"));
+}
+
+TEST_F(ValidateDecorations, MatrixMissingMajornessUniform) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 16
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer Uniform %block
+%var = OpVariable %ptr_block Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "must be explicitly laid out with RowMajor or ColMajor decorations"));
+}
+
+TEST_F(ValidateDecorations, MatrixMissingMajornessStorageBuffer) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 16
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer StorageBuffer %block
+%var = OpVariable %ptr_block StorageBuffer
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "must be explicitly laid out with RowMajor or ColMajor decorations"));
+}
+
+TEST_F(ValidateDecorations, MatrixMissingMajornessPushConstant) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 16
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer PushConstant %block
+%var = OpVariable %ptr_block PushConstant
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "must be explicitly laid out with RowMajor or ColMajor decorations"));
+}
+
+TEST_F(ValidateDecorations, StructWithRowAndColMajor) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 16
+OpMemberDecorate %block 0 ColMajor
+OpMemberDecorate %block 1 Offset 32
+OpMemberDecorate %block 1 MatrixStride 16
+OpMemberDecorate %block 1 RowMajor
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix %matrix
+%ptr_block = OpTypePointer PushConstant %block
+%var = OpVariable %ptr_block PushConstant
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_entry_point.cpp b/test/val/val_entry_point_test.cpp
index f28cf5d1..f28cf5d1 100644
--- a/test/val/val_entry_point.cpp
+++ b/test/val/val_entry_point_test.cpp
diff --git a/test/val/val_ext_inst_debug_test.cpp b/test/val/val_ext_inst_debug_test.cpp
index 307a8009..554e78b0 100644
--- a/test/val/val_ext_inst_debug_test.cpp
+++ b/test/val/val_ext_inst_debug_test.cpp
@@ -1547,7 +1547,7 @@ OpExtension "SPV_KHR_non_semantic_info"
"integer scalar type"));
}
-TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCountZero) {
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayComponentCountZero) {
const std::string src = R"(
%src = OpString "simple.hlsl"
%code = OpString "main() {}"
@@ -1574,12 +1574,7 @@ OpExtension "SPV_KHR_non_semantic_info"
CompileSuccessfully(GenerateShaderCodeForDebugInfo(
src, constants, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Component Count must be OpConstant with a 32- or "
- "64-bits integer scalar type or DebugGlobalVariable or "
- "DebugLocalVariable with a 32- or 64-bits unsigned "
- "integer scalar type"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) {
@@ -1869,6 +1864,178 @@ OpExtension "SPV_KHR_non_semantic_info"
"integer less than or equal to 4"));
}
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeMatrix) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+%true = OpConstantTrue %bool
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4
+%mfloat_info = OpExtInst %void %DbgExt DebugTypeMatrix %vfloat_info %u32_4 %true
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, constants, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeMatrixFailVectorTypeType) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+%true = OpConstantTrue %bool
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4
+%mfloat_info = OpExtInst %void %DbgExt DebugTypeMatrix %dbg_src %u32_4 %true
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, constants, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Vector Type must be a result id of "
+ "DebugTypeVector"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeMatrixFailVectorCountType) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+%true = OpConstantTrue %bool
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4
+%mfloat_info = OpExtInst %void %DbgExt DebugTypeMatrix %vfloat_info %dbg_src %true
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, constants, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Vector Count must be a result id of "
+ "32-bit unsigned OpConstant"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeMatrixFailVectorCountZero) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+%true = OpConstantTrue %bool
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4
+%mfloat_info = OpExtInst %void %DbgExt DebugTypeMatrix %vfloat_info %u32_0 %true
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, constants, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Vector Count must be positive "
+ "integer less than or equal to 4"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeMatrixFailVectorCountFive) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+%true = OpConstantTrue %bool
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4
+%mfloat_info = OpExtInst %void %DbgExt DebugTypeMatrix %vfloat_info %u32_5 %true
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, constants, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Vector Count must be positive "
+ "integer less than or equal to 4"));
+}
+
TEST_F(ValidateOpenCL100DebugInfo, DebugTypedef) {
const std::string src = R"(
%src = OpString "simple.hlsl"
diff --git a/test/val/val_extension_spv_khr_bit_instructions.cpp b/test/val/val_extension_spv_khr_bit_instructions_test.cpp
index 0e926716..0e926716 100644
--- a/test/val/val_extension_spv_khr_bit_instructions.cpp
+++ b/test/val/val_extension_spv_khr_bit_instructions_test.cpp
diff --git a/test/val/val_extension_spv_khr_expect_assume.cpp b/test/val/val_extension_spv_khr_expect_assume_test.cpp
index 6ece15d1..6ece15d1 100644
--- a/test/val/val_extension_spv_khr_expect_assume.cpp
+++ b/test/val/val_extension_spv_khr_expect_assume_test.cpp
diff --git a/test/val/val_extension_spv_khr_integer_dot_product.cpp b/test/val/val_extension_spv_khr_integer_dot_product_test.cpp
index e0e6896c..e0e6896c 100644
--- a/test/val/val_extension_spv_khr_integer_dot_product.cpp
+++ b/test/val/val_extension_spv_khr_integer_dot_product_test.cpp
diff --git a/test/val/val_extension_spv_khr_linkonce_odr.cpp b/test/val/val_extension_spv_khr_linkonce_odr_test.cpp
index ac15558b..ac15558b 100644
--- a/test/val/val_extension_spv_khr_linkonce_odr.cpp
+++ b/test/val/val_extension_spv_khr_linkonce_odr_test.cpp
diff --git a/test/val/val_extension_spv_khr_subgroup_rotate_test.cpp b/test/val/val_extension_spv_khr_subgroup_rotate_test.cpp
new file mode 100644
index 00000000..4f156e8b
--- /dev/null
+++ b/test/val/val_extension_spv_khr_subgroup_rotate_test.cpp
@@ -0,0 +1,352 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+//
+// 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 <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Values;
+using ::testing::ValuesIn;
+
+struct Case {
+ std::vector<std::string> caps;
+ bool shader;
+ std::string result_type;
+ std::string scope;
+ std::string delta;
+ std::string cluster_size;
+ std::string expected_error; // empty for no error.
+};
+
+inline std::ostream& operator<<(std::ostream& out, Case c) {
+ out << "\nSPV_KHR_subgroup_rotate Case{{";
+ for (auto& cap : c.caps) {
+ out << cap;
+ }
+ out << "} ";
+ out << (c.shader ? "shader " : "kernel ");
+ out << c.result_type + " ";
+ out << c.scope + " ";
+ out << c.delta + " ";
+ out << c.cluster_size + " ";
+ out << "err'" << c.expected_error << "'";
+ out << "}";
+ return out;
+}
+
+std::string AssemblyForCase(const Case& c) {
+ std::ostringstream ss;
+
+ if (c.shader) {
+ ss << "OpCapability Shader\n";
+ } else {
+ ss << "OpCapability Kernel\n";
+ ss << "OpCapability Addresses\n";
+ }
+ for (auto& cap : c.caps) {
+ ss << "OpCapability " << cap << "\n";
+ }
+ ss << "OpExtension \"SPV_KHR_subgroup_rotate\"\n";
+
+ if (c.shader) {
+ ss << "OpMemoryModel Logical GLSL450\n";
+ ss << "OpEntryPoint GLCompute %main \"main\"\n";
+ } else {
+ ss << "OpMemoryModel Physical32 OpenCL\n";
+ ss << "OpEntryPoint Kernel %main \"main\"\n";
+ }
+
+ ss << R"(
+ %void = OpTypeVoid
+ %void_fn = OpTypeFunction %void
+ %u32 = OpTypeInt 32 0
+ %float = OpTypeFloat 32
+ %ptr = OpTypePointer Function %u32
+ )";
+
+ if (c.shader) {
+ ss << "%i32 = OpTypeInt 32 1\n";
+ }
+
+ ss << R"(
+ %u32_0 = OpConstant %u32 0
+ %u32_1 = OpConstant %u32 1
+ %u32_15 = OpConstant %u32 15
+ %u32_16 = OpConstant %u32 16
+ %u32_undef = OpUndef %u32
+ %u32_spec_1 = OpSpecConstant %u32 1
+ %u32_spec_16 = OpSpecConstant %u32 16
+ %f32_1 = OpConstant %float 1.0
+ %subgroup = OpConstant %u32 3
+ %workgroup = OpConstant %u32 2
+ %invalid_scope = OpConstant %u32 1
+ %val = OpConstant %u32 42
+ )";
+
+ if (c.shader) {
+ ss << "%i32_1 = OpConstant %i32 1\n";
+ }
+
+ ss << R"(
+ %main = OpFunction %void None %void_fn
+ %entry = OpLabel
+ )";
+
+ ss << "%unused = OpGroupNonUniformRotateKHR ";
+ ss << c.result_type + " ";
+ ss << c.scope;
+ ss << " %val ";
+ ss << c.delta;
+ ss << " " + c.cluster_size;
+ ss << "\n";
+
+ ss << R"(
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ return ss.str();
+}
+
+using ValidateSpvKHRSubgroupRotate = spvtest::ValidateBase<Case>;
+
+TEST_P(ValidateSpvKHRSubgroupRotate, Base) {
+ const auto& c = GetParam();
+ const auto& assembly = AssemblyForCase(c);
+ CompileSuccessfully(assembly);
+ if (c.expected_error.empty()) {
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
+ } else {
+ EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr(c.expected_error));
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ Valid, ValidateSpvKHRSubgroupRotate,
+ ::testing::Values(
+ Case{
+ {"GroupNonUniformRotateKHR"}, false, "%u32", "%subgroup", "%u32_1"},
+ Case{{"GroupNonUniformRotateKHR"}, true, "%u32", "%subgroup", "%u32_1"},
+ Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "%u32_16"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "%u32_16"},
+ Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%u32",
+ "%subgroup",
+ "%u32_spec_1",
+ "%u32_16"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "%u32_spec_16"},
+ Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%u32",
+ "%workgroup",
+ "%u32_1"},
+ Case{
+ {"GroupNonUniformRotateKHR"}, true, "%u32", "%workgroup", "%u32_1"},
+ Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%u32",
+ "%workgroup",
+ "%u32_spec_1"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%workgroup",
+ "%u32_spec_1"}));
+
+INSTANTIATE_TEST_SUITE_P(
+ RequiresCapability, ValidateSpvKHRSubgroupRotate,
+ ::testing::Values(Case{{},
+ false,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "",
+ "Opcode GroupNonUniformRotateKHR requires one of "
+ "these capabilities: "
+ "GroupNonUniformRotateKHR"},
+ Case{{},
+ true,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "",
+ "Opcode GroupNonUniformRotateKHR requires one of "
+ "these capabilities: "
+ "GroupNonUniformRotateKHR"}));
+
+TEST_F(ValidateSpvKHRSubgroupRotate, RequiresExtension) {
+ const std::string str = R"(
+ OpCapability GroupNonUniformRotateKHR
+)";
+ CompileSuccessfully(str.c_str());
+ EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "1st operand of Capability: operand GroupNonUniformRotateKHR(6026) "
+ "requires one of these extensions: SPV_KHR_subgroup_rotate"));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ InvalidExecutionScope, ValidateSpvKHRSubgroupRotate,
+ ::testing::Values(
+ Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%u32",
+ "%invalid_scope",
+ "%u32_1",
+ "",
+ "Execution scope is limited to Subgroup or Workgroup"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%invalid_scope",
+ "%u32_1",
+ "",
+ "Execution scope is limited to Subgroup or Workgroup"}));
+
+INSTANTIATE_TEST_SUITE_P(
+ InvalidResultType, ValidateSpvKHRSubgroupRotate,
+ ::testing::Values(Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%ptr",
+ "%subgroup",
+ "%u32_1",
+ "",
+ "Expected Result Type to be a scalar or vector of "
+ "floating-point, integer or boolean type"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%ptr",
+ "%subgroup",
+ "%u32_1",
+ "",
+ "Expected Result Type to be a scalar or vector of "
+ "floating-point, integer or boolean type"}));
+
+INSTANTIATE_TEST_SUITE_P(
+ MismatchedResultAndValueTypes, ValidateSpvKHRSubgroupRotate,
+ ::testing::Values(
+ Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%float",
+ "%subgroup",
+ "%u32_1",
+ "",
+ "Result Type must be the same as the type of Value"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%float",
+ "%subgroup",
+ "%u32_1",
+ "",
+ "Result Type must be the same as the type of Value"}));
+
+INSTANTIATE_TEST_SUITE_P(
+ InvalidDelta, ValidateSpvKHRSubgroupRotate,
+ ::testing::Values(Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%u32",
+ "%subgroup",
+ "%f32_1",
+ "",
+ "Delta must be a scalar of integer type, whose "
+ "Signedness operand is 0"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%subgroup",
+ "%f32_1",
+ "",
+ "Delta must be a scalar of integer type, whose "
+ "Signedness operand is 0"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%subgroup",
+ "%i32_1",
+ "",
+ "Delta must be a scalar of integer type, whose "
+ "Signedness operand is 0"}));
+
+INSTANTIATE_TEST_SUITE_P(
+ InvalidClusterSize, ValidateSpvKHRSubgroupRotate,
+ ::testing::Values(
+ Case{{"GroupNonUniformRotateKHR"},
+ false,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "%f32_1",
+ "ClusterSize must be a scalar of integer type, whose Signedness "
+ "operand is 0"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "%i32_1",
+ "ClusterSize must be a scalar of integer type, whose Signedness "
+ "operand is 0"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "%u32_0",
+ "Behavior is undefined unless ClusterSize is at least 1 and a "
+ "power of 2"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "%u32_15",
+ "Behavior is undefined unless ClusterSize is at least 1 and a "
+ "power of 2"},
+ Case{{"GroupNonUniformRotateKHR"},
+ true,
+ "%u32",
+ "%subgroup",
+ "%u32_1",
+ "%u32_undef",
+ "ClusterSize must come from a constant instruction"}));
+
+} // namespace
+} // namespace val
+} // namespace spvtools
diff --git a/test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp b/test/val/val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp
index f528cb9e..f528cb9e 100644
--- a/test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp
+++ b/test/val/val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp
diff --git a/test/val/val_extension_spv_khr_terminate_invocation.cpp b/test/val/val_extension_spv_khr_terminate_invocation_test.cpp
index 8d924149..8d924149 100644
--- a/test/val/val_extension_spv_khr_terminate_invocation.cpp
+++ b/test/val/val_extension_spv_khr_terminate_invocation_test.cpp
diff --git a/test/val/val_fixtures.h b/test/val/val_fixtures.h
index acbe0e57..98d8d32a 100644
--- a/test/val/val_fixtures.h
+++ b/test/val/val_fixtures.h
@@ -40,8 +40,10 @@ class ValidateBase : public ::testing::Test,
// Assembles the given SPIR-V text, checks that it fails to assemble,
// and returns resulting diagnostic. No internal state is updated.
+ // Setting the desired_result to SPV_SUCCESS is used to allow all results
std::string CompileFailure(std::string code,
- spv_target_env env = SPV_ENV_UNIVERSAL_1_0);
+ spv_target_env env = SPV_ENV_UNIVERSAL_1_0,
+ spv_result_t desired_result = SPV_SUCCESS);
// Checks that 'code' is valid SPIR-V text representation and stores the
// binary version for further method calls.
@@ -108,11 +110,17 @@ void ValidateBase<T>::TearDown() {
template <typename T>
std::string ValidateBase<T>::CompileFailure(std::string code,
- spv_target_env env) {
+ spv_target_env env,
+ spv_result_t desired_result) {
spv_diagnostic diagnostic = nullptr;
- EXPECT_NE(SPV_SUCCESS,
- spvTextToBinary(ScopedContext(env).context, code.c_str(),
- code.size(), &binary_, &diagnostic));
+ spv_result_t actual_result =
+ spvTextToBinary(ScopedContext(env).context, code.c_str(), code.size(),
+ &binary_, &diagnostic);
+ EXPECT_NE(SPV_SUCCESS, actual_result);
+ // optional check for exact result
+ if (desired_result != SPV_SUCCESS) {
+ EXPECT_EQ(actual_result, desired_result);
+ }
std::string result(diagnostic->error);
spvDiagnosticDestroy(diagnostic);
return result;
diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp
index 69257a58..3cf7575a 100644
--- a/test/val/val_id_test.cpp
+++ b/test/val/val_id_test.cpp
@@ -2187,11 +2187,33 @@ OpFunctionEnd
)";
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("If OpTypeBool is stored in conjunction with OpVariable"
- ", it can only be used with non-externally visible "
- "shader Storage Classes: Workgroup, CrossWorkgroup, "
- "Private, and Function"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "If OpTypeBool is stored in conjunction with OpVariable, it can only "
+ "be used with non-externally visible shader Storage Classes: "
+ "Workgroup, CrossWorkgroup, Private, Function, Input, Output, "
+ "RayPayloadKHR, IncomingRayPayloadKHR, HitAttributeKHR, "
+ "CallableDataKHR, or IncomingCallableDataKHR"));
+}
+
+TEST_F(ValidateIdWithMessage, OpVariableContainsBoolPrivateGood) {
+ std::string spirv = kGLSL450MemoryModel + R"(
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%block = OpTypeStruct %bool %int
+%_ptr_Private_block = OpTypePointer Private %block
+%var = OpVariable %_ptr_Private_block Private
+%void = OpTypeVoid
+%fnty = OpTypeFunction %void
+%main = OpFunction %void None %fnty
+%entry = OpLabel
+%load = OpLoad %block %var
+OpReturn
+OpFunctionEnd
+)";
+ CompileSuccessfully(spirv.c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateIdWithMessage, OpVariableContainsBoolPointerGood) {
@@ -2233,6 +2255,58 @@ OpFunctionEnd
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
+TEST_F(ValidateIdWithMessage, OpVariableContainsNoBuiltinBoolBad) {
+ std::string spirv = kGLSL450MemoryModel + R"(
+%bool = OpTypeBool
+%input = OpTypeStruct %bool
+%_ptr_input = OpTypePointer Input %input
+%var = OpVariable %_ptr_input Input
+%void = OpTypeVoid
+%fnty = OpTypeFunction %void
+%main = OpFunction %void None %fnty
+%entry = OpLabel
+%load = OpLoad %input %var
+OpReturn
+OpFunctionEnd
+)";
+ CompileSuccessfully(spirv.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "If OpTypeBool is stored in conjunction with OpVariable using Input "
+ "or Output Storage Classes it requires a BuiltIn decoration"));
+}
+
+TEST_F(ValidateIdWithMessage, OpVariableContainsNoBuiltinBoolBadVulkan) {
+ std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+%bool = OpTypeBool
+%input = OpTypeStruct %bool
+%_ptr_input = OpTypePointer Input %input
+%var = OpVariable %_ptr_input Input
+%void = OpTypeVoid
+%fnty = OpTypeFunction %void
+%main = OpFunction %void None %fnty
+%entry = OpLabel
+%load = OpLoad %input %var
+OpReturn
+OpFunctionEnd
+)";
+ CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Input-07290"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "If OpTypeBool is stored in conjunction with OpVariable using Input "
+ "or Output Storage Classes it requires a BuiltIn decoration"));
+}
+
TEST_F(ValidateIdWithMessage, OpVariableContainsRayPayloadBoolGood) {
std::string spirv = R"(
OpCapability RayTracingNV
@@ -6570,6 +6644,35 @@ TEST_F(ValidateIdWithMessage, MissingForwardPointer) {
"Operand 3[%_ptr_Uniform__struct_2] requires a previous definition"));
}
+TEST_F(ValidateIdWithMessage, NVBindlessSamplerInStruct) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 64
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %7 = OpTypeImage %float 2D 0 0 0 1 Unknown
+ %8 = OpTypeSampledImage %7
+ %9 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+ %10 = OpTypeSampler
+ %UBO = OpTypeStruct %8 %9 %10
+%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO
+ %_ = OpVariable %_ptr_Uniform_UBO Uniform
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp
index a11d07ce..df140246 100644
--- a/test/val/val_image_test.cpp
+++ b/test/val/val_image_test.cpp
@@ -149,6 +149,7 @@ OpDecorate %input_flat_u32 Location 0
%u32vec4 = OpTypeVector %u32 4
%s32vec4 = OpTypeVector %s32 4
%f32vec4 = OpTypeVector %f32 4
+%boolvec4 = OpTypeVector %bool 4
%f32_0 = OpConstant %f32 0
%f32_1 = OpConstant %f32 1
@@ -175,6 +176,8 @@ OpDecorate %input_flat_u32 Location 0
%u64_0 = OpConstant %u64 0
%u64_1 = OpConstant %u64 1
+%bool_t = OpConstantTrue %bool
+
%u32vec2arr4 = OpTypeArray %u32vec2 %u32_4
%u32vec2arr3 = OpTypeArray %u32vec2 %u32_3
%u32arr4 = OpTypeArray %u32 %u32_4
@@ -217,6 +220,8 @@ OpDecorate %input_flat_u32 Location 0
%f32vec4_0000 = OpConstantComposite %f32vec4 %f32_0 %f32_0 %f32_0 %f32_0
+%boolvec4_tttt = OpConstantComposite %boolvec4 %bool_t %bool_t %bool_t %bool_t
+
%const_offsets = OpConstantComposite %u32vec2arr4 %u32vec2_01 %u32vec2_12 %u32vec2_01 %u32vec2_12
%const_offsets3x2 = OpConstantComposite %u32vec2arr3 %u32vec2_01 %u32vec2_12 %u32vec2_01
%const_offsets4xu = OpConstantComposite %u32arr4 %u32_0 %u32_0 %u32_0 %u32_0
@@ -246,6 +251,11 @@ OpDecorate %input_flat_u32 Location 0
%uniform_image_u32_2d_0001 = OpVariable %ptr_image_u32_2d_0001 UniformConstant
%type_sampled_image_u32_2d_0001 = OpTypeSampledImage %type_image_u32_2d_0001
+%type_image_u32_3d_0001 = OpTypeImage %u32 3D 0 0 0 1 Unknown
+%ptr_image_u32_3d_0001 = OpTypePointer UniformConstant %type_image_u32_3d_0001
+%uniform_image_u32_3d_0001 = OpVariable %ptr_image_u32_3d_0001 UniformConstant
+%type_sampled_image_u32_3d_0001 = OpTypeSampledImage %type_image_u32_3d_0001
+
%type_image_u32_2d_0002 = OpTypeImage %u32 2D 0 0 0 2 Unknown
%ptr_image_u32_2d_0002 = OpTypePointer UniformConstant %type_image_u32_2d_0002
%uniform_image_u32_2d_0002 = OpVariable %ptr_image_u32_2d_0002 UniformConstant
@@ -272,6 +282,11 @@ OpDecorate %input_flat_u32 Location 0
%uniform_image_f32_3d_0111 = OpVariable %ptr_image_f32_3d_0111 UniformConstant
%type_sampled_image_f32_3d_0111 = OpTypeSampledImage %type_image_f32_3d_0111
+%type_image_f32_3d_0001 = OpTypeImage %f32 3D 0 0 0 1 Unknown
+%ptr_image_f32_3d_0001 = OpTypePointer UniformConstant %type_image_f32_3d_0001
+%uniform_image_f32_3d_0001 = OpVariable %ptr_image_f32_3d_0001 UniformConstant
+%type_sampled_image_f32_3d_0001 = OpTypeSampledImage %type_image_f32_3d_0001
+
%type_image_f32_cube_0101 = OpTypeImage %f32 Cube 0 1 0 1 Unknown
%ptr_image_f32_cube_0101 = OpTypePointer UniformConstant %type_image_f32_cube_0101
%uniform_image_f32_cube_0101 = OpVariable %ptr_image_f32_cube_0101 UniformConstant
@@ -738,6 +753,34 @@ TEST_F(ValidateImage, TypeImageWrongSampledForSubpassData) {
HasSubstr("Dim SubpassData requires Sampled to be 2"));
}
+TEST_F(ValidateImage, TypeImageWrongSampledForSubpassDataVulkan) {
+ const std::string code = GetShaderHeader("OpCapability InputAttachment\n") +
+ R"(
+%img_type = OpTypeImage %f32 SubpassData 0 0 0 1 Unknown
+)" + TrivialMain();
+
+ CompileSuccessfully(code.c_str());
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpTypeImage-06214"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Dim SubpassData requires Sampled to be 2"));
+}
+
+TEST_F(ValidateImage, TypeImageWrongArrayForSubpassDataVulkan) {
+ const std::string code = GetShaderHeader("OpCapability InputAttachment\n") +
+ R"(
+%img_type = OpTypeImage %f32 SubpassData 0 1 0 2 Unknown
+)" + TrivialMain();
+
+ CompileSuccessfully(code.c_str());
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpTypeImage-06214"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Dim SubpassData requires Arrayed to be 0"));
+}
+
TEST_F(ValidateImage, TypeImage_OpenCL_Sampled0_OK) {
const std::string code = GetKernelHeader() + R"(
%img_type = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly
@@ -1005,6 +1048,26 @@ TEST_F(ValidateImage, SampledImageNotSampler) {
HasSubstr("Expected Sampler to be of type OpTypeSampler"));
}
+TEST_F(ValidateImage, SampledImageIsStorage) {
+ const std::string declarations = R"(
+%type_sampled_image_f32_2d_0002 = OpTypeSampledImage %type_image_f32_2d_0002
+)";
+ const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0002 %img %sampler
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "",
+ SPV_ENV_UNIVERSAL_1_0, "GLSL450",
+ declarations)
+ .c_str());
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Sampled image type requires an image type with "
+ "\"Sampled\" operand set to 0 or 1"));
+}
+
TEST_F(ValidateImage, ImageTexelPointerSuccess) {
const std::string body = R"(
%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %private_image_u32_buffer_0002_r32ui %u32_0 %u32_0
@@ -1062,7 +1125,7 @@ TEST_F(ValidateImage, ImageTexelPointerImageNotResultTypePointer) {
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 137[%137] cannot be a "
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 148[%148] cannot be a "
"type"));
}
@@ -2293,6 +2356,24 @@ TEST_F(ValidateImage, SampleDrefImplicitLodWrongDrefType) {
HasSubstr("Expected Dref to be of 32-bit float type"));
}
+TEST_F(ValidateImage, SampleDrefImplicitLodWrongDimVulkan) {
+ const std::string body = R"(
+%img = OpLoad %type_image_u32_3d_0001 %uniform_image_u32_3d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_u32_3d_0001 %img %sampler
+%res1 = OpImageSampleDrefImplicitLod %u32 %simg %f32vec3_hhh %f32_1
+)";
+
+ CompileSuccessfully(
+ GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_VULKAN_1_0).c_str());
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpImage-04777"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("In Vulkan, OpImage*Dref* instructions must not use "
+ "images with a 3D Dim"));
+}
+
TEST_F(ValidateImage, SampleDrefExplicitLodSuccess) {
const std::string body = R"(
%img = OpLoad %type_image_s32_3d_0001 %uniform_image_s32_3d_0001
@@ -3249,6 +3330,23 @@ TEST_F(ValidateImage, DrefGatherWrongDrefType) {
HasSubstr("Expected Dref to be of 32-bit float type"));
}
+TEST_F(ValidateImage, DrefGatherWrongDimVulkan) {
+ const std::string body = R"(
+%img = OpLoad %type_image_f32_3d_0001 %uniform_image_f32_3d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_3d_0001 %img %sampler
+%res1 = OpImageDrefGather %f32vec4 %simg %f32vec4_0000 %f32_0_5
+)";
+
+ CompileSuccessfully(
+ GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_VULKAN_1_0).c_str());
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpImage-04777"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Expected Image 'Dim' to be 2D, Cube, or Rect"));
+}
+
TEST_F(ValidateImage, ReadSuccess1) {
const std::string body = R"(
%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002
@@ -3653,6 +3751,17 @@ OpImageWrite %img %u32_1 %u32vec4_0123
"but given only 1"));
}
+TEST_F(ValidateImage, WriteTexelScalarSuccess) {
+ const std::string body = R"(
+%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002
+OpImageWrite %img %u32vec2_01 %u32_2
+)";
+
+ const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n";
+ CompileSuccessfully(GenerateShaderCode(body, extra).c_str());
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
TEST_F(ValidateImage, WriteTexelWrongType) {
const std::string body = R"(
%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002
@@ -3666,17 +3775,17 @@ OpImageWrite %img %u32vec2_01 %img
HasSubstr("Expected Texel to be int or float vector or scalar"));
}
-TEST_F(ValidateImage, DISABLED_WriteTexelNotVector4) {
+TEST_F(ValidateImage, WriteTexelNonNumericalType) {
const std::string body = R"(
%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002
-OpImageWrite %img %u32vec2_01 %u32vec3_012
+OpImageWrite %img %u32vec2_01 %boolvec4_tttt
)";
const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n";
CompileSuccessfully(GenerateShaderCode(body, extra).c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Expected Texel to have 4 components"));
+ HasSubstr("Expected Texel to be int or float vector or scalar"));
}
TEST_F(ValidateImage, WriteTexelWrongComponentType) {
@@ -5467,7 +5576,8 @@ TEST_F(ValidateImage, SignExtendV13Bad) {
)";
EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment", "",
- SPV_ENV_UNIVERSAL_1_3)),
+ SPV_ENV_UNIVERSAL_1_3),
+ SPV_ENV_UNIVERSAL_1_3, SPV_ERROR_WRONG_VERSION),
HasSubstr("Invalid image operand 'SignExtend'"));
}
@@ -5478,7 +5588,8 @@ TEST_F(ValidateImage, ZeroExtendV13Bad) {
)";
EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment", "",
- SPV_ENV_UNIVERSAL_1_3)),
+ SPV_ENV_UNIVERSAL_1_3),
+ SPV_ENV_UNIVERSAL_1_3, SPV_ERROR_WRONG_VERSION),
HasSubstr("Invalid image operand 'ZeroExtend'"));
}
@@ -6153,6 +6264,138 @@ OpFunctionEnd
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
}
+TEST_F(ValidateImage, NVBindlessSamplerBuiltins) {
+ const std::string text = R"(
+ OpCapability Shader
+ OpCapability Int64
+ OpCapability Image1D
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 64
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %s2D "s2D"
+ OpName %textureHandle "textureHandle"
+ OpName %i1D "i1D"
+ OpName %s "s"
+ OpName %temp "temp"
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %7 = OpTypeImage %float 2D 0 0 0 1 Unknown
+ %8 = OpTypeSampledImage %7
+%_ptr_Function_8 = OpTypePointer Function %8
+ %ulong = OpTypeInt 64 0
+%_ptr_Private_ulong = OpTypePointer Private %ulong
+%textureHandle = OpVariable %_ptr_Private_ulong Private
+ %16 = OpTypeImage %float 1D 0 0 0 2 Rgba32f
+%_ptr_Function_16 = OpTypePointer Function %16
+ %21 = OpTypeSampler
+%_ptr_Function_21 = OpTypePointer Function %21
+%_ptr_Function_ulong = OpTypePointer Function %ulong
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %s2D = OpVariable %_ptr_Function_8 Function
+ %i1D = OpVariable %_ptr_Function_16 Function
+ %s = OpVariable %_ptr_Function_21 Function
+ %temp = OpVariable %_ptr_Function_ulong Function
+ %14 = OpLoad %ulong %textureHandle
+ %15 = OpConvertUToSampledImageNV %8 %14
+ OpStore %s2D %15
+ %19 = OpLoad %ulong %textureHandle
+ %20 = OpConvertUToImageNV %16 %19
+ OpStore %i1D %20
+ %24 = OpLoad %ulong %textureHandle
+ %25 = OpConvertUToSamplerNV %21 %24
+ OpStore %s %25
+ %28 = OpLoad %8 %s2D
+ %29 = OpConvertSampledImageToUNV %ulong %28
+ OpStore %temp %29
+ %30 = OpLoad %16 %i1D
+ %31 = OpConvertImageToUNV %ulong %30
+ OpStore %temp %31
+ %32 = OpLoad %21 %s
+ %33 = OpConvertSamplerToUNV %ulong %32
+ OpStore %temp %33
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
+TEST_F(ValidateImage, NVBindlessAddressingMode64) {
+ std::string text = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 64
+ OpEntryPoint GLCompute %func "main"
+%voidt = OpTypeVoid
+%uintt = OpTypeInt 32 0
+%funct = OpTypeFunction %voidt
+%func = OpFunction %voidt None %funct
+%entry = OpLabel
+%udef = OpUndef %uintt
+ OpReturn
+ OpFunctionEnd
+)";
+ CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
+TEST_F(ValidateImage, NVBindlessAddressingMode32) {
+ std::string text = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 32
+ OpEntryPoint GLCompute %func "main"
+%voidt = OpTypeVoid
+%uintt = OpTypeInt 32 0
+%funct = OpTypeFunction %voidt
+%func = OpFunction %voidt None %funct
+%entry = OpLabel
+%udef = OpUndef %uintt
+ OpReturn
+ OpFunctionEnd
+)";
+ CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
+TEST_F(ValidateImage, NVBindlessInvalidAddressingMode) {
+ std::string text = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 0
+ OpEntryPoint GLCompute %func "main"
+%voidt = OpTypeVoid
+%uintt = OpTypeInt 32 0
+%funct = OpTypeFunction %voidt
+%func = OpFunction %voidt None %funct
+%entry = OpLabel
+%udef = OpUndef %uintt
+ OpReturn
+ OpFunctionEnd
+)";
+ CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+ ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("OpSamplerImageAddressingModeNV bitwidth should be 64 or 32"));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_interfaces_test.cpp b/test/val/val_interfaces_test.cpp
index bec8d026..d9c3748f 100644
--- a/test/val/val_interfaces_test.cpp
+++ b/test/val/val_interfaces_test.cpp
@@ -711,7 +711,9 @@ OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %var1 %var2
OpExecutionMode %main OriginUpperLeft
OpDecorate %var1 Location 0
+OpDecorate %var1 Flat
OpDecorate %var2 Location 1
+OpDecorate %var2 Flat
%void = OpTypeVoid
%void_fn = OpTypeFunction %void
%float = OpTypeFloat 32
diff --git a/test/val/val_layout_test.cpp b/test/val/val_layout_test.cpp
index 7ebd7c00..8cca96f5 100644
--- a/test/val/val_layout_test.cpp
+++ b/test/val/val_layout_test.cpp
@@ -667,6 +667,98 @@ TEST_F(ValidateLayout, ModuleProcessedInvalidInBasicBlock) {
// TODO(umar): Test optional instructions
+TEST_F(ValidateLayout, ValidNVBindlessTexturelayout) {
+ std::string str = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 64
+ OpEntryPoint GLCompute %func "main"
+%voidt = OpTypeVoid
+%uintt = OpTypeInt 32 0
+%funct = OpTypeFunction %voidt
+%func = OpFunction %voidt None %funct
+%entry = OpLabel
+%udef = OpUndef %uintt
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(str);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateLayout, InvalidValidNVBindlessTexturelayout) {
+ std::string str = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %func "main"
+ OpSamplerImageAddressingModeNV 64
+%voidt = OpTypeVoid
+%uintt = OpTypeInt 32 0
+%funct = OpTypeFunction %voidt
+%func = OpFunction %voidt None %funct
+%entry = OpLabel
+%udef = OpUndef %uintt
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(str);
+ ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
+ ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "SamplerImageAddressingModeNV is in an invalid layout section"));
+}
+
+TEST_F(ValidateLayout, MissingNVBindlessAddressModeFromLayout) {
+ std::string str = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %func "main"
+%voidt = OpTypeVoid
+%uintt = OpTypeInt 32 0
+%funct = OpTypeFunction %voidt
+%func = OpFunction %voidt None %funct
+%entry = OpLabel
+%udef = OpUndef %uintt
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(str);
+ ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
+ ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Missing required OpSamplerImageAddressingModeNV instruction"));
+}
+
+TEST_F(ValidateLayout, NVBindlessAddressModeFromLayoutSpecifiedTwice) {
+ std::string str = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 64
+ OpSamplerImageAddressingModeNV 64
+)";
+
+ CompileSuccessfully(str);
+ ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT,
+ ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("OpSamplerImageAddressingModeNV should only be provided once"));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_logicals_test.cpp b/test/val/val_logicals_test.cpp
index b57c7433..c1406728 100644
--- a/test/val/val_logicals_test.cpp
+++ b/test/val/val_logicals_test.cpp
@@ -1053,18 +1053,18 @@ TEST_F(ValidateLogicals, OpSGreaterThanDifferentBitWidth) {
TEST_F(ValidateLogicals, PSBSelectSuccess) {
const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 AliasedPointerEXT
+OpDecorate %val1 AliasedPointer
%uint64 = OpTypeInt 64 0
%bool = OpTypeBool
%true = OpConstantTrue %bool
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
@@ -1159,6 +1159,61 @@ OpFunctionEnd
"condition to be equal: Select"));
}
+TEST_F(ValidateLogicals, SelectNVBindlessSamplers) {
+ const std::string spirv = R"(
+ OpCapability Shader
+ OpCapability BindlessTextureNV
+ OpExtension "SPV_NV_bindless_texture"
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpSamplerImageAddressingModeNV 64
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpSourceExtension "GL_NV_bindless_texture"
+ OpName %main "main"
+ OpName %s2D "s2D"
+ OpName %pickhandle "pickhandle"
+ OpName %Sampler1 "Sampler1"
+ OpName %Sampler2 "Sampler2"
+ OpDecorate %pickhandle Flat
+ OpDecorate %Sampler1 DescriptorSet 0
+ OpDecorate %Sampler1 Binding 0
+ OpDecorate %Sampler1 BindlessSamplerNV
+ OpDecorate %Sampler2 DescriptorSet 0
+ OpDecorate %Sampler2 Binding 1
+ OpDecorate %Sampler2 BindlessSamplerNV
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %7 = OpTypeImage %float 2D 0 0 0 1 Unknown
+ %8 = OpTypeSampledImage %7
+%_ptr_Function_8 = OpTypePointer Function %8
+ %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+ %int_0 = OpConstant %int 0
+ %bool = OpTypeBool
+%_ptr_UniformConstant_8 = OpTypePointer UniformConstant %8
+ %Sampler1 = OpVariable %_ptr_UniformConstant_8 UniformConstant
+ %Sampler2 = OpVariable %_ptr_UniformConstant_8 UniformConstant
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %s2D = OpVariable %_ptr_Function_8 Function
+ %pickhandle = OpVariable %_ptr_Function_int Function
+ %14 = OpLoad %int %pickhandle
+ %17 = OpIEqual %bool %14 %int_0
+ %20 = OpLoad %8 %Sampler1
+ %22 = OpLoad %8 %Sampler2
+ %23 = OpSelect %8 %17 %20 %22
+ OpStore %s2D %23
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp
index 8ff40e14..ec1a0007 100644
--- a/test/val/val_memory_test.cpp
+++ b/test/val/val_memory_test.cpp
@@ -203,9 +203,11 @@ TEST_F(ValidateMemory, VulkanUniformOnIntBad) {
)";
CompileSuccessfully(src, SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06807"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\n"
+ HasSubstr("From Vulkan spec:\n"
"Variables identified with the Uniform storage class are used "
"to access transparent buffer backed resources. Such variables "
"must be typed as OpTypeStruct, or an array of this type"));
@@ -277,9 +279,11 @@ TEST_F(ValidateMemory, VulkanUniformOnRuntimeArrayOfArrayBad) {
)";
CompileSuccessfully(src, SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06807"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\n"
+ HasSubstr("From Vulkan spec:\n"
"Variables identified with the Uniform storage class are used "
"to access transparent buffer backed resources. Such variables "
"must be typed as OpTypeStruct, or an array of this type"));
@@ -318,9 +322,11 @@ TEST_F(ValidateMemory, VulkanUniformOnArrayOfArrayBad) {
)";
CompileSuccessfully(src, SPV_ENV_VULKAN_1_1);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06807"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\n"
+ HasSubstr("From Vulkan spec:\n"
"Variables identified with the Uniform storage class are used "
"to access transparent buffer backed resources. Such variables "
"must be typed as OpTypeStruct, or an array of this type"));
@@ -834,10 +840,48 @@ TEST_F(ValidateMemory, VulkanPushConstantNotStructBad) {
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("PushConstant OpVariable <id> '6[%6]' has illegal "
- "type.\nFrom Vulkan spec, section 14.5.1:\n"
- "Such variables must be typed as OpTypeStruct, "
- "or an array of this type"));
+ AnyVUID("VUID-StandaloneSpirv-PushConstant-06808"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("PushConstant OpVariable <id> '6[%6]' has illegal "
+ "type.\nFrom Vulkan spec, Push Constant Interface section:\n"
+ "Such variables must be typed as OpTypeStruct"));
+}
+
+TEST_F(ValidateMemory, VulkanPushConstantArrayOfStructBad) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+
+ OpDecorate %struct Block
+ OpMemberDecorate %struct 0 Offset 0
+
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %int = OpTypeInt 32 0
+ %int_1 = OpConstant %int 1
+ %struct = OpTypeStruct %float
+ %array = OpTypeArray %struct %int_1
+ %ptr = OpTypePointer PushConstant %array
+ %pc = OpVariable %ptr PushConstant
+
+ %1 = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-PushConstant-06808"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("PushConstant OpVariable <id> '10[%10]' has illegal "
+ "type.\nFrom Vulkan spec, Push Constant Interface section:\n"
+ "Such variables must be typed as OpTypeStruct"));
}
TEST_F(ValidateMemory, VulkanPushConstant) {
@@ -1563,16 +1607,16 @@ OpFunctionEnd
TEST_F(ValidateMemory, PSBLoadAlignedSuccess) {
const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 AliasedPointerEXT
+OpDecorate %val1 AliasedPointer
%uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
@@ -1585,22 +1629,22 @@ OpReturn
OpFunctionEnd
)";
- CompileSuccessfully(body.c_str());
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+ CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
}
TEST_F(ValidateMemory, PSBLoadAlignedMissing) {
const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 AliasedPointerEXT
+OpDecorate %val1 AliasedPointer
%uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
@@ -1613,27 +1657,61 @@ OpReturn
OpFunctionEnd
)";
- CompileSuccessfully(body.c_str());
- ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr(
- "Memory accesses with PhysicalStorageBufferEXT must use Aligned"));
+ HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned"));
+}
+
+TEST_F(ValidateMemory, PSBLoadAlignedMissingWithOtherOperand) {
+ const std::string body = R"(
+OpCapability PhysicalStorageBufferAddresses
+OpCapability Int64
+OpCapability Shader
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %val1 AliasedPointer
+%uint64 = OpTypeInt 64 0
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
+%pptr_f = OpTypePointer Function %ptr
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%main = OpFunction %void None %voidfn
+%entry = OpLabel
+%val1 = OpVariable %pptr_f Function
+%val2 = OpLoad %ptr %val1
+%val3 = OpLoad %uint64 %val2 Volatile
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned"));
}
TEST_F(ValidateMemory, PSBStoreAlignedSuccess) {
const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 AliasedPointerEXT
+OpDecorate %val1 AliasedPointer
%uint64 = OpTypeInt 64 0
%u64_1 = OpConstant %uint64 1
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
@@ -1646,23 +1724,23 @@ OpReturn
OpFunctionEnd
)";
- CompileSuccessfully(body.c_str());
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+ CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
}
TEST_F(ValidateMemory, PSBStoreAlignedMissing) {
const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 AliasedPointerEXT
+OpDecorate %val1 AliasedPointer
%uint64 = OpTypeInt 64 0
%u64_1 = OpConstant %uint64 1
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
%pptr_f = OpTypePointer Function %ptr
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
@@ -1675,27 +1753,168 @@ OpReturn
OpFunctionEnd
)";
- CompileSuccessfully(body.c_str());
- ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr(
- "Memory accesses with PhysicalStorageBufferEXT must use Aligned"));
+ HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned"));
+}
+
+TEST_F(ValidateMemory, PSBCopyMemoryAlignedSuccess) {
+ const std::string body = R"(
+OpCapability PhysicalStorageBufferAddresses
+OpCapability Int64
+OpCapability Shader
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %val1 AliasedPointer
+%int = OpTypeInt 32 0
+%uint64 = OpTypeInt 64 0
+%u64_1 = OpConstant %uint64 1
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
+%pptr_f = OpTypePointer Function %ptr
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%main = OpFunction %void None %voidfn
+%entry = OpLabel
+%val1 = OpVariable %pptr_f Function
+%val2 = OpLoad %ptr %val1
+%val3 = OpLoad %ptr %val1
+OpCopyMemory %val2 %val3 Aligned 4
+OpCopyMemory %val3 %val2 Aligned 4 Aligned 4
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateMemory, PSBCopyMemoryAlignedMissingTarget) {
+ const std::string body = R"(
+OpCapability PhysicalStorageBufferAddresses
+OpCapability Int64
+OpCapability Shader
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %val1 AliasedPointer
+%int = OpTypeInt 32 0
+%uint64 = OpTypeInt 64 0
+%u64_1 = OpConstant %uint64 1
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
+%pptr_f = OpTypePointer Function %ptr
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%main = OpFunction %void None %voidfn
+%entry = OpLabel
+%val1 = OpVariable %pptr_f Function
+%val2 = OpLoad %ptr %val1
+%val3 = OpLoad %ptr %val1
+OpCopyMemory %val2 %val3 Volatile Aligned 4
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned"));
+}
+
+TEST_F(ValidateMemory, PSBCopyMemoryAlignedMissingSource) {
+ const std::string body = R"(
+OpCapability PhysicalStorageBufferAddresses
+OpCapability Int64
+OpCapability Shader
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %val1 AliasedPointer
+%int = OpTypeInt 32 0
+%uint64 = OpTypeInt 64 0
+%u64_1 = OpConstant %uint64 1
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
+%pptr_f = OpTypePointer Function %ptr
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%main = OpFunction %void None %voidfn
+%entry = OpLabel
+%val1 = OpVariable %pptr_f Function
+%val2 = OpLoad %ptr %val1
+%val3 = OpLoad %ptr %val1
+OpCopyMemory %val2 %val3 Aligned 4 Volatile
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned"));
+}
+
+TEST_F(ValidateMemory, PSBCopyMemoryAlignedMissingBoth) {
+ const std::string body = R"(
+OpCapability PhysicalStorageBufferAddresses
+OpCapability Int64
+OpCapability Shader
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %val1 AliasedPointer
+%int = OpTypeInt 32 0
+%uint64 = OpTypeInt 64 0
+%u64_1 = OpConstant %uint64 1
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
+%pptr_f = OpTypePointer Function %ptr
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%main = OpFunction %void None %voidfn
+%entry = OpLabel
+%val1 = OpVariable %pptr_f Function
+%val2 = OpLoad %ptr %val1
+%val3 = OpLoad %ptr %val1
+OpCopyMemory %val2 %val3 Volatile
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned"));
}
TEST_F(ValidateMemory, PSBVariable) {
const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
OpCapability Int64
OpCapability Shader
OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 AliasedPointerEXT
+OpDecorate %val1 AliasedPointer
%uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
-%val1 = OpVariable %ptr PhysicalStorageBufferEXT
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
+%val1 = OpVariable %ptr PhysicalStorageBuffer
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%main = OpFunction %void None %voidfn
@@ -1708,7 +1927,7 @@ OpFunctionEnd
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("PhysicalStorageBufferEXT must not be used with OpVariable"));
+ HasSubstr("PhysicalStorageBuffer must not be used with OpVariable"));
}
std::string GenCoopMatLoadStoreShader(const std::string& storeMemoryAccess,
@@ -2097,6 +2316,8 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
@@ -2162,6 +2383,8 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("For Vulkan with RuntimeDescriptorArrayEXT, a variable "
@@ -2217,11 +2440,14 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"For Vulkan, OpTypeStruct variables containing OpTypeRuntimeArray "
- "must have storage class of StorageBuffer or Uniform.\n %6 = "
+ "must have storage class of StorageBuffer, PhysicalStorageBuffer, or "
+ "Uniform.\n %6 = "
"OpVariable %_ptr_Workgroup__struct_4 Workgroup\n"));
}
@@ -2247,9 +2473,12 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("For Vulkan, an OpTypeStruct variable containing an "
"OpTypeRuntimeArray must be decorated with Block if it "
- "has storage class StorageBuffer.\n %6 = OpVariable "
+ "has storage class StorageBuffer or "
+ "PhysicalStorageBuffer.\n %6 = OpVariable "
"%_ptr_StorageBuffer__struct_4 StorageBuffer\n"));
}
@@ -2301,6 +2530,8 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("For Vulkan, an OpTypeStruct variable containing an "
"OpTypeRuntimeArray must be decorated with BufferBlock "
"if it has storage class Uniform.\n %6 = OpVariable "
@@ -2328,6 +2559,8 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
@@ -2361,6 +2594,8 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
@@ -2423,6 +2658,8 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
@@ -2459,6 +2696,8 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
@@ -2490,6 +2729,8 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpTypeArray Element Type <id> '5[%_runtimearr_4]' is not "
@@ -2524,6 +2765,8 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
@@ -2558,6 +2801,8 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
@@ -2595,6 +2840,8 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
@@ -3015,8 +3262,8 @@ OpFunctionEnd
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Instruction cannot be used without a variable "
- "pointers capability"));
+ HasSubstr("Instruction cannot for logical addressing model be "
+ "used without a variable pointers capability"));
}
}
@@ -3253,6 +3500,8 @@ OpFunctionEnd
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06925"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
@@ -3294,6 +3543,8 @@ OpFunctionEnd
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06925"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
@@ -3366,6 +3617,8 @@ OpFunctionEnd
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06925"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
@@ -3410,6 +3663,8 @@ OpFunctionEnd
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06925"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
@@ -3450,6 +3705,8 @@ OpFunctionEnd
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06925"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
@@ -3975,9 +4232,11 @@ OpFunctionEnd
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06807"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nVariables identified with "
+ HasSubstr("From Vulkan spec:\nVariables identified with "
"the StorageBuffer storage class are used to access "
"transparent buffer backed resources. Such variables must be "
"typed as OpTypeStruct, or an array of this type"));
@@ -4006,9 +4265,11 @@ OpFunctionEnd
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06807"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nVariables identified with "
+ HasSubstr("From Vulkan spec:\nVariables identified with "
"the StorageBuffer storage class are used to access "
"transparent buffer backed resources. Such variables must be "
"typed as OpTypeStruct, or an array of this type"));
@@ -4036,9 +4297,11 @@ OpFunctionEnd
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Uniform-06807"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("From Vulkan spec, section 14.5.2:\nVariables identified with "
+ HasSubstr("From Vulkan spec:\nVariables identified with "
"the StorageBuffer storage class are used to access "
"transparent buffer backed resources. Such variables must be "
"typed as OpTypeStruct, or an array of this type"));
@@ -4259,8 +4522,10 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Variable initializers in Workgroup storage class are "
- "limited to OpConstantNull"));
+ AnyVUID(" VUID-StandaloneSpirv-OpVariable-04734"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("OpVariable, <id> '5[%5]', initializers are limited to "
+ "OpConstantNull in Workgroup storage class"));
}
TEST_F(ValidateMemory, VulkanInitializerWithWorkgroupStorageClassGood) {
diff --git a/test/val/val_mesh_shading_test.cpp b/test/val/val_mesh_shading_test.cpp
new file mode 100644
index 00000000..d10f40d8
--- /dev/null
+++ b/test/val/val_mesh_shading_test.cpp
@@ -0,0 +1,95 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+//
+// 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.
+
+// Tests instructions from SPV_EXT_mesh_shader
+
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Values;
+
+using ValidateMeshShading = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateMeshShading, EmitMeshTasksEXTNotLastInstructionUniversal) {
+ const std::string body = R"(
+ OpCapability MeshShadingEXT
+ OpExtension "SPV_EXT_mesh_shader"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint TaskEXT %main "main" %p
+ OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %uint_1 = OpConstant %uint 1
+ %float = OpTypeFloat 32
+ %arr_float = OpTypeArray %float %uint_1
+ %Payload = OpTypeStruct %arr_float
+%ptr_Payload = OpTypePointer TaskPayloadWorkgroupEXT %Payload
+ %p = OpVariable %ptr_Payload TaskPayloadWorkgroupEXT
+ %main = OpFunction %void None %func
+ %label1 = OpLabel
+ OpEmitMeshTasksEXT %uint_1 %uint_1 %uint_1 %p
+ OpBranch %label2
+ %label2 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_4);
+ EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT,
+ ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Branch must appear in a block"));
+}
+
+TEST_F(ValidateMeshShading, EmitMeshTasksEXTNotLastInstructionVulkan) {
+ const std::string body = R"(
+ OpCapability MeshShadingEXT
+ OpExtension "SPV_EXT_mesh_shader"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint TaskEXT %main "main" %p
+ OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %uint_1 = OpConstant %uint 1
+ %float = OpTypeFloat 32
+ %arr_float = OpTypeArray %float %uint_1
+ %Payload = OpTypeStruct %arr_float
+%ptr_Payload = OpTypePointer TaskPayloadWorkgroupEXT %Payload
+ %p = OpVariable %ptr_Payload TaskPayloadWorkgroupEXT
+ %main = OpFunction %void None %func
+ %label1 = OpLabel
+ OpEmitMeshTasksEXT %uint_1 %uint_1 %uint_1 %p
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(body, SPV_ENV_VULKAN_1_2);
+ EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Return must appear in a block"));
+}
+
+} // namespace
+} // namespace val
+} // namespace spvtools
diff --git a/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp
index a37989b4..689f0baa 100644
--- a/test/val/val_modes_test.cpp
+++ b/test/val/val_modes_test.cpp
@@ -1101,6 +1101,89 @@ OpFunctionEnd
EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
}
+
+TEST_F(ValidateMode, FragmentShaderStencilRefFrontTooManyModesBad) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability StencilExportEXT
+OpExtension "SPV_AMD_shader_early_and_late_fragment_tests"
+OpExtension "SPV_EXT_shader_stencil_export"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpExecutionMode %main EarlyAndLateFragmentTestsAMD
+OpExecutionMode %main StencilRefLessFrontAMD
+OpExecutionMode %main StencilRefGreaterFrontAMD
+)" + kVoidFunction;
+
+ CompileSuccessfully(spirv);
+ EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Fragment execution model entry points can specify at most "
+ "one of StencilRefUnchangedFrontAMD, "
+ "StencilRefLessFrontAMD or StencilRefGreaterFrontAMD "
+ "execution modes."));
+}
+
+TEST_F(ValidateMode, FragmentShaderStencilRefBackTooManyModesBad) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability StencilExportEXT
+OpExtension "SPV_AMD_shader_early_and_late_fragment_tests"
+OpExtension "SPV_EXT_shader_stencil_export"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpExecutionMode %main EarlyAndLateFragmentTestsAMD
+OpExecutionMode %main StencilRefLessBackAMD
+OpExecutionMode %main StencilRefGreaterBackAMD
+)" + kVoidFunction;
+
+ CompileSuccessfully(spirv);
+ EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Fragment execution model entry points can specify at most "
+ "one of StencilRefUnchangedBackAMD, "
+ "StencilRefLessBackAMD or StencilRefGreaterBackAMD "
+ "execution modes."));
+}
+
+TEST_F(ValidateMode, FragmentShaderStencilRefFrontGood) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability StencilExportEXT
+OpExtension "SPV_AMD_shader_early_and_late_fragment_tests"
+OpExtension "SPV_EXT_shader_stencil_export"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpExecutionMode %main EarlyAndLateFragmentTestsAMD
+OpExecutionMode %main StencilRefLessFrontAMD
+)" + kVoidFunction;
+
+ CompileSuccessfully(spirv);
+ EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateMode, FragmentShaderStencilRefBackGood) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability StencilExportEXT
+OpExtension "SPV_AMD_shader_early_and_late_fragment_tests"
+OpExtension "SPV_EXT_shader_stencil_export"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpExecutionMode %main EarlyAndLateFragmentTestsAMD
+OpExecutionMode %main StencilRefLessBackAMD
+)" + kVoidFunction;
+
+ CompileSuccessfully(spirv);
+ EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
+}
+
TEST_F(ValidateMode, FragmentShaderDemoteVertexBad) {
const std::string spirv = R"(
OpCapability Shader
diff --git a/test/val/val_ray_query_test.cpp b/test/val/val_ray_query_test.cpp
new file mode 100644
index 00000000..1541c745
--- /dev/null
+++ b/test/val/val_ray_query_test.cpp
@@ -0,0 +1,631 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+//
+// 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.
+
+// Tests ray query instructions from SPV_KHR_ray_query.
+
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "spirv-tools/libspirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Values;
+
+using ValidateRayQuery = spvtest::ValidateBase<bool>;
+
+std::string GenerateShaderCode(
+ const std::string& body,
+ const std::string& capabilities_and_extensions = "",
+ const std::string& declarations = "") {
+ std::ostringstream ss;
+ ss << R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability Float64
+OpCapability RayQueryKHR
+OpExtension "SPV_KHR_ray_query"
+)";
+
+ ss << capabilities_and_extensions;
+
+ ss << R"(
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+
+OpDecorate %top_level_as DescriptorSet 0
+OpDecorate %top_level_as Binding 0
+
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bool = OpTypeBool
+%f32 = OpTypeFloat 32
+%f64 = OpTypeFloat 64
+%u32 = OpTypeInt 32 0
+%s32 = OpTypeInt 32 1
+%u64 = OpTypeInt 64 0
+%s64 = OpTypeInt 64 1
+%type_rq = OpTypeRayQueryKHR
+%type_as = OpTypeAccelerationStructureKHR
+
+%s32vec2 = OpTypeVector %s32 2
+%u32vec2 = OpTypeVector %u32 2
+%f32vec2 = OpTypeVector %f32 2
+%u32vec3 = OpTypeVector %u32 3
+%s32vec3 = OpTypeVector %s32 3
+%f32vec3 = OpTypeVector %f32 3
+%u32vec4 = OpTypeVector %u32 4
+%s32vec4 = OpTypeVector %s32 4
+%f32vec4 = OpTypeVector %f32 4
+
+%mat4x3 = OpTypeMatrix %f32vec3 4
+
+%f32_0 = OpConstant %f32 0
+%f64_0 = OpConstant %f64 0
+%s32_0 = OpConstant %s32 0
+%u32_0 = OpConstant %u32 0
+%u64_0 = OpConstant %u64 0
+
+%u32vec3_0 = OpConstantComposite %u32vec3 %u32_0 %u32_0 %u32_0
+%f32vec3_0 = OpConstantComposite %f32vec3 %f32_0 %f32_0 %f32_0
+%f32vec4_0 = OpConstantComposite %f32vec4 %f32_0 %f32_0 %f32_0 %f32_0
+
+%ptr_rq = OpTypePointer Private %type_rq
+%ray_query = OpVariable %ptr_rq Private
+
+%ptr_as = OpTypePointer UniformConstant %type_as
+%top_level_as = OpVariable %ptr_as UniformConstant
+
+%ptr_function_u32 = OpTypePointer Function %u32
+%ptr_function_f32 = OpTypePointer Function %f32
+%ptr_function_f32vec3 = OpTypePointer Function %f32vec3
+)";
+
+ ss << declarations;
+
+ ss << R"(
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+)";
+
+ ss << body;
+
+ ss << R"(
+OpReturn
+OpFunctionEnd)";
+ return ss.str();
+}
+
+std::string RayQueryResult(std::string opcode) {
+ if (opcode.compare("OpRayQueryProceedKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionTypeKHR") == 0 ||
+ opcode.compare("OpRayQueryGetRayTMinKHR") == 0 ||
+ opcode.compare("OpRayQueryGetRayFlagsKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionTKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceCustomIndexKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceIdKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceShaderBindingTableRecord"
+ "OffsetKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionGeometryIndexKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionPrimitiveIndexKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionBarycentricsKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionFrontFaceKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionCandidateAABBOpaqueKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionObjectRayDirectionKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionObjectRayOriginKHR") == 0 ||
+ opcode.compare("OpRayQueryGetWorldRayDirectionKHR") == 0 ||
+ opcode.compare("OpRayQueryGetWorldRayOriginKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionObjectToWorldKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionWorldToObjectKHR") == 0) {
+ return "%result =";
+ }
+ return "";
+}
+
+std::string RayQueryResultType(std::string opcode, bool valid) {
+ if (opcode.compare("OpRayQueryGetIntersectionTypeKHR") == 0 ||
+ opcode.compare("OpRayQueryGetRayFlagsKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceCustomIndexKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceIdKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceShaderBindingTableRecord"
+ "OffsetKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionGeometryIndexKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionPrimitiveIndexKHR") == 0) {
+ return valid ? "%u32" : "%f64";
+ }
+
+ if (opcode.compare("OpRayQueryGetRayTMinKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionTKHR") == 0) {
+ return valid ? "%f32" : "%f64";
+ }
+
+ if (opcode.compare("OpRayQueryGetIntersectionBarycentricsKHR") == 0) {
+ return valid ? "%f32vec2" : "%f64";
+ }
+
+ if (opcode.compare("OpRayQueryGetIntersectionObjectRayDirectionKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionObjectRayOriginKHR") == 0 ||
+ opcode.compare("OpRayQueryGetWorldRayDirectionKHR") == 0 ||
+ opcode.compare("OpRayQueryGetWorldRayOriginKHR") == 0) {
+ return valid ? "%f32vec3" : "%f64";
+ }
+
+ if (opcode.compare("OpRayQueryProceedKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionFrontFaceKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionCandidateAABBOpaqueKHR") == 0) {
+ return valid ? "%bool" : "%f64";
+ }
+
+ if (opcode.compare("OpRayQueryGetIntersectionObjectToWorldKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionWorldToObjectKHR") == 0) {
+ return valid ? "%mat4x3" : "%f64";
+ }
+ return "";
+}
+
+std::string RayQueryIntersection(std::string opcode, bool valid) {
+ if (opcode.compare("OpRayQueryGetIntersectionTypeKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionTKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceCustomIndexKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceIdKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionInstanceShaderBindingTableRecord"
+ "OffsetKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionGeometryIndexKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionPrimitiveIndexKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionBarycentricsKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionFrontFaceKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionObjectRayDirectionKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionObjectRayOriginKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionObjectToWorldKHR") == 0 ||
+ opcode.compare("OpRayQueryGetIntersectionWorldToObjectKHR") == 0) {
+ return valid ? "%s32_0" : "%f32_0";
+ }
+ return "";
+}
+
+using RayQueryCommon = spvtest::ValidateBase<std::string>;
+
+TEST_P(RayQueryCommon, Success) {
+ std::string opcode = GetParam();
+ std::ostringstream ss;
+ ss << RayQueryResult(opcode);
+ ss << " " << opcode << " ";
+ ss << RayQueryResultType(opcode, true);
+ ss << " %ray_query ";
+ ss << RayQueryIntersection(opcode, true);
+ CompileSuccessfully(GenerateShaderCode(ss.str()).c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(RayQueryCommon, BadQuery) {
+ std::string opcode = GetParam();
+ std::ostringstream ss;
+ ss << RayQueryResult(opcode);
+ ss << " " << opcode << " ";
+ ss << RayQueryResultType(opcode, true);
+ ss << " %top_level_as ";
+ ss << RayQueryIntersection(opcode, true);
+ CompileSuccessfully(GenerateShaderCode(ss.str()).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Ray Query must be a pointer to OpTypeRayQueryKHR"));
+}
+
+TEST_P(RayQueryCommon, BadResult) {
+ std::string opcode = GetParam();
+ std::string result_type = RayQueryResultType(opcode, false);
+ if (!result_type.empty()) {
+ std::ostringstream ss;
+ ss << RayQueryResult(opcode);
+ ss << " " << opcode << " ";
+ ss << result_type;
+ ss << " %ray_query ";
+ ss << RayQueryIntersection(opcode, true);
+ CompileSuccessfully(GenerateShaderCode(ss.str()).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+
+ std::string correct_result_type = RayQueryResultType(opcode, true);
+ if (correct_result_type.compare("%u32") == 0) {
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("expected Result Type to be 32-bit int scalar type"));
+ } else if (correct_result_type.compare("%f32") == 0) {
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("expected Result Type to be 32-bit float scalar type"));
+ } else if (correct_result_type.compare("%f32vec2") == 0) {
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected Result Type to be 32-bit float "
+ "2-component vector type"));
+ } else if (correct_result_type.compare("%f32vec3") == 0) {
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected Result Type to be 32-bit float "
+ "3-component vector type"));
+ } else if (correct_result_type.compare("%bool") == 0) {
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected Result Type to be bool scalar type"));
+ } else if (correct_result_type.compare("%mat4x3") == 0) {
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected matrix type as Result Type"));
+ }
+ }
+}
+
+TEST_P(RayQueryCommon, BadIntersection) {
+ std::string opcode = GetParam();
+ std::string intersection = RayQueryIntersection(opcode, false);
+ if (!intersection.empty()) {
+ std::ostringstream ss;
+ ss << RayQueryResult(opcode);
+ ss << " " << opcode << " ";
+ ss << RayQueryResultType(opcode, true);
+ ss << " %ray_query ";
+ ss << intersection;
+ CompileSuccessfully(GenerateShaderCode(ss.str()).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "expected Intersection ID to be a constant 32-bit int scalar"));
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ValidateRayQueryCommon, RayQueryCommon,
+ Values("OpRayQueryTerminateKHR", "OpRayQueryConfirmIntersectionKHR",
+ "OpRayQueryProceedKHR", "OpRayQueryGetIntersectionTypeKHR",
+ "OpRayQueryGetRayTMinKHR", "OpRayQueryGetRayFlagsKHR",
+ "OpRayQueryGetWorldRayDirectionKHR",
+ "OpRayQueryGetWorldRayOriginKHR", "OpRayQueryGetIntersectionTKHR",
+ "OpRayQueryGetIntersectionInstanceCustomIndexKHR",
+ "OpRayQueryGetIntersectionInstanceIdKHR",
+ "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR",
+ "OpRayQueryGetIntersectionGeometryIndexKHR",
+ "OpRayQueryGetIntersectionPrimitiveIndexKHR",
+ "OpRayQueryGetIntersectionBarycentricsKHR",
+ "OpRayQueryGetIntersectionFrontFaceKHR",
+ "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR",
+ "OpRayQueryGetIntersectionObjectRayDirectionKHR",
+ "OpRayQueryGetIntersectionObjectRayOriginKHR",
+ "OpRayQueryGetIntersectionObjectToWorldKHR",
+ "OpRayQueryGetIntersectionWorldToObjectKHR"));
+
+// tests various Intersection operand types
+TEST_F(ValidateRayQuery, IntersectionSuccess) {
+ const std::string body = R"(
+%result_1 = OpRayQueryGetIntersectionFrontFaceKHR %bool %ray_query %s32_0
+%result_2 = OpRayQueryGetIntersectionFrontFaceKHR %bool %ray_query %u32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateRayQuery, IntersectionVector) {
+ const std::string body = R"(
+%result = OpRayQueryGetIntersectionFrontFaceKHR %bool %ray_query %u32vec3_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("expected Intersection ID to be a constant 32-bit int scalar"));
+}
+
+TEST_F(ValidateRayQuery, IntersectionNonConstantVariable) {
+ const std::string body = R"(
+%var = OpVariable %ptr_function_u32 Function
+%result = OpRayQueryGetIntersectionFrontFaceKHR %bool %ray_query %var
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("expected Intersection ID to be a constant 32-bit int scalar"));
+}
+
+TEST_F(ValidateRayQuery, IntersectionNonConstantLoad) {
+ const std::string body = R"(
+%var = OpVariable %ptr_function_u32 Function
+%load = OpLoad %u32 %var
+%result = OpRayQueryGetIntersectionFrontFaceKHR %bool %ray_query %load
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("expected Intersection ID to be a constant 32-bit int scalar"));
+}
+
+TEST_F(ValidateRayQuery, InitializeSuccess) {
+ const std::string body = R"(
+%var_u32 = OpVariable %ptr_function_u32 Function
+%var_f32 = OpVariable %ptr_function_f32 Function
+%var_f32vec3 = OpVariable %ptr_function_f32vec3 Function
+
+%as = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %as %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
+
+%_u32 = OpLoad %u32 %var_u32
+%_f32 = OpLoad %f32 %var_f32
+%_f32vec3 = OpLoad %f32vec3 %var_f32vec3
+OpRayQueryInitializeKHR %ray_query %as %_u32 %_u32 %_f32vec3 %_f32 %_f32vec3 %_f32
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateRayQuery, InitializeFunctionSuccess) {
+ const std::string declaration = R"(
+%rq_ptr = OpTypePointer Private %type_rq
+%rq_func_type = OpTypeFunction %void %rq_ptr
+%rq_var_1 = OpVariable %rq_ptr Private
+%rq_var_2 = OpVariable %rq_ptr Private
+)";
+
+ const std::string body = R"(
+%fcall_1 = OpFunctionCall %void %rq_func %rq_var_1
+%as_1 = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %rq_var_1 %as_1 %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
+%fcall_2 = OpFunctionCall %void %rq_func %rq_var_2
+OpReturn
+OpFunctionEnd
+%rq_func = OpFunction %void None %rq_func_type
+%rq_param = OpFunctionParameter %rq_ptr
+%label = OpLabel
+%as_2 = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %rq_param %as_2 %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, "", declaration).c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayQuery) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %top_level_as %load %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Ray Query must be a pointer to OpTypeRayQueryKHR"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadAS) {
+ const std::string body = R"(
+OpRayQueryInitializeKHR %ray_query %ray_query %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Expected Acceleration Structure to be of type "
+ "OpTypeAccelerationStructureKHR"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayFlags64) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u64_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Ray Flags must be a 32-bit int scalar"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayFlagsVector) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u32vec2 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Operand 15[%v2uint] cannot be a type"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadCullMask) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u32_0 %f32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Cull Mask must be a 32-bit int scalar"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayOriginVec4) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %f32vec4_0 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Ray Origin must be a 32-bit float 3-component vector"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayOriginFloat) {
+ const std::string body = R"(
+%var_f32 = OpVariable %ptr_function_f32 Function
+%_f32 = OpLoad %f32 %var_f32
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %_f32 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Ray Origin must be a 32-bit float 3-component vector"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayOriginInt) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %u32vec3_0 %f32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Ray Origin must be a 32-bit float 3-component vector"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayTMin) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %f32vec3_0 %u32_0 %f32vec3_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Ray TMin must be a 32-bit float scalar"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayDirection) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec4_0 %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Ray Direction must be a 32-bit float 3-component vector"));
+}
+
+TEST_F(ValidateRayQuery, InitializeBadRayTMax) {
+ const std::string body = R"(
+%load = OpLoad %type_as %top_level_as
+OpRayQueryInitializeKHR %ray_query %load %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f64_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Ray TMax must be a 32-bit float scalar"));
+}
+
+TEST_F(ValidateRayQuery, GenerateIntersectionSuccess) {
+ const std::string body = R"(
+%var = OpVariable %ptr_function_f32 Function
+%load = OpLoad %f32 %var
+OpRayQueryGenerateIntersectionKHR %ray_query %f32_0
+OpRayQueryGenerateIntersectionKHR %ray_query %load
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateRayQuery, GenerateIntersectionBadRayQuery) {
+ const std::string body = R"(
+OpRayQueryGenerateIntersectionKHR %top_level_as %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Ray Query must be a pointer to OpTypeRayQueryKHR"));
+}
+
+TEST_F(ValidateRayQuery, GenerateIntersectionBadHitT) {
+ const std::string body = R"(
+OpRayQueryGenerateIntersectionKHR %ray_query %u32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Hit T must be a 32-bit float scalar"));
+}
+
+TEST_F(ValidateRayQuery, RayQueryArraySuccess) {
+ // This shader is slightly different to the ones above, so it doesn't reuse
+ // the shader code generator.
+ const std::string shader = R"(
+ OpCapability Shader
+ OpCapability RayQueryKHR
+ OpExtension "SPV_KHR_ray_query"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ OpSource GLSL 460
+ OpDecorate %topLevelAS DescriptorSet 0
+ OpDecorate %topLevelAS Binding 0
+ OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+ %ray_query = OpTypeRayQueryKHR
+ %uint = OpTypeInt 32 0
+ %uint_2 = OpConstant %uint 2
+ %ray_query_array = OpTypeArray %ray_query %uint_2
+%ptr_ray_query_array = OpTypePointer Private %ray_query_array
+ %rayQueries = OpVariable %ptr_ray_query_array Private
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %ptr_ray_query = OpTypePointer Private %ray_query
+ %accel_struct = OpTypeAccelerationStructureKHR
+ %ptr_accel_struct = OpTypePointer UniformConstant %accel_struct
+ %topLevelAS = OpVariable %ptr_accel_struct UniformConstant
+ %uint_0 = OpConstant %uint 0
+ %uint_255 = OpConstant %uint 255
+ %float = OpTypeFloat 32
+ %v3float = OpTypeVector %float 3
+ %float_0 = OpConstant %float 0
+ %vec3_zero = OpConstantComposite %v3float %float_0 %float_0 %float_0
+ %float_1 = OpConstant %float 1
+ %vec3_xy_0_z_1 = OpConstantComposite %v3float %float_0 %float_0 %float_1
+ %float_10 = OpConstant %float 10
+ %v3uint = OpTypeVector %uint 3
+ %uint_1 = OpConstant %uint 1
+ %gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
+ %main = OpFunction %void None %func
+ %main_label = OpLabel
+ %first_ray_query = OpAccessChain %ptr_ray_query %rayQueries %int_0
+ %topLevelAS_val = OpLoad %accel_struct %topLevelAS
+ OpRayQueryInitializeKHR %first_ray_query %topLevelAS_val %uint_0 %uint_255 %vec3_zero %float_0 %vec3_xy_0_z_1 %float_10
+ OpReturn
+ OpFunctionEnd
+)";
+ CompileSuccessfully(shader);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+} // namespace
+} // namespace val
+} // namespace spvtools
diff --git a/test/val/val_ray_tracing_test.cpp b/test/val/val_ray_tracing_test.cpp
new file mode 100644
index 00000000..58b9356c
--- /dev/null
+++ b/test/val/val_ray_tracing_test.cpp
@@ -0,0 +1,583 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+//
+// 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.
+
+// Tests ray tracing instructions from SPV_KHR_ray_tracing.
+
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Values;
+
+using ValidateRayTracing = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateRayTracing, IgnoreIntersectionSuccess) {
+ const std::string body = R"(
+OpCapability RayTracingKHR
+OpExtension "SPV_KHR_ray_tracing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint AnyHitKHR %main "main"
+OpName %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%main = OpFunction %void None %func
+%label = OpLabel
+OpIgnoreIntersectionKHR
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateRayTracing, IgnoreIntersectionExecutionModel) {
+ const std::string body = R"(
+OpCapability RayTracingKHR
+OpExtension "SPV_KHR_ray_tracing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint CallableKHR %main "main"
+OpName %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%main = OpFunction %void None %func
+%label = OpLabel
+OpIgnoreIntersectionKHR
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("OpIgnoreIntersectionKHR requires AnyHitKHR execution model"));
+}
+
+TEST_F(ValidateRayTracing, TerminateRaySuccess) {
+ const std::string body = R"(
+OpCapability RayTracingKHR
+OpExtension "SPV_KHR_ray_tracing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint AnyHitKHR %main "main"
+OpName %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%main = OpFunction %void None %func
+%label = OpLabel
+OpIgnoreIntersectionKHR
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateRayTracing, TerminateRayExecutionModel) {
+ const std::string body = R"(
+OpCapability RayTracingKHR
+OpExtension "SPV_KHR_ray_tracing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint MissKHR %main "main"
+OpName %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%main = OpFunction %void None %func
+%label = OpLabel
+OpTerminateRayKHR
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("OpTerminateRayKHR requires AnyHitKHR execution model"));
+}
+
+TEST_F(ValidateRayTracing, ReportIntersectionRaySuccess) {
+ const std::string body = R"(
+OpCapability RayTracingKHR
+OpExtension "SPV_KHR_ray_tracing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint IntersectionKHR %main "main"
+OpName %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float_1 = OpConstant %float 1
+%uint = OpTypeInt 32 0
+%uint_1 = OpConstant %uint 1
+%bool = OpTypeBool
+%main = OpFunction %void None %func
+%label = OpLabel
+%report = OpReportIntersectionKHR %bool %float_1 %uint_1
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateRayTracing, ReportIntersectionExecutionModel) {
+ const std::string body = R"(
+OpCapability RayTracingKHR
+OpExtension "SPV_KHR_ray_tracing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint MissKHR %main "main"
+OpName %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float_1 = OpConstant %float 1
+%uint = OpTypeInt 32 0
+%uint_1 = OpConstant %uint 1
+%bool = OpTypeBool
+%main = OpFunction %void None %func
+%label = OpLabel
+%report = OpReportIntersectionKHR %bool %float_1 %uint_1
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "OpReportIntersectionKHR requires IntersectionKHR execution model"));
+}
+
+TEST_F(ValidateRayTracing, ReportIntersectionReturnType) {
+ const std::string body = R"(
+OpCapability RayTracingKHR
+OpExtension "SPV_KHR_ray_tracing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint IntersectionKHR %main "main"
+OpName %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float_1 = OpConstant %float 1
+%uint = OpTypeInt 32 0
+%uint_1 = OpConstant %uint 1
+%main = OpFunction %void None %func
+%label = OpLabel
+%report = OpReportIntersectionKHR %uint %float_1 %uint_1
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected Result Type to be bool scalar type"));
+}
+
+TEST_F(ValidateRayTracing, ReportIntersectionHit) {
+ const std::string body = R"(
+OpCapability RayTracingKHR
+OpCapability Float64
+OpExtension "SPV_KHR_ray_tracing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint IntersectionKHR %main "main"
+OpName %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%float64 = OpTypeFloat 64
+%float64_1 = OpConstant %float64 1
+%uint = OpTypeInt 32 0
+%uint_1 = OpConstant %uint 1
+%bool = OpTypeBool
+%main = OpFunction %void None %func
+%label = OpLabel
+%report = OpReportIntersectionKHR %bool %float64_1 %uint_1
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Hit must be a 32-bit int scalar"));
+}
+
+TEST_F(ValidateRayTracing, ReportIntersectionHitKind) {
+ const std::string body = R"(
+OpCapability RayTracingKHR
+OpExtension "SPV_KHR_ray_tracing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint IntersectionKHR %main "main"
+OpName %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float_1 = OpConstant %float 1
+%sint = OpTypeInt 32 1
+%sint_1 = OpConstant %sint 1
+%bool = OpTypeBool
+%main = OpFunction %void None %func
+%label = OpLabel
+%report = OpReportIntersectionKHR %bool %float_1 %sint_1
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Hit Kind must be a 32-bit unsigned int scalar"));
+}
+
+TEST_F(ValidateRayTracing, ExecuteCallableSuccess) {
+ const std::string body = R"(
+OpCapability RayTracingKHR
+OpExtension "SPV_KHR_ray_tracing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint CallableKHR %main "main"
+OpName %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%data_ptr = OpTypePointer CallableDataKHR %int
+%data = OpVariable %data_ptr CallableDataKHR
+%inData_ptr = OpTypePointer IncomingCallableDataKHR %int
+%inData = OpVariable %inData_ptr IncomingCallableDataKHR
+%main = OpFunction %void None %func
+%label = OpLabel
+OpExecuteCallableKHR %uint_0 %data
+OpExecuteCallableKHR %uint_0 %inData
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateRayTracing, ExecuteCallableExecutionModel) {
+ const std::string body = R"(
+OpCapability RayTracingKHR
+OpExtension "SPV_KHR_ray_tracing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint AnyHitKHR %main "main"
+OpName %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%data_ptr = OpTypePointer CallableDataKHR %int
+%data = OpVariable %data_ptr CallableDataKHR
+%inData_ptr = OpTypePointer IncomingCallableDataKHR %int
+%inData = OpVariable %inData_ptr IncomingCallableDataKHR
+%main = OpFunction %void None %func
+%label = OpLabel
+OpExecuteCallableKHR %uint_0 %data
+OpExecuteCallableKHR %uint_0 %inData
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("OpExecuteCallableKHR requires RayGenerationKHR, "
+ "ClosestHitKHR, MissKHR and CallableKHR execution models"));
+}
+
+TEST_F(ValidateRayTracing, ExecuteCallableStorageClass) {
+ const std::string body = R"(
+OpCapability RayTracingKHR
+OpExtension "SPV_KHR_ray_tracing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint RayGenerationKHR %main "main"
+OpName %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%data_ptr = OpTypePointer RayPayloadKHR %int
+%data = OpVariable %data_ptr RayPayloadKHR
+%main = OpFunction %void None %func
+%label = OpLabel
+OpExecuteCallableKHR %uint_0 %data
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Callable Data must have storage class CallableDataKHR "
+ "or IncomingCallableDataKHR"));
+}
+
+TEST_F(ValidateRayTracing, ExecuteCallableSbtIndex) {
+ const std::string body = R"(
+OpCapability RayTracingKHR
+OpExtension "SPV_KHR_ray_tracing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint CallableKHR %main "main"
+OpName %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%int_1 = OpConstant %int 1
+%data_ptr = OpTypePointer CallableDataKHR %int
+%data = OpVariable %data_ptr CallableDataKHR
+%main = OpFunction %void None %func
+%label = OpLabel
+OpExecuteCallableKHR %int_1 %data
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(body.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("SBT Index must be a 32-bit unsigned int scalar"));
+}
+
+std::string GenerateRayTraceCode(
+ const std::string& body,
+ const std::string execution_model = "RayGenerationKHR") {
+ std::ostringstream ss;
+ ss << R"(
+OpCapability RayTracingKHR
+OpCapability Float64
+OpExtension "SPV_KHR_ray_tracing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint )"
+ << execution_model << R"( %main "main"
+OpDecorate %top_level_as DescriptorSet 0
+OpDecorate %top_level_as Binding 0
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%type_as = OpTypeAccelerationStructureKHR
+%as_uc_ptr = OpTypePointer UniformConstant %type_as
+%top_level_as = OpVariable %as_uc_ptr UniformConstant
+%uint = OpTypeInt 32 0
+%uint_1 = OpConstant %uint 1
+%float = OpTypeFloat 32
+%float64 = OpTypeFloat 64
+%f32vec3 = OpTypeVector %float 3
+%f32vec4 = OpTypeVector %float 4
+%float_0 = OpConstant %float 0
+%float64_0 = OpConstant %float64 0
+%v3composite = OpConstantComposite %f32vec3 %float_0 %float_0 %float_0
+%v4composite = OpConstantComposite %f32vec4 %float_0 %float_0 %float_0 %float_0
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%payload_ptr = OpTypePointer RayPayloadKHR %int
+%payload = OpVariable %payload_ptr RayPayloadKHR
+%callable_ptr = OpTypePointer CallableDataKHR %int
+%callable = OpVariable %callable_ptr CallableDataKHR
+%ptr_uint = OpTypePointer Private %uint
+%var_uint = OpVariable %ptr_uint Private
+%ptr_float = OpTypePointer Private %float
+%var_float = OpVariable %ptr_float Private
+%ptr_f32vec3 = OpTypePointer Private %f32vec3
+%var_f32vec3 = OpVariable %ptr_f32vec3 Private
+%main = OpFunction %void None %func
+%label = OpLabel
+)";
+
+ ss << body;
+
+ ss << R"(
+OpReturn
+OpFunctionEnd)";
+ return ss.str();
+}
+
+TEST_F(ValidateRayTracing, TraceRaySuccess) {
+ const std::string body = R"(
+%as = OpLoad %type_as %top_level_as
+OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload
+
+%_uint = OpLoad %uint %var_uint
+%_float = OpLoad %float %var_float
+%_f32vec3 = OpLoad %f32vec3 %var_f32vec3
+OpTraceRayKHR %as %_uint %_uint %_uint %_uint %_uint %_f32vec3 %_float %_f32vec3 %_float %payload
+)";
+
+ CompileSuccessfully(GenerateRayTraceCode(body).c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateRayTracing, TraceRayExecutionModel) {
+ const std::string body = R"(
+%as = OpLoad %type_as %top_level_as
+OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload
+)";
+
+ CompileSuccessfully(GenerateRayTraceCode(body, "CallableKHR").c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("OpTraceRayKHR requires RayGenerationKHR, "
+ "ClosestHitKHR and MissKHR execution models"));
+}
+
+TEST_F(ValidateRayTracing, TraceRayAccelerationStructure) {
+ const std::string body = R"(
+%_uint = OpLoad %uint %var_uint
+OpTraceRayKHR %_uint %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload
+)";
+
+ CompileSuccessfully(GenerateRayTraceCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Expected Acceleration Structure to be of type "
+ "OpTypeAccelerationStructureKHR"));
+}
+
+TEST_F(ValidateRayTracing, TraceRayRayFlags) {
+ const std::string body = R"(
+%as = OpLoad %type_as %top_level_as
+OpTraceRayKHR %as %float_0 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload
+)";
+
+ CompileSuccessfully(GenerateRayTraceCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Ray Flags must be a 32-bit int scalar"));
+}
+
+TEST_F(ValidateRayTracing, TraceRayCullMask) {
+ const std::string body = R"(
+%as = OpLoad %type_as %top_level_as
+OpTraceRayKHR %as %uint_1 %float_0 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload
+)";
+
+ CompileSuccessfully(GenerateRayTraceCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Cull Mask must be a 32-bit int scalar"));
+}
+
+TEST_F(ValidateRayTracing, TraceRaySbtOffest) {
+ const std::string body = R"(
+%as = OpLoad %type_as %top_level_as
+OpTraceRayKHR %as %uint_1 %uint_1 %float_0 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload
+)";
+
+ CompileSuccessfully(GenerateRayTraceCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("SBT Offset must be a 32-bit int scalar"));
+}
+
+TEST_F(ValidateRayTracing, TraceRaySbtStride) {
+ const std::string body = R"(
+%as = OpLoad %type_as %top_level_as
+OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %float_0 %uint_1 %v3composite %float_0 %v3composite %float_0 %payload
+)";
+
+ CompileSuccessfully(GenerateRayTraceCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("SBT Stride must be a 32-bit int scalar"));
+}
+
+TEST_F(ValidateRayTracing, TraceRayMissIndex) {
+ const std::string body = R"(
+%as = OpLoad %type_as %top_level_as
+OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %float_0 %v3composite %float_0 %v3composite %float_0 %payload
+)";
+
+ CompileSuccessfully(GenerateRayTraceCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Miss Index must be a 32-bit int scalar"));
+}
+
+TEST_F(ValidateRayTracing, TraceRayRayOrigin) {
+ const std::string body = R"(
+%as = OpLoad %type_as %top_level_as
+OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %float_0 %float_0 %v3composite %float_0 %payload
+)";
+
+ CompileSuccessfully(GenerateRayTraceCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Ray Origin must be a 32-bit float 3-component vector"));
+}
+
+TEST_F(ValidateRayTracing, TraceRayRayTMin) {
+ const std::string body = R"(
+%as = OpLoad %type_as %top_level_as
+OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %uint_1 %v3composite %float_0 %payload
+)";
+
+ CompileSuccessfully(GenerateRayTraceCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Ray TMin must be a 32-bit float scalar"));
+}
+
+TEST_F(ValidateRayTracing, TraceRayRayDirection) {
+ const std::string body = R"(
+%as = OpLoad %type_as %top_level_as
+OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v4composite %float_0 %payload
+)";
+
+ CompileSuccessfully(GenerateRayTraceCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Ray Direction must be a 32-bit float 3-component vector"));
+}
+
+TEST_F(ValidateRayTracing, TraceRayRayTMax) {
+ const std::string body = R"(
+%as = OpLoad %type_as %top_level_as
+OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float64_0 %payload
+)";
+
+ CompileSuccessfully(GenerateRayTraceCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Ray TMax must be a 32-bit float scalar"));
+}
+
+TEST_F(ValidateRayTracing, TraceRayPayload) {
+ const std::string body = R"(
+%as = OpLoad %type_as %top_level_as
+OpTraceRayKHR %as %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %v3composite %float_0 %v3composite %float_0 %callable
+)";
+
+ CompileSuccessfully(GenerateRayTraceCode(body).c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Payload must have storage class RayPayloadKHR or "
+ "IncomingRayPayloadKHR"));
+}
+
+} // namespace
+} // namespace val
+} // namespace spvtools
diff --git a/test/val/val_storage_test.cpp b/test/val/val_storage_test.cpp
index ae4047b9..8693e803 100644
--- a/test/val/val_storage_test.cpp
+++ b/test/val/val_storage_test.cpp
@@ -251,30 +251,46 @@ TEST_F(ValidateStorage, RelaxedLogicalPointerFunctionParamBad) {
HasSubstr("OpFunctionCall Argument <id> '"));
}
-TEST_P(ValidateStorageExecutionModel, VulkanOutsideStoreFailure) {
- std::stringstream ss;
+std::string GenerateExecutionModelCode(const std::string& execution_model,
+ const std::string& storage_class,
+ bool store) {
+ const std::string mode = (execution_model.compare("GLCompute") == 0)
+ ? "OpExecutionMode %func LocalSize 1 1 1"
+ : "";
+ const std::string operation =
+ (store) ? "OpStore %var %int0" : "%load = OpLoad %intt %var";
+ std::ostringstream ss;
ss << R"(
OpCapability Shader
OpCapability RayTracingKHR
OpExtension "SPV_KHR_ray_tracing"
OpMemoryModel Logical GLSL450
OpEntryPoint )"
- << GetParam() << R"( %func "func" %output
- OpDecorate %output Location 0
+ << execution_model << R"( %func "func" %var
+ )" << mode << R"(
+ OpDecorate %var Location 0
%intt = OpTypeInt 32 0
%int0 = OpConstant %intt 0
%voidt = OpTypeVoid
%vfunct = OpTypeFunction %voidt
-%outputptrt = OpTypePointer Output %intt
-%output = OpVariable %outputptrt Output
+%ptr = OpTypePointer )"
+ << storage_class << R"( %intt
+%var = OpVariable %ptr )" << storage_class << R"(
%func = OpFunction %voidt None %vfunct
%funcl = OpLabel
- OpStore %output %int0
+ )" << operation << R"(
OpReturn
OpFunctionEnd
)";
- CompileSuccessfully(ss.str(), SPV_ENV_VULKAN_1_0);
+ return ss.str();
+}
+
+TEST_P(ValidateStorageExecutionModel, VulkanOutsideStoreFailure) {
+ std::string execution_model = GetParam();
+ CompileSuccessfully(
+ GenerateExecutionModelCode(execution_model, "Output", true).c_str(),
+ SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
AnyVUID("VUID-StandaloneSpirv-None-04644"));
@@ -285,11 +301,264 @@ TEST_P(ValidateStorageExecutionModel, VulkanOutsideStoreFailure) {
"ClosestHitKHR, MissKHR, or CallableKHR execution models"));
}
+TEST_P(ValidateStorageExecutionModel, CallableDataStore) {
+ std::string execution_model = GetParam();
+ CompileSuccessfully(
+ GenerateExecutionModelCode(execution_model, "CallableDataKHR", true)
+ .c_str(),
+ SPV_ENV_VULKAN_1_2);
+ if (execution_model.compare("RayGenerationKHR") == 0 ||
+ execution_model.compare("ClosestHitKHR") == 0 ||
+ execution_model.compare("CallableKHR") == 0 ||
+ execution_model.compare("MissKHR") == 0) {
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ } else {
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-CallableDataKHR-04704"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "CallableDataKHR Storage Class is limited to RayGenerationKHR, "
+ "ClosestHitKHR, CallableKHR, and MissKHR execution model"));
+ }
+}
+
+TEST_P(ValidateStorageExecutionModel, CallableDataLoad) {
+ std::string execution_model = GetParam();
+ CompileSuccessfully(
+ GenerateExecutionModelCode(execution_model, "CallableDataKHR", false)
+ .c_str(),
+ SPV_ENV_VULKAN_1_2);
+ if (execution_model.compare("RayGenerationKHR") == 0 ||
+ execution_model.compare("ClosestHitKHR") == 0 ||
+ execution_model.compare("CallableKHR") == 0 ||
+ execution_model.compare("MissKHR") == 0) {
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ } else {
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-CallableDataKHR-04704"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "CallableDataKHR Storage Class is limited to RayGenerationKHR, "
+ "ClosestHitKHR, CallableKHR, and MissKHR execution model"));
+ }
+}
+
+TEST_P(ValidateStorageExecutionModel, IncomingCallableDataStore) {
+ std::string execution_model = GetParam();
+ CompileSuccessfully(GenerateExecutionModelCode(
+ execution_model, "IncomingCallableDataKHR", true)
+ .c_str(),
+ SPV_ENV_VULKAN_1_2);
+ if (execution_model.compare("CallableKHR") == 0) {
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ } else {
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-IncomingCallableDataKHR-04705"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("IncomingCallableDataKHR Storage Class is limited to "
+ "CallableKHR execution model"));
+ }
+}
+
+TEST_P(ValidateStorageExecutionModel, IncomingCallableDataLoad) {
+ std::string execution_model = GetParam();
+ CompileSuccessfully(GenerateExecutionModelCode(
+ execution_model, "IncomingCallableDataKHR", false)
+ .c_str(),
+ SPV_ENV_VULKAN_1_2);
+ if (execution_model.compare("CallableKHR") == 0) {
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ } else {
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-IncomingCallableDataKHR-04705"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("IncomingCallableDataKHR Storage Class is limited to "
+ "CallableKHR execution model"));
+ }
+}
+
+TEST_P(ValidateStorageExecutionModel, RayPayloadStore) {
+ std::string execution_model = GetParam();
+ CompileSuccessfully(
+ GenerateExecutionModelCode(execution_model, "RayPayloadKHR", true)
+ .c_str(),
+ SPV_ENV_VULKAN_1_2);
+ if (execution_model.compare("RayGenerationKHR") == 0 ||
+ execution_model.compare("ClosestHitKHR") == 0 ||
+ execution_model.compare("MissKHR") == 0) {
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ } else {
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-RayPayloadKHR-04698"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("RayPayloadKHR Storage Class is limited to RayGenerationKHR, "
+ "ClosestHitKHR, and MissKHR execution model"));
+ }
+}
+
+TEST_P(ValidateStorageExecutionModel, RayPayloadLoad) {
+ std::string execution_model = GetParam();
+ CompileSuccessfully(
+ GenerateExecutionModelCode(execution_model, "RayPayloadKHR", false)
+ .c_str(),
+ SPV_ENV_VULKAN_1_2);
+ if (execution_model.compare("RayGenerationKHR") == 0 ||
+ execution_model.compare("ClosestHitKHR") == 0 ||
+ execution_model.compare("MissKHR") == 0) {
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ } else {
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-RayPayloadKHR-04698"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("RayPayloadKHR Storage Class is limited to RayGenerationKHR, "
+ "ClosestHitKHR, and MissKHR execution model"));
+ }
+}
+
+TEST_P(ValidateStorageExecutionModel, HitAttributeStore) {
+ std::string execution_model = GetParam();
+ CompileSuccessfully(
+ GenerateExecutionModelCode(execution_model, "HitAttributeKHR", true)
+ .c_str(),
+ SPV_ENV_VULKAN_1_2);
+ if (execution_model.compare("IntersectionKHR") == 0) {
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ } else if (execution_model.compare("AnyHitKHR") == 0 ||
+ execution_model.compare("ClosestHitKHR") == 0) {
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-HitAttributeKHR-04703"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("HitAttributeKHR Storage Class variables are read "
+ "only with AnyHitKHR and ClosestHitKHR"));
+ } else {
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-HitAttributeKHR-04701"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "HitAttributeKHR Storage Class is limited to IntersectionKHR, "
+ "AnyHitKHR, sand ClosestHitKHR execution model"));
+ }
+}
+
+TEST_P(ValidateStorageExecutionModel, HitAttributeLoad) {
+ std::string execution_model = GetParam();
+ CompileSuccessfully(
+ GenerateExecutionModelCode(execution_model, "HitAttributeKHR", false)
+ .c_str(),
+ SPV_ENV_VULKAN_1_2);
+ if (execution_model.compare("IntersectionKHR") == 0 ||
+ execution_model.compare("AnyHitKHR") == 0 ||
+ execution_model.compare("ClosestHitKHR") == 0) {
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ } else {
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-HitAttributeKHR-04701"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "HitAttributeKHR Storage Class is limited to IntersectionKHR, "
+ "AnyHitKHR, sand ClosestHitKHR execution model"));
+ }
+}
+
+TEST_P(ValidateStorageExecutionModel, IncomingRayPayloadStore) {
+ std::string execution_model = GetParam();
+ CompileSuccessfully(
+ GenerateExecutionModelCode(execution_model, "IncomingRayPayloadKHR", true)
+ .c_str(),
+ SPV_ENV_VULKAN_1_2);
+ if (execution_model.compare("AnyHitKHR") == 0 ||
+ execution_model.compare("ClosestHitKHR") == 0 ||
+ execution_model.compare("MissKHR") == 0) {
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ } else {
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-IncomingRayPayloadKHR-04699"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("IncomingRayPayloadKHR Storage Class is limited to "
+ "AnyHitKHR, ClosestHitKHR, and MissKHR execution model"));
+ }
+}
+
+TEST_P(ValidateStorageExecutionModel, IncomingRayPayloadLoad) {
+ std::string execution_model = GetParam();
+ CompileSuccessfully(GenerateExecutionModelCode(execution_model,
+ "IncomingRayPayloadKHR", false)
+ .c_str(),
+ SPV_ENV_VULKAN_1_2);
+ if (execution_model.compare("AnyHitKHR") == 0 ||
+ execution_model.compare("ClosestHitKHR") == 0 ||
+ execution_model.compare("MissKHR") == 0) {
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ } else {
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-IncomingRayPayloadKHR-04699"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("IncomingRayPayloadKHR Storage Class is limited to "
+ "AnyHitKHR, ClosestHitKHR, and MissKHR execution model"));
+ }
+}
+
+TEST_P(ValidateStorageExecutionModel, ShaderRecordBufferStore) {
+ std::string execution_model = GetParam();
+ CompileSuccessfully(
+ GenerateExecutionModelCode(execution_model, "ShaderRecordBufferKHR", true)
+ .c_str(),
+ SPV_ENV_VULKAN_1_2);
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("ShaderRecordBufferKHR Storage Class variables are read only"));
+}
+
+TEST_P(ValidateStorageExecutionModel, ShaderRecordBufferLoad) {
+ std::string execution_model = GetParam();
+ CompileSuccessfully(GenerateExecutionModelCode(execution_model,
+ "ShaderRecordBufferKHR", false)
+ .c_str(),
+ SPV_ENV_VULKAN_1_2);
+ if (execution_model.compare("RayGenerationKHR") == 0 ||
+ execution_model.compare("IntersectionKHR") == 0 ||
+ execution_model.compare("AnyHitKHR") == 0 ||
+ execution_model.compare("ClosestHitKHR") == 0 ||
+ execution_model.compare("CallableKHR") == 0 ||
+ execution_model.compare("MissKHR") == 0) {
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ } else {
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-ShaderRecordBufferKHR-07119"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("ShaderRecordBufferKHR Storage Class is limited to "
+ "RayGenerationKHR, IntersectionKHR, AnyHitKHR, "
+ "ClosestHitKHR, CallableKHR, and MissKHR execution model"));
+ }
+}
+
INSTANTIATE_TEST_SUITE_P(MatrixExecutionModel, ValidateStorageExecutionModel,
::testing::Values("RayGenerationKHR",
"IntersectionKHR", "AnyHitKHR",
"ClosestHitKHR", "MissKHR",
- "CallableKHR"));
+ "CallableKHR", "GLCompute"));
} // namespace
} // namespace val
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 0a7e8651..86d0bc46 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -41,6 +41,7 @@ endfunction()
if (NOT ${SPIRV_SKIP_EXECUTABLES})
add_spvtools_tool(TARGET spirv-as SRCS as/as.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
+ add_spvtools_tool(TARGET spirv-diff SRCS diff/diff.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-diff SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
add_spvtools_tool(TARGET spirv-dis SRCS dis/dis.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
add_spvtools_tool(TARGET spirv-val SRCS val/val.cpp util/cli_consumer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
add_spvtools_tool(TARGET spirv-opt SRCS opt/opt.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
diff --git a/tools/diff/diff.cpp b/tools/diff/diff.cpp
new file mode 100644
index 00000000..d3cad04b
--- /dev/null
+++ b/tools/diff/diff.cpp
@@ -0,0 +1,201 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+//
+// 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.
+
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#include <unistd.h>
+#endif
+
+#include "source/diff/diff.h"
+
+#include "source/opt/build_module.h"
+#include "source/opt/ir_context.h"
+#include "spirv-tools/libspirv.hpp"
+#include "tools/io.h"
+#include "tools/util/cli_consumer.h"
+
+static void print_usage(char* argv0) {
+ printf(R"(%s - Compare two SPIR-V files
+
+Usage: %s <src_filename> <dst_filename>
+
+The SPIR-V binary is read from <src_filename> and <dst_filename>. If either
+file ends in .spvasm, the SPIR-V is read as text and disassembled.
+
+The contents of the SPIR-V modules are analyzed and a diff is produced showing a
+logical transformation from src to dst, in src's id-space.
+
+ -h, --help Print this help.
+ --version Display diff version information.
+
+ --color Force color output. The default when printing to a terminal.
+ Overrides a previous --no-color option.
+ --no-color Don't print in color. Overrides a previous --color option.
+ The default when output goes to something other than a
+ terminal (e.g. a pipe, or a shell redirection).
+
+ --no-indent Don't indent instructions.
+
+ --no-header Don't output the header as leading comments.
+
+ --with-id-map Also output the mapping between src and dst outputs.
+
+ --ignore-set-binding
+ Don't use set/binding decorations for variable matching.
+ --ignore-location
+ Don't use location decorations for variable matching.
+)",
+ argv0, argv0);
+}
+
+static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+
+static bool is_assembly(const char* path) {
+ const char* suffix = strrchr(path, '.');
+ if (suffix == nullptr) {
+ return false;
+ }
+
+ return strcmp(suffix, ".spvasm") == 0;
+}
+
+static std::unique_ptr<spvtools::opt::IRContext> load_module(const char* path) {
+ if (is_assembly(path)) {
+ std::vector<char> contents;
+ if (!ReadTextFile<char>(path, &contents)) return {};
+
+ return spvtools::BuildModule(
+ kDefaultEnvironment, spvtools::utils::CLIMessageConsumer,
+ std::string(contents.begin(), contents.end()),
+ spvtools::SpirvTools::kDefaultAssembleOption |
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ }
+
+ std::vector<uint32_t> contents;
+ if (!ReadBinaryFile<uint32_t>(path, &contents)) return {};
+
+ return spvtools::BuildModule(kDefaultEnvironment,
+ spvtools::utils::CLIMessageConsumer,
+ contents.data(), contents.size());
+}
+
+int main(int argc, char** argv) {
+ const char* src_file = nullptr;
+ const char* dst_file = nullptr;
+ bool color_is_possible =
+#if SPIRV_COLOR_TERMINAL
+ true;
+#else
+ false;
+#endif
+ bool force_color = false;
+ bool force_no_color = false;
+ bool allow_indent = true;
+ bool no_header = false;
+ bool dump_id_map = false;
+ bool ignore_set_binding = false;
+ bool ignore_location = false;
+
+ for (int argi = 1; argi < argc; ++argi) {
+ if ('-' == argv[argi][0]) {
+ switch (argv[argi][1]) {
+ case 'h':
+ print_usage(argv[0]);
+ return 0;
+ case '-': {
+ // Long options
+ if (strcmp(argv[argi], "--no-color") == 0) {
+ force_no_color = true;
+ force_color = false;
+ } else if (strcmp(argv[argi], "--color") == 0) {
+ force_no_color = false;
+ force_color = true;
+ } else if (strcmp(argv[argi], "--no-indent") == 0) {
+ allow_indent = false;
+ } else if (strcmp(argv[argi], "--no-header") == 0) {
+ no_header = true;
+ } else if (strcmp(argv[argi], "--with-id-map") == 0) {
+ dump_id_map = true;
+ } else if (strcmp(argv[argi], "--ignore-set-binding") == 0) {
+ ignore_set_binding = true;
+ } else if (strcmp(argv[argi], "--ignore-location") == 0) {
+ ignore_location = true;
+ } else if (strcmp(argv[argi], "--help") == 0) {
+ print_usage(argv[0]);
+ return 0;
+ } else if (strcmp(argv[argi], "--version") == 0) {
+ printf("%s\n", spvSoftwareVersionDetailsString());
+ printf("Target: %s\n",
+ spvTargetEnvDescription(kDefaultEnvironment));
+ return 0;
+ } else {
+ print_usage(argv[0]);
+ return 1;
+ }
+ } break;
+ default:
+ print_usage(argv[0]);
+ return 1;
+ }
+ } else {
+ if (src_file == nullptr) {
+ src_file = argv[argi];
+ } else if (dst_file == nullptr) {
+ dst_file = argv[argi];
+ } else {
+ fprintf(stderr, "error: More than two input files specified\n");
+ return 1;
+ }
+ }
+ }
+
+ if (src_file == nullptr || dst_file == nullptr) {
+ print_usage(argv[0]);
+ return 1;
+ }
+
+ spvtools::diff::Options options;
+
+ if (allow_indent) options.indent = true;
+ if (no_header) options.no_header = true;
+ if (dump_id_map) options.dump_id_map = true;
+ if (ignore_set_binding) options.ignore_set_binding = true;
+ if (ignore_location) options.ignore_location = true;
+
+ if (color_is_possible && !force_no_color) {
+ bool output_is_tty = true;
+#if defined(_POSIX_VERSION)
+ output_is_tty = isatty(fileno(stdout));
+#endif
+ if (output_is_tty || force_color) {
+ options.color_output = true;
+ }
+ }
+
+ std::unique_ptr<spvtools::opt::IRContext> src = load_module(src_file);
+ std::unique_ptr<spvtools::opt::IRContext> dst = load_module(dst_file);
+
+ if (!src) {
+ fprintf(stderr, "error: Loading src file\n");
+ }
+ if (!dst) {
+ fprintf(stderr, "error: Loading dst file\n");
+ }
+ if (!src || !dst) {
+ return 1;
+ }
+
+ spvtools::diff::Diff(src.get(), dst.get(), std::cout, options);
+
+ return 0;
+}
diff --git a/tools/fuzz/fuzz.cpp b/tools/fuzz/fuzz.cpp
index 306f9255..ca6633a6 100644
--- a/tools/fuzz/fuzz.cpp
+++ b/tools/fuzz/fuzz.cpp
@@ -673,19 +673,6 @@ void DumpTransformationsBinary(
transformations_file.close();
}
-// The Chromium project applies the following patch to the protobuf library:
-//
-// source.chromium.org/chromium/chromium/src/+/main:third_party/protobuf/patches/0003-remove-static-initializers.patch
-//
-// This affects how Status objects must be constructed. This method provides a
-// convenient way to get the OK status that works both with and without the
-// patch. With the patch OK is a StatusPod, from which a Status can be
-// constructed. Without the patch, OK is already a Status, and we harmlessly
-// copy-construct the result from it.
-google::protobuf::util::Status GetProtobufOkStatus() {
- return google::protobuf::util::Status(google::protobuf::util::Status::OK);
-}
-
// Dumps |transformations| to file |filename| in JSON format. Useful for
// interactive debugging.
void DumpTransformationsJson(
@@ -696,7 +683,7 @@ void DumpTransformationsJson(
json_options.add_whitespace = true;
auto json_generation_status = google::protobuf::util::MessageToJsonString(
transformations, &json_string, json_options);
- if (json_generation_status == GetProtobufOkStatus()) {
+ if (json_generation_status.ok()) {
std::ofstream transformations_json_file(filename);
transformations_json_file << json_string;
transformations_json_file.close();
@@ -747,8 +734,9 @@ int main(int argc, const char** argv) {
std::string facts_json_string((std::istreambuf_iterator<char>(facts_input)),
std::istreambuf_iterator<char>());
facts_input.close();
- if (GetProtobufOkStatus() != google::protobuf::util::JsonStringToMessage(
- facts_json_string, &initial_facts)) {
+ if (!google::protobuf::util::JsonStringToMessage(facts_json_string,
+ &initial_facts)
+ .ok()) {
spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error reading facts data");
return 1;
}
@@ -828,7 +816,7 @@ int main(int argc, const char** argv) {
json_options.add_whitespace = true;
auto json_generation_status = google::protobuf::util::MessageToJsonString(
transformations_applied, &json_string, json_options);
- if (json_generation_status != GetProtobufOkStatus()) {
+ if (!json_generation_status.ok()) {
spvtools::Error(FuzzDiagnostic, nullptr, {},
"Error writing out transformations in JSON format");
return 1;
diff --git a/tools/io.h b/tools/io.h
index 83a85c1d..9dc834ed 100644
--- a/tools/io.h
+++ b/tools/io.h
@@ -26,9 +26,15 @@
#define SET_STDIN_TO_BINARY_MODE() _setmode(_fileno(stdin), O_BINARY);
#define SET_STDIN_TO_TEXT_MODE() _setmode(_fileno(stdin), O_TEXT);
+#define SET_STDOUT_TO_BINARY_MODE() _setmode(_fileno(stdout), O_BINARY);
+#define SET_STDOUT_TO_TEXT_MODE() _setmode(_fileno(stdout), O_TEXT);
+#define SET_STDOUT_MODE(mode) _setmode(_fileno(stdout), mode);
#else
#define SET_STDIN_TO_BINARY_MODE()
#define SET_STDIN_TO_TEXT_MODE()
+#define SET_STDOUT_TO_BINARY_MODE() 0
+#define SET_STDOUT_TO_TEXT_MODE() 0
+#define SET_STDOUT_MODE(mode)
#endif
// Appends the contents of the |file| to |data|, assuming each element in the
@@ -115,6 +121,44 @@ bool ReadTextFile(const char* filename, std::vector<T>* data) {
return succeeded;
}
+namespace {
+// A class to create and manage a file for outputting data.
+class OutputFile {
+ public:
+ // Opens |filename| in the given mode. If |filename| is nullptr, the empty
+ // string or "-", stdout will be set to the given mode.
+ OutputFile(const char* filename, const char* mode) {
+ const bool use_stdout =
+ !filename || (filename[0] == '-' && filename[1] == '\0');
+ if (use_stdout) {
+ if (strchr(mode, 'b')) {
+ old_mode_ = SET_STDOUT_TO_BINARY_MODE();
+ } else {
+ old_mode_ = SET_STDOUT_TO_TEXT_MODE();
+ }
+ fp_ = stdout;
+ } else {
+ fp_ = fopen(filename, mode);
+ }
+ }
+
+ ~OutputFile() {
+ if (fp_ == stdout) {
+ SET_STDOUT_MODE(old_mode_);
+ } else if (fp_ != nullptr) {
+ fclose(fp_);
+ }
+ }
+
+ // Returns a file handle to the file.
+ FILE* GetFileHandle() const { return fp_; }
+
+ private:
+ FILE* fp_;
+ int old_mode_;
+};
+} // namespace
+
// Writes the given |data| into the file named as |filename| using the given
// |mode|, assuming |data| is an array of |count| elements of type |T|. If
// |filename| is nullptr or "-", writes to standard output. If any error occurs,
@@ -122,20 +166,19 @@ bool ReadTextFile(const char* filename, std::vector<T>* data) {
template <typename T>
bool WriteFile(const char* filename, const char* mode, const T* data,
size_t count) {
- const bool use_stdout =
- !filename || (filename[0] == '-' && filename[1] == '\0');
- if (FILE* fp = (use_stdout ? stdout : fopen(filename, mode))) {
- size_t written = fwrite(data, sizeof(T), count, fp);
- if (count != written) {
- fprintf(stderr, "error: could not write to file '%s'\n", filename);
- if (!use_stdout) fclose(fp);
- return false;
- }
- if (!use_stdout) fclose(fp);
- } else {
+ OutputFile file(filename, mode);
+ FILE* fp = file.GetFileHandle();
+ if (fp == nullptr) {
fprintf(stderr, "error: could not open file '%s'\n", filename);
return false;
}
+
+ size_t written = fwrite(data, sizeof(T), count, fp);
+ if (count != written) {
+ fprintf(stderr, "error: could not write to file '%s'\n", filename);
+ return false;
+ }
+
return true;
}
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index 63511a6a..ce2103ca 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -157,12 +157,6 @@ Options (in lexicographical order):)",
another. It will only propagate an array if the source is never
written to, and the only store to the target is the copy.)");
printf(R"(
- --decompose-initialized-variables
- Decomposes initialized variable declarations into a declaration
- followed by a store of the initial value. This is done to work
- around known issues with some Vulkan drivers for initialize
- variables.)");
- printf(R"(
--replace-desc-array-access-using-var-index
Replaces accesses to descriptor arrays based on a variable index
with a switch that has a case for every possible value of the
@@ -208,6 +202,10 @@ Options (in lexicographical order):)",
unused stores to vector components, that are not removed by
aggressive dead code elimination.)");
printf(R"(
+ --eliminate-dead-input-components
+ Deletes unused components from input variables. Currently
+ deletes trailing unused elements from input arrays.)");
+ printf(R"(
--eliminate-dead-variables
Deletes module scope variables that are not referenced.)");
printf(R"(
@@ -233,6 +231,10 @@ Options (in lexicographical order):)",
loads and stores. Performed only on entry point call tree
functions.)");
printf(R"(
+ --fix-func-call-param
+ fix non memory argument for the function call, replace
+ accesschain pointer argument with a variable.)");
+ printf(R"(
--flatten-decorations
Replace decoration groups with repeated OpDecorate and
OpMemberDecorate instructions.)");
@@ -483,9 +485,6 @@ Options (in lexicographical order):)",
--strength-reduction
Replaces instructions with equivalent and less expensive ones.)");
printf(R"(
- --strip-atomic-counter-memory
- Removes AtomicCountMemory bit from memory semantics values.)");
- printf(R"(
--strip-debug
Remove all debug instructions.)");
printf(R"(
diff --git a/tools/sva/yarn.lock b/tools/sva/yarn.lock
index afa11f6c..e7b735e0 100644
--- a/tools/sva/yarn.lock
+++ b/tools/sva/yarn.lock
@@ -1260,9 +1260,9 @@ path-to-regexp@2.2.1:
integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==
pathval@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0"
- integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA=
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d"
+ integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==
prelude-ls@~1.1.2:
version "1.1.2"
diff --git a/utils/check_copyright.py b/utils/check_copyright.py
index b6dc933e..aa647af5 100755
--- a/utils/check_copyright.py
+++ b/utils/check_copyright.py
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""Checks for copyright notices in all the files that need them under the
+
current directory. Optionally insert them. When inserting, replaces
an MIT or Khronos free use license with Apache 2.
"""
@@ -41,11 +42,28 @@ AUTHORS = ['The Khronos Group Inc.',
'Mostafa Ashraf',
'Shiyu Liu',
'ZHOU He']
-CURRENT_YEAR='2021'
-
-YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020|2021|2022)'
-COPYRIGHT_RE = re.compile(
- 'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS)))
+CURRENT_YEAR = 2022
+
+FIRST_YEAR = 2014
+FINAL_YEAR = CURRENT_YEAR + 5
+# A regular expression to match the valid years in the copyright information.
+YEAR_REGEX = '(' + '|'.join(
+ str(year) for year in range(FIRST_YEAR, FINAL_YEAR + 1)) + ')'
+
+# A regular expression to make a range of years in the form <year1>-<year2>.
+YEAR_RANGE_REGEX = '('
+for year1 in range(FIRST_YEAR, FINAL_YEAR + 1):
+ for year2 in range(year1 + 1, FINAL_YEAR + 1):
+ YEAR_RANGE_REGEX += str(year1) + '-' + str(year2) + '|'
+YEAR_RANGE_REGEX = YEAR_RANGE_REGEX[:-1] + ')'
+
+# In the copyright info, the year can be a single year or a range. This is a
+# regex to make sure it matches one of them.
+YEAR_OR_RANGE_REGEX = '(' + YEAR_REGEX + '|' + YEAR_RANGE_REGEX + ')'
+
+# The final regular expression to match a valid copyright line.
+COPYRIGHT_RE = re.compile('Copyright \(c\) {} ({})'.format(
+ YEAR_OR_RANGE_REGEX, '|'.join(AUTHORS)))
MIT_BEGIN_RE = re.compile('Permission is hereby granted, '
'free of charge, to any person obtaining a')
diff --git a/utils/roll_deps.sh b/utils/roll_deps.sh
index cef8b526..20c061fd 100755
--- a/utils/roll_deps.sh
+++ b/utils/roll_deps.sh
@@ -39,6 +39,8 @@ if [[ $(git diff --stat) != '' ]]; then
exit 1
fi
+echo "*** Ignore messages about running 'git cl upload' ***"
+
old_head=$(git rev-parse HEAD)
set +e
diff --git a/utils/update_build_version.py b/utils/update_build_version.py
index 321de74b..2a1ca600 100755
--- a/utils/update_build_version.py
+++ b/utils/update_build_version.py
@@ -17,16 +17,16 @@
# Updates an output file with version info unless the new content is the same
# as the existing content.
#
-# Args: <spirv-tools_dir> <output-file>
+# Args: <changes-file> <output-file>
#
# The output file will contain a line of text consisting of two C source syntax
# string literals separated by a comma:
-# - The software version deduced from the CHANGES file in the given directory.
+# - The software version deduced from the given CHANGES file.
# - A longer string with the project name, the software version number, and
-# git commit information for the directory. The commit information
-# is the output of "git describe" if that succeeds, or "git rev-parse HEAD"
-# if that succeeds, or otherwise a message containing the phrase
-# "unknown hash".
+# git commit information for the CHANGES file's directory. The commit
+# information is the output of "git describe" if that succeeds, or "git
+# rev-parse HEAD" if that succeeds, or otherwise a message containing the
+# phrase "unknown hash".
# The string contents are escaped as necessary.
import datetime
@@ -73,9 +73,8 @@ def command_output(cmd, directory):
return stdout
-def deduce_software_version(directory):
- """Returns a software version number parsed from the CHANGES file
- in the given directory.
+def deduce_software_version(changes_file):
+ """Returns a software version number parsed from the given CHANGES file.
The CHANGES file describes most recent versions first.
"""
@@ -85,7 +84,6 @@ def deduce_software_version(directory):
# unexpected carriage returns on a linefeed-only system such as
# Linux.
pattern = re.compile(r'^(v\d+\.\d+(-dev)?) \d\d\d\d-\d\d-\d\d\s*$')
- changes_file = os.path.join(directory, 'CHANGES')
with open(changes_file, mode='r') as f:
for line in f.readlines():
match = pattern.match(line)
@@ -125,16 +123,17 @@ def describe(directory):
def main():
if len(sys.argv) != 3:
- print('usage: {} <spirv-tools-dir> <output-file>'.format(sys.argv[0]))
+ print('usage: {} <changes-files> <output-file>'.format(sys.argv[0]))
sys.exit(1)
output_file = sys.argv[2]
mkdir_p(os.path.dirname(output_file))
software_version = deduce_software_version(sys.argv[1])
+ directory = os.path.dirname(sys.argv[1])
new_content = '"{}", "SPIRV-Tools {} {}"\n'.format(
software_version, software_version,
- describe(sys.argv[1]).replace('"', '\\"'))
+ describe(directory).replace('"', '\\"'))
if os.path.isfile(output_file):
with open(output_file, 'r') as f: