aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-08-25 07:44:22 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-08-25 07:44:22 +0000
commitf4336c65587722cc275c01db477a8e0b5b926a4d (patch)
treec3f2b67062030ff8b7b7d13473b0202bfd312c12
parentd8490920f314964de2b415e54dcb03c100e30f16 (diff)
parent8bfcaf9ebfeefe4daaa55095be49f6db49a7e704 (diff)
downloadSPIRV-Tools-gki13-boot-release.tar.gz
Snap for 8992082 from 8bfcaf9ebfeefe4daaa55095be49f6db49a7e704 to gki13-boot-releasegki13-boot-release
Change-Id: I5ce2bcddb71283ff63853f0e94287698b2c73a3c
-rw-r--r--.appveyor.yml90
-rw-r--r--.github/workflows/wasm.yml14
-rw-r--r--Android.bp23
-rw-r--r--BUILD.bazel43
-rw-r--r--BUILD.gn513
-rw-r--r--CHANGES66
-rw-r--r--CMakeLists.txt23
-rw-r--r--CONTRIBUTING.md7
-rw-r--r--DEPS8
-rw-r--r--README.md167
-rw-r--r--build_defs.bzl18
-rw-r--r--docker-compose.yml10
-rw-r--r--docs/downloads.md20
-rw-r--r--include/spirv-tools/libspirv.h18
-rw-r--r--include/spirv-tools/libspirv.hpp10
-rw-r--r--include/spirv-tools/linter.hpp48
-rw-r--r--include/spirv-tools/optimizer.hpp89
-rwxr-xr-xkokoro/linux-clang-ubsan/build.sh24
-rw-r--r--kokoro/linux-clang-ubsan/continuous.cfg16
-rw-r--r--kokoro/linux-clang-ubsan/presubmit.cfg16
-rwxr-xr-xkokoro/scripts/linux/build-docker.sh15
-rw-r--r--kokoro/scripts/linux/build.sh2
-rw-r--r--kokoro/scripts/macos/build.sh2
-rw-r--r--kokoro/scripts/windows/build.bat4
-rw-r--r--source/CMakeLists.txt17
-rw-r--r--source/binary.cpp42
-rw-r--r--source/binary.h7
-rw-r--r--source/cfa.h2
-rw-r--r--source/common_debug_info.h64
-rw-r--r--source/diagnostic.cpp2
-rw-r--r--source/disassemble.cpp488
-rw-r--r--source/disassemble.h59
-rw-r--r--source/ext_inst.cpp11
-rw-r--r--source/ext_inst.h4
-rw-r--r--source/extensions.cpp4
-rw-r--r--source/fuzz/CMakeLists.txt4
-rw-r--r--source/fuzz/available_instructions.cpp2
-rw-r--r--source/fuzz/fact_manager/fact_manager.h8
-rw-r--r--source/fuzz/fuzzer.cpp15
-rw-r--r--source/fuzz/fuzzer.h9
-rw-r--r--source/fuzz/fuzzer_context.cpp11
-rw-r--r--source/fuzz/fuzzer_context.h19
-rw-r--r--source/fuzz/fuzzer_pass.cpp33
-rw-r--r--source/fuzz/fuzzer_pass.h7
-rw-r--r--source/fuzz/fuzzer_pass_add_access_chains.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_access_chains.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_composite_extract.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_composite_extract.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_composite_inserts.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_composite_inserts.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_composite_types.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_composite_types.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_copy_memory.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_copy_memory.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_dead_blocks.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_dead_blocks.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_dead_breaks.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_dead_breaks.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_dead_continues.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_dead_continues.h9
-rw-r--r--source/fuzz/fuzzer_pass_add_equation_instructions.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_equation_instructions.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_function_calls.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_function_calls.h9
-rw-r--r--source/fuzz/fuzzer_pass_add_global_variables.cpp9
-rw-r--r--source/fuzz/fuzzer_pass_add_global_variables.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_image_sample_unused_components.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_loads.cpp73
-rw-r--r--source/fuzz/fuzzer_pass_add_loads.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_local_variables.cpp9
-rw-r--r--source/fuzz/fuzzer_pass_add_local_variables.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_loop_preheaders.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_loop_preheaders.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_no_contraction_decorations.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_opphi_synonyms.h9
-rw-r--r--source/fuzz/fuzzer_pass_add_parameters.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_parameters.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_relaxed_decorations.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_stores.cpp62
-rw-r--r--source/fuzz/fuzzer_pass_add_stores.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_synonyms.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_add_synonyms.h3
-rw-r--r--source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp7
-rw-r--r--source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h3
-rw-r--r--source/fuzz/fuzzer_pass_adjust_branch_weights.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_adjust_branch_weights.h3
-rw-r--r--source/fuzz/fuzzer_pass_adjust_function_controls.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_adjust_function_controls.h3
-rw-r--r--source/fuzz/fuzzer_pass_adjust_loop_controls.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_adjust_loop_controls.h3
-rw-r--r--source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h3
-rw-r--r--source/fuzz/fuzzer_pass_adjust_selection_controls.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_adjust_selection_controls.h3
-rw-r--r--source/fuzz/fuzzer_pass_apply_id_synonyms.cpp7
-rw-r--r--source/fuzz/fuzzer_pass_apply_id_synonyms.h3
-rw-r--r--source/fuzz/fuzzer_pass_construct_composites.cpp7
-rw-r--r--source/fuzz/fuzzer_pass_construct_composites.h3
-rw-r--r--source/fuzz/fuzzer_pass_copy_objects.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_copy_objects.h3
-rw-r--r--source/fuzz/fuzzer_pass_donate_modules.cpp9
-rw-r--r--source/fuzz/fuzzer_pass_donate_modules.h3
-rw-r--r--source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h3
-rw-r--r--source/fuzz/fuzzer_pass_expand_vector_reductions.cpp7
-rw-r--r--source/fuzz/fuzzer_pass_expand_vector_reductions.h3
-rw-r--r--source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_flatten_conditional_branches.h3
-rw-r--r--source/fuzz/fuzzer_pass_inline_functions.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_inline_functions.h3
-rw-r--r--source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h3
-rw-r--r--source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_interchange_zero_like_constants.h3
-rw-r--r--source/fuzz/fuzzer_pass_invert_comparison_operators.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_invert_comparison_operators.h3
-rw-r--r--source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h3
-rw-r--r--source/fuzz/fuzzer_pass_merge_blocks.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_merge_blocks.h3
-rw-r--r--source/fuzz/fuzzer_pass_merge_function_returns.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_merge_function_returns.h3
-rw-r--r--source/fuzz/fuzzer_pass_mutate_pointers.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_mutate_pointers.h3
-rw-r--r--source/fuzz/fuzzer_pass_obfuscate_constants.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_obfuscate_constants.h3
-rw-r--r--source/fuzz/fuzzer_pass_outline_functions.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_outline_functions.h9
-rw-r--r--source/fuzz/fuzzer_pass_permute_blocks.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_permute_blocks.h3
-rw-r--r--source/fuzz/fuzzer_pass_permute_function_parameters.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_permute_function_parameters.h3
-rw-r--r--source/fuzz/fuzzer_pass_permute_function_variables.cpp6
-rw-r--r--source/fuzz/fuzzer_pass_permute_function_variables.h3
-rw-r--r--source/fuzz/fuzzer_pass_permute_instructions.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_permute_instructions.h3
-rw-r--r--source/fuzz/fuzzer_pass_permute_phi_operands.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_permute_phi_operands.h3
-rw-r--r--source/fuzz/fuzzer_pass_propagate_instructions_down.cpp8
-rw-r--r--source/fuzz/fuzzer_pass_propagate_instructions_down.h3
-rw-r--r--source/fuzz/fuzzer_pass_propagate_instructions_up.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_propagate_instructions_up.h3
-rw-r--r--source/fuzz/fuzzer_pass_push_ids_through_variables.cpp15
-rw-r--r--source/fuzz/fuzzer_pass_push_ids_through_variables.h3
-rw-r--r--source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h3
-rw-r--r--source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h3
-rw-r--r--source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h3
-rw-r--r--source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h3
-rw-r--r--source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_replace_irrelevant_ids.h3
-rw-r--r--source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h3
-rw-r--r--source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h3
-rw-r--r--source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h3
-rw-r--r--source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h3
-rw-r--r--source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_replace_parameter_with_global.h3
-rw-r--r--source/fuzz/fuzzer_pass_replace_params_with_struct.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_replace_params_with_struct.h3
-rw-r--r--source/fuzz/fuzzer_pass_split_blocks.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_split_blocks.h3
-rw-r--r--source/fuzz/fuzzer_pass_swap_commutable_operands.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_swap_commutable_operands.h3
-rw-r--r--source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h3
-rw-r--r--source/fuzz/fuzzer_pass_swap_functions.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_swap_functions.h3
-rw-r--r--source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h3
-rw-r--r--source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp5
-rw-r--r--source/fuzz/fuzzer_pass_wrap_regions_in_selections.h3
-rw-r--r--source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp144
-rw-r--r--source/fuzz/fuzzer_pass_wrap_vector_synonym.h38
-rw-r--r--source/fuzz/fuzzer_util.cpp237
-rw-r--r--source/fuzz/fuzzer_util.h32
-rw-r--r--source/fuzz/pass_management/repeated_pass_instances.h2
-rw-r--r--source/fuzz/pass_management/repeated_pass_recommender_standard.cpp6
-rw-r--r--source/fuzz/protobufs/spirvfuzz_protobufs.h5
-rw-r--r--source/fuzz/protobufs/spvtoolsfuzz.proto81
-rw-r--r--source/fuzz/transformation.cpp4
-rw-r--r--source/fuzz/transformation_add_constant_composite.cpp2
-rw-r--r--source/fuzz/transformation_add_constant_null.cpp4
-rw-r--r--source/fuzz/transformation_add_dead_block.cpp6
-rw-r--r--source/fuzz/transformation_add_dead_break.cpp2
-rw-r--r--source/fuzz/transformation_add_dead_continue.cpp3
-rw-r--r--source/fuzz/transformation_add_global_undef.cpp9
-rw-r--r--source/fuzz/transformation_add_global_undef.h2
-rw-r--r--source/fuzz/transformation_add_synonym.cpp3
-rw-r--r--source/fuzz/transformation_composite_construct.cpp2
-rw-r--r--source/fuzz/transformation_composite_extract.cpp2
-rw-r--r--source/fuzz/transformation_composite_insert.cpp8
-rw-r--r--source/fuzz/transformation_duplicate_region_with_selection.cpp6
-rw-r--r--source/fuzz/transformation_expand_vector_reduction.cpp2
-rw-r--r--source/fuzz/transformation_flatten_conditional_branch.cpp10
-rw-r--r--source/fuzz/transformation_load.cpp154
-rw-r--r--source/fuzz/transformation_load.h11
-rw-r--r--source/fuzz/transformation_merge_blocks.cpp3
-rw-r--r--source/fuzz/transformation_merge_blocks.h1
-rw-r--r--source/fuzz/transformation_outline_function.cpp3
-rw-r--r--source/fuzz/transformation_propagate_instruction_down.cpp10
-rw-r--r--source/fuzz/transformation_push_id_through_variable.cpp4
-rw-r--r--source/fuzz/transformation_replace_id_with_synonym.cpp60
-rw-r--r--source/fuzz/transformation_replace_id_with_synonym.h17
-rw-r--r--source/fuzz/transformation_store.cpp146
-rw-r--r--source/fuzz/transformation_store.h11
-rw-r--r--source/fuzz/transformation_vector_shuffle.cpp4
-rw-r--r--source/fuzz/transformation_wrap_vector_synonym.cpp200
-rw-r--r--source/fuzz/transformation_wrap_vector_synonym.h80
-rw-r--r--source/link/CMakeLists.txt2
-rw-r--r--source/link/linker.cpp216
-rw-r--r--source/lint/CMakeLists.txt61
-rw-r--r--source/lint/divergence_analysis.cpp245
-rw-r--r--source/lint/divergence_analysis.h163
-rw-r--r--source/lint/lint_divergent_derivatives.cpp169
-rw-r--r--source/lint/linter.cpp60
-rw-r--r--source/lint/lints.h34
-rw-r--r--source/name_mapper.cpp11
-rw-r--r--source/opcode.cpp4
-rw-r--r--source/operand.cpp6
-rw-r--r--source/opt/CMakeLists.txt18
-rw-r--r--source/opt/aggressive_dead_code_elim_pass.cpp805
-rw-r--r--source/opt/aggressive_dead_code_elim_pass.h152
-rw-r--r--source/opt/amd_ext_to_khr.cpp24
-rw-r--r--source/opt/amd_ext_to_khr.h2
-rw-r--r--source/opt/basic_block.h2
-rw-r--r--source/opt/block_merge_pass.cpp6
-rw-r--r--source/opt/block_merge_util.cpp20
-rw-r--r--source/opt/ccp_pass.cpp47
-rw-r--r--source/opt/ccp_pass.h16
-rw-r--r--source/opt/cfg.h2
-rw-r--r--source/opt/combine_access_chains.cpp4
-rw-r--r--source/opt/compact_ids_pass.cpp5
-rw-r--r--source/opt/const_folding_rules.cpp132
-rw-r--r--source/opt/constants.cpp28
-rw-r--r--source/opt/constants.h16
-rw-r--r--source/opt/control_dependence.cpp156
-rw-r--r--source/opt/control_dependence.h197
-rw-r--r--source/opt/convert_to_half_pass.cpp36
-rw-r--r--source/opt/convert_to_half_pass.h2
-rw-r--r--source/opt/convert_to_sampled_image_pass.cpp437
-rw-r--r--source/opt/convert_to_sampled_image_pass.h207
-rw-r--r--source/opt/copy_prop_arrays.cpp29
-rw-r--r--source/opt/dataflow.cpp91
-rw-r--r--source/opt/dataflow.h148
-rw-r--r--source/opt/dead_branch_elim_pass.cpp5
-rw-r--r--source/opt/dead_branch_elim_pass.h4
-rw-r--r--source/opt/dead_insert_elim_pass.cpp4
-rw-r--r--source/opt/debug_info_manager.cpp332
-rw-r--r--source/opt/debug_info_manager.h16
-rw-r--r--source/opt/decoration_manager.cpp8
-rw-r--r--source/opt/decoration_manager.h4
-rw-r--r--source/opt/def_use_manager.cpp60
-rw-r--r--source/opt/def_use_manager.h50
-rw-r--r--source/opt/desc_sroa.cpp231
-rw-r--r--source/opt/desc_sroa.h50
-rw-r--r--source/opt/desc_sroa_util.cpp117
-rw-r--r--source/opt/desc_sroa_util.h54
-rw-r--r--source/opt/dominator_tree.cpp4
-rw-r--r--source/opt/dominator_tree.h2
-rw-r--r--source/opt/eliminate_dead_members_pass.cpp35
-rw-r--r--source/opt/feature_manager.cpp10
-rw-r--r--source/opt/feature_manager.h8
-rw-r--r--source/opt/fold.cpp2
-rw-r--r--source/opt/fold_spec_constant_op_and_composite_pass.cpp2
-rw-r--r--source/opt/folding_rules.cpp241
-rw-r--r--source/opt/function.h3
-rw-r--r--source/opt/graphics_robust_access_pass.cpp16
-rw-r--r--source/opt/graphics_robust_access_pass.h2
-rw-r--r--source/opt/if_conversion.cpp1
-rw-r--r--source/opt/inline_exhaustive_pass.cpp2
-rw-r--r--source/opt/inline_opaque_pass.cpp2
-rw-r--r--source/opt/inline_pass.cpp27
-rw-r--r--source/opt/inst_bindless_check_pass.h4
-rw-r--r--source/opt/inst_buff_addr_check_pass.cpp60
-rw-r--r--source/opt/inst_buff_addr_check_pass.h12
-rw-r--r--source/opt/inst_debug_printf_pass.cpp12
-rw-r--r--source/opt/instruction.cpp129
-rw-r--r--source/opt/instruction.h62
-rw-r--r--source/opt/instrument_pass.h6
-rw-r--r--source/opt/ir_builder.h22
-rw-r--r--source/opt/ir_context.cpp35
-rw-r--r--source/opt/ir_context.h45
-rw-r--r--source/opt/ir_loader.cpp70
-rw-r--r--source/opt/iterator.h2
-rw-r--r--source/opt/local_access_chain_convert_pass.cpp22
-rw-r--r--source/opt/local_access_chain_convert_pass.h2
-rw-r--r--source/opt/local_single_block_elim_pass.cpp26
-rw-r--r--source/opt/local_single_store_elim_pass.cpp32
-rw-r--r--source/opt/loop_descriptor.cpp6
-rw-r--r--source/opt/loop_descriptor.h2
-rw-r--r--source/opt/loop_fission.cpp4
-rw-r--r--source/opt/loop_fission.h2
-rw-r--r--source/opt/loop_fusion.cpp4
-rw-r--r--source/opt/loop_fusion.h2
-rw-r--r--source/opt/loop_fusion_pass.h2
-rw-r--r--source/opt/loop_peeling.h2
-rw-r--r--source/opt/loop_unroller.cpp20
-rw-r--r--source/opt/loop_unswitch_pass.cpp2
-rw-r--r--source/opt/loop_unswitch_pass.h2
-rw-r--r--source/opt/loop_utils.h4
-rw-r--r--source/opt/mem_pass.cpp9
-rw-r--r--source/opt/merge_return_pass.cpp21
-rw-r--r--source/opt/merge_return_pass.h8
-rw-r--r--source/opt/module.cpp67
-rw-r--r--source/opt/module.h14
-rw-r--r--source/opt/optimizer.cpp94
-rw-r--r--source/opt/pass.cpp4
-rw-r--r--source/opt/pass.h2
-rw-r--r--source/opt/pass_manager.cpp14
-rw-r--r--source/opt/pass_manager.h4
-rw-r--r--source/opt/passes.h6
-rw-r--r--source/opt/private_to_local_pass.cpp6
-rw-r--r--source/opt/private_to_local_pass.h2
-rw-r--r--source/opt/reduce_load_size.cpp7
-rw-r--r--source/opt/reduce_load_size.h8
-rw-r--r--source/opt/redundancy_elimination.cpp4
-rw-r--r--source/opt/redundancy_elimination.h2
-rw-r--r--source/opt/reflect.h5
-rw-r--r--source/opt/register_pressure.cpp2
-rw-r--r--source/opt/relax_float_ops_pass.cpp2
-rw-r--r--source/opt/remove_duplicates_pass.cpp5
-rw-r--r--source/opt/remove_unused_interface_variables_pass.cpp93
-rw-r--r--source/opt/remove_unused_interface_variables_pass.h26
-rw-r--r--source/opt/replace_desc_array_access_using_var_index.cpp423
-rw-r--r--source/opt/replace_desc_array_access_using_var_index.h204
-rw-r--r--source/opt/replace_invalid_opc.cpp23
-rw-r--r--source/opt/scalar_analysis.cpp2
-rw-r--r--source/opt/scalar_analysis_nodes.h2
-rw-r--r--source/opt/scalar_analysis_simplification.cpp4
-rw-r--r--source/opt/scalar_replacement_pass.cpp32
-rw-r--r--source/opt/scalar_replacement_pass.h3
-rw-r--r--source/opt/set_spec_constant_default_value_pass.cpp33
-rw-r--r--source/opt/simplification_pass.cpp4
-rw-r--r--source/opt/spread_volatile_semantics.cpp314
-rw-r--r--source/opt/spread_volatile_semantics.h110
-rw-r--r--source/opt/ssa_rewrite_pass.cpp3
-rw-r--r--source/opt/strength_reduction_pass.h2
-rw-r--r--source/opt/strip_debug_info_pass.cpp13
-rw-r--r--source/opt/strip_nonsemantic_info_pass.cpp (renamed from source/opt/strip_reflect_info_pass.cpp)47
-rw-r--r--source/opt/strip_nonsemantic_info_pass.h (renamed from source/opt/strip_reflect_info_pass.h)10
-rw-r--r--source/opt/type_manager.cpp9
-rw-r--r--source/opt/type_manager.h7
-rw-r--r--source/opt/types.cpp1
-rw-r--r--source/opt/unify_const_pass.cpp2
-rw-r--r--source/opt/upgrade_memory_model.cpp8
-rw-r--r--source/opt/value_number_table.cpp2
-rw-r--r--source/opt/vector_dce.cpp36
-rw-r--r--source/opt/vector_dce.h9
-rw-r--r--source/print.cpp2
-rw-r--r--source/reduce/CMakeLists.txt20
-rw-r--r--source/reduce/reducer.cpp5
-rw-r--r--source/reduce/remove_struct_member_reduction_opportunity.cpp2
-rw-r--r--source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp4
-rw-r--r--source/reduce/structured_construct_to_block_reduction_opportunity.cpp67
-rw-r--r--source/reduce/structured_construct_to_block_reduction_opportunity.h49
-rw-r--r--source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp185
-rw-r--r--source/reduce/structured_construct_to_block_reduction_opportunity_finder.h57
-rw-r--r--source/reduce/structured_loop_to_selection_reduction_opportunity.cpp19
-rw-r--r--source/reduce/structured_loop_to_selection_reduction_opportunity.h14
-rw-r--r--source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp4
-rw-r--r--source/spirv_constant.h1
-rw-r--r--source/spirv_definition.h2
-rw-r--r--source/spirv_endian.h2
-rw-r--r--source/spirv_target_env.cpp93
-rw-r--r--source/spirv_target_env.h5
-rw-r--r--source/spirv_validator_options.cpp5
-rw-r--r--source/spirv_validator_options.h2
-rw-r--r--source/table.cpp2
-rw-r--r--source/text.cpp6
-rw-r--r--source/text_handler.cpp14
-rw-r--r--source/util/bit_vector.h2
-rw-r--r--source/util/hex_float.h18
-rw-r--r--source/util/ilist.h2
-rw-r--r--source/util/parse_number.h2
-rw-r--r--source/util/small_vector.h7
-rw-r--r--source/util/string_utils.h63
-rw-r--r--source/util/timer.h6
-rw-r--r--source/val/basic_block.h12
-rw-r--r--source/val/construct.cpp3
-rw-r--r--source/val/function.cpp7
-rw-r--r--source/val/function.h10
-rw-r--r--source/val/instruction.cpp10
-rw-r--r--source/val/instruction.h3
-rw-r--r--source/val/validate.cpp13
-rw-r--r--source/val/validate.h2
-rw-r--r--source/val/validate_adjacency.cpp5
-rw-r--r--source/val/validate_annotation.cpp262
-rw-r--r--source/val/validate_arithmetics.cpp2
-rw-r--r--source/val/validate_atomics.cpp9
-rw-r--r--source/val/validate_builtins.cpp19
-rw-r--r--source/val/validate_cfg.cpp29
-rw-r--r--source/val/validate_decorations.cpp115
-rw-r--r--source/val/validate_derivatives.cpp12
-rw-r--r--source/val/validate_extensions.cpp1091
-rw-r--r--source/val/validate_image.cpp56
-rw-r--r--source/val/validate_instruction.cpp11
-rw-r--r--source/val/validate_interfaces.cpp73
-rw-r--r--source/val/validate_layout.cpp79
-rw-r--r--source/val/validate_memory.cpp8
-rw-r--r--source/val/validate_mode_setting.cpp19
-rw-r--r--source/val/validate_scopes.cpp4
-rw-r--r--source/val/validate_type.cpp2
-rw-r--r--source/val/validation_state.cpp171
-rw-r--r--source/val/validation_state.h43
-rw-r--r--source/wasm/README.md43
-rwxr-xr-xsource/wasm/build.sh78
-rw-r--r--source/wasm/package.json17
-rw-r--r--source/wasm/spirv-tools.cpp94
-rw-r--r--source/wasm/spirv-tools.d.ts57
-rw-r--r--test/CMakeLists.txt2
-rw-r--r--test/binary_header_get_test.cpp4
-rw-r--r--test/binary_parse_test.cpp55
-rw-r--r--test/binary_to_text.literal_test.cpp17
-rw-r--r--test/binary_to_text_test.cpp2
-rw-r--r--test/c_interface_test.cpp2
-rw-r--r--test/ext_inst.cldebug100_test.cpp2
-rw-r--r--test/ext_inst.opencl_test.cpp2
-rw-r--r--test/fuzz/CMakeLists.txt1
-rw-r--r--test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp2
-rw-r--r--test/fuzz/fuzzer_pass_construct_composites_test.cpp4
-rw-r--r--test/fuzz/fuzzer_pass_donate_modules_test.cpp40
-rw-r--r--test/fuzz/fuzzer_pass_outline_functions_test.cpp8
-rw-r--r--test/fuzz/fuzzer_pass_test.cpp2
-rw-r--r--test/fuzz/fuzzer_replayer_test.cpp26
-rw-r--r--test/fuzz/fuzzer_shrinker_test.cpp2
-rw-r--r--test/fuzz/fuzzerutil_test.cpp271
-rw-r--r--test/fuzz/shrinker_test.cpp4
-rw-r--r--test/fuzz/transformation_access_chain_test.cpp13
-rw-r--r--test/fuzz/transformation_add_copy_memory_test.cpp10
-rw-r--r--test/fuzz/transformation_add_parameter_test.cpp2
-rw-r--r--test/fuzz/transformation_add_synonym_test.cpp10
-rw-r--r--test/fuzz/transformation_add_type_int_test.cpp2
-rw-r--r--test/fuzz/transformation_adjust_branch_weights_test.cpp2
-rw-r--r--test/fuzz/transformation_load_test.cpp375
-rw-r--r--test/fuzz/transformation_mutate_pointer_test.cpp8
-rw-r--r--test/fuzz/transformation_push_id_through_variable_test.cpp10
-rw-r--r--test/fuzz/transformation_replace_id_with_synonym_test.cpp377
-rw-r--r--test/fuzz/transformation_store_test.cpp307
-rw-r--r--test/fuzz/transformation_swap_two_functions_test.cpp13
-rw-r--r--test/fuzz/transformation_wrap_vector_synonym_test.cpp1553
-rw-r--r--test/fuzzers/BUILD.gn4
-rw-r--r--test/fuzzers/CMakeLists.txt56
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_000.spvbin0 -> 7968 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_001.spvbin0 -> 2452 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_002.spvbin0 -> 2060 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_003.spvbin0 -> 13336 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_004.spvbin0 -> 12368 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_005.spvbin0 -> 2416 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_006.spvbin0 -> 2728 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_007.spvbin0 -> 2668 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_008.spvbin0 -> 22972 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_009.spvbin0 -> 3728 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_010.spvbin0 -> 3280 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_011.spvbin0 -> 5440 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_012.spvbin0 -> 3692 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_013.spvbin0 -> 2860 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_014.spvbin0 -> 1772 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_015.spvbin0 -> 7488 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_016.spvbin0 -> 4044 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_017.spvbin0 -> 14360 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_018.spvbin0 -> 16524 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_019.spvbin0 -> 10552 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_020.spvbin0 -> 9800 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_021.spvbin0 -> 1936 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_022.spvbin0 -> 9436 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_023.spvbin0 -> 1940 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_024.spvbin0 -> 9256 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_025.spvbin0 -> 7960 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_026.spvbin0 -> 9680 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_027.spvbin0 -> 9432 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_028.spvbin0 -> 4092 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_029.spvbin0 -> 2404 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_030.spvbin0 -> 2568 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_031.spvbin0 -> 6084 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_032.spvbin0 -> 11380 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_033.spvbin0 -> 7180 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_034.spvbin0 -> 1356 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_035.spvbin0 -> 648 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_036.spvbin0 -> 2872 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_037.spvbin0 -> 6256 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_038.spvbin0 -> 3648 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_039.spvbin0 -> 3428 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_040.spvbin0 -> 3284 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_041.spvbin0 -> 3360 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_042.spvbin0 -> 6456 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_043.spvbin0 -> 2084 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_044.spvbin0 -> 2596 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_045.spvbin0 -> 4680 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_046.spvbin0 -> 5816 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_047.spvbin0 -> 3216 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_048.spvbin0 -> 4644 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_049.spvbin0 -> 7436 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_050.spvbin0 -> 4156 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_051.spvbin0 -> 3200 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_052.spvbin0 -> 5572 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_053.spvbin0 -> 3936 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_054.spvbin0 -> 3024 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_055.spvbin0 -> 1220 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_056.spvbin0 -> 7660 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_057.spvbin0 -> 9044 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_058.spvbin0 -> 3172 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_059.spvbin0 -> 5940 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_060.spvbin0 -> 12388 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_061.spvbin0 -> 9564 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_062.spvbin0 -> 1824 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_063.spvbin0 -> 13152 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_064.spvbin0 -> 6284 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_065.spvbin0 -> 7184 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_066.spvbin0 -> 3796 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_067.spvbin0 -> 3716 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_068.spvbin0 -> 1956 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_069.spvbin0 -> 3740 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_070.spvbin0 -> 6024 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_071.spvbin0 -> 964 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_072.spvbin0 -> 20916 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_073.spvbin0 -> 10108 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_074.spvbin0 -> 5476 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_075.spvbin0 -> 1264 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_076.spvbin0 -> 3772 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_077.spvbin0 -> 4276 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_078.spvbin0 -> 7136 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_079.spvbin0 -> 5548 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_080.spvbin0 -> 5548 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_081.spvbin0 -> 3216 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_082.spvbin0 -> 16372 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_083.spvbin0 -> 4328 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_084.spvbin0 -> 4180 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_085.spvbin0 -> 2132 bytes
-rw-r--r--test/fuzzers/corpora/spv/graphicsfuzz_086.spvbin0 -> 2476 bytes
-rw-r--r--test/fuzzers/random_generator.cpp135
-rw-r--r--test/fuzzers/random_generator.h68
-rw-r--r--test/fuzzers/spvtools_as_fuzzer.cpp30
-rw-r--r--test/fuzzers/spvtools_binary_parser_fuzzer.cpp16
-rw-r--r--test/fuzzers/spvtools_dis_fuzzer.cpp18
-rw-r--r--test/fuzzers/spvtools_fuzz_fuzzer.cpp80
-rw-r--r--test/fuzzers/spvtools_opt_fuzzer_common.cpp84
-rw-r--r--test/fuzzers/spvtools_opt_fuzzer_common.h35
-rw-r--r--test/fuzzers/spvtools_opt_legalization_fuzzer.cpp27
-rw-r--r--test/fuzzers/spvtools_opt_performance_fuzzer.cpp27
-rw-r--r--test/fuzzers/spvtools_opt_size_fuzzer.cpp27
-rw-r--r--test/fuzzers/spvtools_val_fuzzer.cpp8
-rw-r--r--test/hex_float_test.cpp70
-rw-r--r--test/link/binary_version_test.cpp67
-rw-r--r--test/link/entry_points_test.cpp18
-rw-r--r--test/link/global_values_amount_test.cpp192
-rw-r--r--test/link/ids_limit_test.cpp139
-rw-r--r--test/link/matching_imports_to_exports_test.cpp139
-rw-r--r--test/link/memory_model_test.cpp39
-rw-r--r--test/link/partial_linkage_test.cpp18
-rw-r--r--test/link/type_match_test.cpp1
-rw-r--r--test/link/unique_ids_test.cpp3
-rw-r--r--test/lint/CMakeLists.txt18
-rw-r--r--test/lint/divergence_analysis_test.cpp700
-rw-r--r--test/operand_capabilities_test.cpp13
-rw-r--r--test/opt/CMakeLists.txt8
-rw-r--r--test/opt/aggressive_dead_code_elim_test.cpp1084
-rw-r--r--test/opt/assembly_builder.h4
-rw-r--r--test/opt/block_merge_test.cpp165
-rw-r--r--test/opt/ccp_test.cpp106
-rw-r--r--test/opt/code_sink_test.cpp6
-rw-r--r--test/opt/combine_access_chains_test.cpp26
-rw-r--r--test/opt/control_dependence.cpp306
-rw-r--r--test/opt/convert_relaxed_to_half_test.cpp173
-rw-r--r--test/opt/convert_to_sampled_image_test.cpp353
-rw-r--r--test/opt/copy_prop_array_test.cpp25
-rw-r--r--test/opt/dataflow.cpp225
-rw-r--r--test/opt/dead_branch_elim_test.cpp67
-rw-r--r--test/opt/dead_insert_elim_test.cpp66
-rw-r--r--test/opt/debug_info_manager_test.cpp116
-rw-r--r--test/opt/decoration_manager_test.cpp4
-rw-r--r--test/opt/def_use_test.cpp9
-rw-r--r--test/opt/desc_sroa_test.cpp150
-rw-r--r--test/opt/dominator_tree/generated.cpp2
-rw-r--r--test/opt/eliminate_dead_const_test.cpp2
-rw-r--r--test/opt/eliminate_dead_member_test.cpp62
-rw-r--r--test/opt/flatten_decoration_test.cpp2
-rw-r--r--test/opt/fold_test.cpp403
-rw-r--r--test/opt/graphics_robust_access_test.cpp2
-rw-r--r--test/opt/inline_opaque_test.cpp109
-rw-r--r--test/opt/inline_test.cpp400
-rw-r--r--test/opt/inst_bindless_check_test.cpp4
-rw-r--r--test/opt/inst_buff_addr_check_test.cpp206
-rw-r--r--test/opt/inst_debug_printf_test.cpp4
-rw-r--r--test/opt/instruction_test.cpp6
-rw-r--r--test/opt/ir_loader_test.cpp16
-rw-r--r--test/opt/local_single_block_elim.cpp50
-rw-r--r--test/opt/local_single_store_elim_test.cpp85
-rw-r--r--test/opt/local_ssa_elim_test.cpp160
-rw-r--r--test/opt/loop_optimizations/loop_descriptions.cpp2
-rw-r--r--test/opt/loop_optimizations/loop_fission.cpp2
-rw-r--r--test/opt/loop_optimizations/unroll_assumptions.cpp31
-rw-r--r--test/opt/loop_optimizations/unroll_simple.cpp183
-rw-r--r--test/opt/module_test.cpp2
-rw-r--r--test/opt/optimizer_test.cpp2
-rw-r--r--test/opt/pass_manager_test.cpp2
-rw-r--r--test/opt/pass_merge_return_test.cpp33
-rw-r--r--test/opt/reduce_load_size_test.cpp89
-rw-r--r--test/opt/redundancy_elimination_test.cpp28
-rw-r--r--test/opt/relax_float_ops_test.cpp80
-rw-r--r--test/opt/remove_unused_interface_variables_test.cpp185
-rw-r--r--test/opt/replace_desc_array_access_using_var_index_test.cpp411
-rw-r--r--test/opt/scalar_replacement_test.cpp40
-rw-r--r--test/opt/set_spec_const_default_value_test.cpp94
-rw-r--r--test/opt/simplification_test.cpp25
-rw-r--r--test/opt/spread_volatile_semantics_test.cpp1118
-rw-r--r--test/opt/strip_nonsemantic_info_test.cpp (renamed from test/opt/strip_reflect_info_test.cpp)99
-rw-r--r--test/opt/unify_const_test.cpp6
-rw-r--r--test/opt/upgrade_memory_model_test.cpp2
-rw-r--r--test/opt/vector_dce_test.cpp66
-rw-r--r--test/reduce/CMakeLists.txt5
-rw-r--r--test/reduce/merge_blocks_test.cpp58
-rw-r--r--test/reduce/reduce_test_util.cpp28
-rw-r--r--test/reduce/structured_construct_to_block_test.cpp245
-rw-r--r--test/target_env_test.cpp18
-rw-r--r--test/test_fixture.h19
-rw-r--r--test/text_to_binary.control_flow_test.cpp2
-rw-r--r--test/text_to_binary.extension_test.cpp54
-rw-r--r--test/text_to_binary_test.cpp8
-rwxr-xr-xtest/tools/expect.py15
-rw-r--r--test/tools/opt/flags.py14
-rw-r--r--test/val/CMakeLists.txt3
-rw-r--r--test/val/val_annotation_test.cpp951
-rw-r--r--test/val/val_arithmetics_test.cpp53
-rw-r--r--test/val/val_atomics_test.cpp60
-rw-r--r--test/val/val_builtins_test.cpp144
-rw-r--r--test/val/val_capability_test.cpp466
-rw-r--r--test/val/val_cfg_test.cpp235
-rw-r--r--test/val/val_composites_test.cpp23
-rw-r--r--test/val/val_decoration_test.cpp602
-rw-r--r--test/val/val_derivatives_test.cpp25
-rw-r--r--test/val/val_ext_inst_debug_test.cpp5313
-rw-r--r--test/val/val_ext_inst_test.cpp2853
-rw-r--r--test/val/val_extension_spv_khr_bit_instructions.cpp117
-rw-r--r--test/val/val_extension_spv_khr_terminate_invocation.cpp29
-rw-r--r--test/val/val_id_test.cpp18
-rw-r--r--test/val/val_image_test.cpp114
-rw-r--r--test/val/val_interfaces_test.cpp134
-rw-r--r--test/val/val_layout_test.cpp2
-rw-r--r--test/val/val_limits_test.cpp2
-rw-r--r--test/val/val_literals_test.cpp2
-rw-r--r--test/val/val_memory_test.cpp127
-rw-r--r--test/val/val_modes_test.cpp73
-rw-r--r--test/val/val_non_semantic_test.cpp22
-rw-r--r--test/val/val_storage_test.cpp2
-rw-r--r--test/val/val_version_test.cpp166
-rw-r--r--test/wasm/test.js64
-rw-r--r--tools/CMakeLists.txt12
-rw-r--r--tools/as/as.cpp2
-rw-r--r--tools/cfg/bin_to_dot.cpp4
-rw-r--r--tools/cfg/bin_to_dot.h2
-rw-r--r--tools/cfg/cfg.cpp12
-rw-r--r--tools/fuzz/fuzz.cpp24
-rw-r--r--tools/io.h4
-rw-r--r--tools/link/linker.cpp18
-rw-r--r--tools/lint/lint.cpp75
-rw-r--r--tools/opt/opt.cpp50
-rw-r--r--tools/reduce/reduce.cpp2
-rw-r--r--tools/sva/README.md2
-rw-r--r--tools/sva/yarn.lock6
-rw-r--r--tools/val/val.cpp13
-rwxr-xr-xutils/check_copyright.py5
-rwxr-xr-xutils/generate_grammar_tables.py2
-rwxr-xr-xutils/git-sync-deps2
-rwxr-xr-xutils/roll_deps.sh13
-rw-r--r--utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go2
-rw-r--r--utils/vscode/src/lsp/jsonrpc2/wire.go2
-rw-r--r--utils/vscode/src/lsp/protocol/tsprotocol.go22
-rw-r--r--utils/vscode/src/parser/parser.go2
683 files changed, 31147 insertions, 7545 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
deleted file mode 100644
index 0a4cca05..00000000
--- a/.appveyor.yml
+++ /dev/null
@@ -1,90 +0,0 @@
-# Windows Build Configuration for AppVeyor
-# http://www.appveyor.com/docs/appveyor-yml
-
-# version format
-version: "{build}"
-
-# The most recent compiler gives the most interesting new results.
-# Put it first so we get its feedback first.
-os:
- - Visual Studio 2017
- #- Visual Studio 2013
-
-platform:
- - x64
-
-configuration:
- - Debug
- #- Release
-
-branches:
- only:
- - master
-
-# Travis advances the master-tot tag to current top of the tree after
-# each push into the master branch, because it relies on that tag to
-# upload build artifacts to the master-tot release. This will cause
-# double testing for each push on Appveyor: one for the push, one for
-# the tag advance. Disable testing tags.
-skip_tags: true
-
-clone_depth: 1
-
-matrix:
- fast_finish: true # Show final status immediately if a test fails.
- #exclude:
- # - os: Visual Studio 2013
- # configuration: Debug
-
-# scripts that run after cloning repository
-install:
- # Install ninja
- - set NINJA_URL="https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-win.zip"
- - appveyor DownloadFile %NINJA_URL% -FileName ninja.zip
- - 7z x ninja.zip -oC:\ninja > nul
- - set PATH=C:\ninja;C:\Python36;%PATH%
-
-before_build:
- - git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers
- - git clone https://github.com/google/googletest.git external/googletest
- - cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd ..
- - git clone --depth=1 https://github.com/google/effcee.git external/effcee
- - git clone --depth=1 https://github.com/google/re2.git external/re2
- # Set path and environment variables for the current Visual Studio version
- - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" (call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64)
- - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86_amd64)
-
-build:
- parallel: true # enable MSBuild parallel builds
- verbosity: minimal
-
-build_script:
- - mkdir build && cd build
- - cmake -GNinja -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DCMAKE_INSTALL_PREFIX=install -DRE2_BUILD_TESTING=OFF ..
- - ninja install
-
-test_script:
- - ctest -C %CONFIGURATION% --output-on-failure --timeout 310
-
-after_test:
- # Zip build artifacts for uploading and deploying
- - cd install
- - 7z a SPIRV-Tools-master-windows-"%PLATFORM%"-"%CONFIGURATION%".zip *\*
-
-artifacts:
- - path: build\install\*.zip
- name: artifacts-zip
-
-deploy:
- - provider: GitHub
- auth_token:
- secure: TMfcScKzzFIm1YgeV/PwCRXFDCw8Xm0wY2Vb2FU6WKlbzb5eUITTpr6I5vHPnAxS
- release: master-tot
- description: "Continuous build of the latest master branch by Appveyor and Travis CI"
- artifact: artifacts-zip
- draft: false
- prerelease: false
- force_update: true
- on:
- branch: master
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml
new file mode 100644
index 00000000..d9a9c5cb
--- /dev/null
+++ b/.github/workflows/wasm.yml
@@ -0,0 +1,14 @@
+name: Wasm Build
+
+on: [ push, pull_request ]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Build web
+ run: docker-compose up
+ - name: Run tests
+ run: node test/wasm/test.js
diff --git a/Android.bp b/Android.bp
index 07bd32b1..ca6b9f59 100644
--- a/Android.bp
+++ b/Android.bp
@@ -39,6 +39,7 @@ genrule {
"extension_enum.inc",
"glsl.std.450.insts.inc",
"nonsemantic.clspvreflection.insts.inc",
+ "nonsemantic.shader.debuginfo.100.insts.inc",
"opencl.debuginfo.100.insts.inc",
"opencl.std.insts.inc",
"operand.kinds-unified1.inc",
@@ -51,6 +52,7 @@ genrule {
":deqp_spirv_headers_unified1_extinst.debuginfo.grammar.json",
":deqp_spirv_headers_unified1_extinst.glsl.std.450.grammar.json",
":deqp_spirv_headers_unified1_extinst.nonsemantic.clspvreflection.grammar.json",
+ ":deqp_spirv_headers_unified1_extinst.nonsemantic.shader.debuginfo.100.grammar.json",
":deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json",
":deqp_spirv_headers_unified1_extinst.opencl.std.100.grammar.json",
":deqp_spirv_headers_unified1_extinst.spv-amd-gcn-shader.grammar.json",
@@ -61,33 +63,36 @@ genrule {
],
tool_files: ["utils/generate_grammar_tables.py"],
cmd:
- "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json) --vendor-insts-output=$(location spv-amd-shader-explicit-vertex-parameter.insts.inc) --vendor-operand-kind-prefix=; "+
- "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-gcn-shader.grammar.json) --vendor-insts-output=$(location spv-amd-gcn-shader.insts.inc) --vendor-operand-kind-prefix=; "+
- "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-shader-trinary-minmax.grammar.json) --vendor-insts-output=$(location spv-amd-shader-trinary-minmax.insts.inc) --vendor-operand-kind-prefix=; "+
+ "$(location) --extinst-glsl-grammar=$(location :deqp_spirv_headers_unified1_extinst.glsl.std.450.grammar.json) --glsl-insts-output=$(location glsl.std.450.insts.inc); "+
+ "$(location) --extinst-opencl-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.std.100.grammar.json) --opencl-insts-output=$(location opencl.std.insts.inc); "+
"$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.debuginfo.grammar.json) --vendor-insts-output=$(location debuginfo.insts.inc) --vendor-operand-kind-prefix=; "+
- "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-shader-ballot.grammar.json) --vendor-insts-output=$(location spv-amd-shader-ballot.insts.inc) --vendor-operand-kind-prefix=; "+
- "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json) --vendor-insts-output=$(location opencl.debuginfo.100.insts.inc) --vendor-operand-kind-prefix=CLDEBUG100_; "+
"$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.nonsemantic.clspvreflection.grammar.json) --vendor-insts-output=$(location nonsemantic.clspvreflection.insts.inc) --vendor-operand-kind-prefix=; "+
- "$(location) --extinst-opencl-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.std.100.grammar.json) --opencl-insts-output=$(location opencl.std.insts.inc); "+
- "$(location) --extinst-glsl-grammar=$(location :deqp_spirv_headers_unified1_extinst.glsl.std.450.grammar.json) --glsl-insts-output=$(location glsl.std.450.insts.inc); "+
+ "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.nonsemantic.shader.debuginfo.100.grammar.json) --vendor-insts-output=$(location nonsemantic.shader.debuginfo.100.insts.inc) --vendor-operand-kind-prefix=SHDEBUG100_; "+
+ "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json) --vendor-insts-output=$(location opencl.debuginfo.100.insts.inc) --vendor-operand-kind-prefix=CLDEBUG100_; "+
+ "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-gcn-shader.grammar.json) --vendor-insts-output=$(location spv-amd-gcn-shader.insts.inc) --vendor-operand-kind-prefix=; "+
+ "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-shader-ballot.grammar.json) --vendor-insts-output=$(location spv-amd-shader-ballot.insts.inc) --vendor-operand-kind-prefix=; "+
+ "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json) --vendor-insts-output=$(location spv-amd-shader-explicit-vertex-parameter.insts.inc) --vendor-operand-kind-prefix=; "+
+ "$(location) --extinst-vendor-grammar=$(location :deqp_spirv_headers_unified1_extinst.spv-amd-shader-trinary-minmax.grammar.json) --vendor-insts-output=$(location spv-amd-shader-trinary-minmax.insts.inc) --vendor-operand-kind-prefix=; "+
"$(location) --spirv-core-grammar=$(location :deqp_spirv_headers_unified1_spirv.core.grammar.json) --extinst-debuginfo-grammar=$(location :deqp_spirv_headers_unified1_extinst.debuginfo.grammar.json) --extinst-cldebuginfo100-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json) --core-insts-output=$(location core.insts-unified1.inc) --operand-kinds-output=$(location operand.kinds-unified1.inc); "+
"$(location) --spirv-core-grammar=$(location :deqp_spirv_headers_unified1_spirv.core.grammar.json) --extinst-debuginfo-grammar=$(location :deqp_spirv_headers_unified1_extinst.debuginfo.grammar.json) --extinst-cldebuginfo100-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json) --extension-enum-output=$(location extension_enum.inc) --enum-string-mapping-output=$(location enum_string_mapping.inc); "
}
-
genrule {
name: "deqp_spvtools_generate_language_headers",
out: [
"DebugInfo.h",
+ "NonSemanticShaderDebugInfo100.h",
"OpenCLDebugInfo100.h",
],
srcs: [
":deqp_spirv_headers_unified1_extinst.debuginfo.grammar.json",
+ ":deqp_spirv_headers_unified1_extinst.nonsemantic.shader.debuginfo.100.grammar.json",
":deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json",
],
tool_files: ["utils/generate_language_headers.py"],
- cmd:
+ cmd:
"$(location) --extinst-grammar=$(location :deqp_spirv_headers_unified1_extinst.debuginfo.grammar.json) --extinst-output-path=$(location DebugInfo.h); "+
+ "$(location) --extinst-grammar=$(location :deqp_spirv_headers_unified1_extinst.nonsemantic.shader.debuginfo.100.grammar.json) --extinst-output-path=$(location NonSemanticShaderDebugInfo100.h); "+
"$(location) --extinst-grammar=$(location :deqp_spirv_headers_unified1_extinst.opencl.debuginfo.100.grammar.json) --extinst-output-path=$(location OpenCLDebugInfo100.h); "
}
diff --git a/BUILD.bazel b/BUILD.bazel
index 52290cfb..b2031ded 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -3,6 +3,7 @@ load(
"COMMON_COPTS",
"DEBUGINFO_GRAMMAR_JSON_FILE",
"CLDEBUGINFO100_GRAMMAR_JSON_FILE",
+ "SHDEBUGINFO100_GRAMMAR_JSON_FILE",
"TEST_COPTS",
"base_test",
"generate_core_tables",
@@ -12,6 +13,7 @@ load(
"generate_opencl_tables",
"generate_vendor_tables",
"link_test",
+ "lint_test",
"opt_test",
"reduce_test",
"util_test",
@@ -59,12 +61,16 @@ generate_vendor_tables("debuginfo")
generate_vendor_tables("opencl.debuginfo.100", "CLDEBUG100_")
+generate_vendor_tables("nonsemantic.shader.debuginfo.100", "SHDEBUG100_")
+
generate_vendor_tables("nonsemantic.clspvreflection")
generate_extinst_lang_headers("DebugInfo", DEBUGINFO_GRAMMAR_JSON_FILE)
generate_extinst_lang_headers("OpenCLDebugInfo100", CLDEBUGINFO100_GRAMMAR_JSON_FILE)
+generate_extinst_lang_headers("NonSemanticShaderDebugInfo100", SHDEBUGINFO100_GRAMMAR_JSON_FILE)
+
py_binary(
name = "generate_registry_tables",
srcs = ["utils/generate_registry_tables.py"],
@@ -101,12 +107,14 @@ cc_library(
":gen_enum_string_mapping",
":gen_extinst_lang_headers_DebugInfo",
":gen_extinst_lang_headers_OpenCLDebugInfo100",
+ ":gen_extinst_lang_headers_NonSemanticShaderDebugInfo100",
":gen_glsl_tables_unified1",
":gen_opencl_tables_unified1",
":gen_registry_tables",
":gen_vendor_tables_debuginfo",
":gen_vendor_tables_nonsemantic_clspvreflection",
":gen_vendor_tables_opencl_debuginfo_100",
+ ":gen_vendor_tables_nonsemantic_shader_debuginfo_100",
":gen_vendor_tables_spv_amd_gcn_shader",
":gen_vendor_tables_spv_amd_shader_ballot",
":gen_vendor_tables_spv_amd_shader_explicit_vertex_parameter",
@@ -226,6 +234,19 @@ cc_library(
)
cc_library(
+ name = "spirv_tools_lint",
+ srcs = glob(["source/lint/*.cpp", "source/lint/*.h"]),
+ hdrs = ["include/spirv-tools/linter.hpp"],
+ copts = COMMON_COPTS,
+ linkstatic = 1,
+ visibility = ["//visibility:public"],
+ deps = [
+ ":spirv_tools",
+ ":spirv_tools_opt",
+ ],
+)
+
+cc_library(
name = "tools_util",
srcs = glob(["tools/util/*.cpp"]),
hdrs = glob(["tools/util/*.h"]),
@@ -323,6 +344,21 @@ cc_binary(
)
cc_binary(
+ name = "spirv-lint",
+ srcs = [
+ "tools/io.h",
+ "tools/lint/lint.cpp",
+ ],
+ copts = COMMON_COPTS,
+ visibility = ["//visibility:public"],
+ deps = [
+ ":spirv_tools",
+ ":spirv_tools_lint",
+ ":tools_util",
+ ],
+)
+
+cc_binary(
name = "spirv-cfg",
srcs = [
"tools/cfg/bin_to_dot.cpp",
@@ -465,6 +501,13 @@ base_test(
["test/link/*.cpp"],
)]
+[lint_test(
+ name = f[10:-4], # strip test/lint/, .cpp
+ srcs = [f],
+) for f in glob(
+ ["test/lint/*.cpp"],
+)]
+
[opt_test(
name = f[9:-4], # strip test/opt/, .cpp
srcs = [f],
diff --git a/BUILD.gn b/BUILD.gn
index 19ee77ac..4d154caf 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -16,6 +16,7 @@ import("//build_overrides/build.gni")
import("//build_overrides/spirv_tools.gni")
if (build_with_chromium) {
import("//testing/test.gni")
+ import("//third_party/protobuf/proto_library.gni")
}
spirv_headers = spirv_tools_spirv_headers_dir
@@ -275,6 +276,10 @@ spvtools_language_header("cldebuginfo100") {
name = "OpenCLDebugInfo100"
grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json"
}
+spvtools_language_header("vkdebuginfo100") {
+ name = "NonSemanticShaderDebugInfo100"
+ grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json"
+}
spvtools_vendor_tables = [
[
@@ -305,6 +310,10 @@ spvtools_vendor_tables = [
"nonsemantic.clspvreflection",
"...nil...",
],
+ [
+ "nonsemantic.shader.debuginfo.100",
+ "SHDEBUG100_",
+ ],
]
foreach(table_def, spvtools_vendor_tables) {
@@ -318,20 +327,28 @@ config("spvtools_public_config") {
include_dirs = [ "include" ]
}
+config("spvtools_include_gen_dirs") {
+ include_dirs = [ "$target_gen_dir" ]
+}
+
config("spvtools_internal_config") {
include_dirs = [
".",
- "$target_gen_dir",
"${spirv_headers}/include",
]
- configs = [ ":spvtools_public_config" ]
+ configs = [
+ ":spvtools_public_config",
+ ":spvtools_include_gen_dirs",
+ ]
cflags = []
if (is_clang) {
cflags += [
"-Wno-implicit-fallthrough",
"-Wno-newline-eof",
+ "-Wno-unreachable-code-break",
+ "-Wno-unreachable-code-return",
]
} else if (!is_win) {
# Work around a false-positive on a Skia GCC 10 builder.
@@ -351,6 +368,14 @@ source_set("spvtools_headers") {
public_configs = [ ":spvtools_public_config" ]
}
+group("spvtools_language_headers") {
+ public_deps = [
+ ":spvtools_language_header_cldebuginfo100",
+ ":spvtools_language_header_debuginfo",
+ ":spvtools_language_header_vkdebuginfo100",
+ ]
+}
+
static_library("spvtools") {
deps = [
":spvtools_core_tables_unified1",
@@ -358,6 +383,7 @@ static_library("spvtools") {
":spvtools_glsl_tables_glsl1-0",
":spvtools_language_header_cldebuginfo100",
":spvtools_language_header_debuginfo",
+ ":spvtools_language_header_vkdebuginfo100",
":spvtools_opencl_tables_opencl1-0",
]
foreach(table_def, spvtools_vendor_tables) {
@@ -371,6 +397,7 @@ static_library("spvtools") {
"source/binary.cpp",
"source/binary.h",
"source/cfa.h",
+ "source/common_debug_info.h",
"source/diagnostic.cpp",
"source/diagnostic.h",
"source/disassemble.cpp",
@@ -402,8 +429,12 @@ static_library("spvtools") {
"source/spirv_definition.h",
"source/spirv_endian.cpp",
"source/spirv_endian.h",
+ "source/spirv_fuzzer_options.cpp",
+ "source/spirv_fuzzer_options.h",
"source/spirv_optimizer_options.cpp",
"source/spirv_optimizer_options.h",
+ "source/spirv_reducer_options.cpp",
+ "source/spirv_reducer_options.h",
"source/spirv_target_env.cpp",
"source/spirv_target_env.h",
"source/spirv_validator_options.cpp",
@@ -499,6 +530,7 @@ static_library("spvtools_val") {
":spvtools",
":spvtools_language_header_cldebuginfo100",
":spvtools_language_header_debuginfo",
+ ":spvtools_language_header_vkdebuginfo100",
]
public_deps = [ ":spvtools_headers" ]
@@ -541,10 +573,16 @@ static_library("spvtools_opt") {
"source/opt/const_folding_rules.h",
"source/opt/constants.cpp",
"source/opt/constants.h",
+ "source/opt/control_dependence.cpp",
+ "source/opt/control_dependence.h",
"source/opt/convert_to_half_pass.cpp",
"source/opt/convert_to_half_pass.h",
+ "source/opt/convert_to_sampled_image_pass.cpp",
+ "source/opt/convert_to_sampled_image_pass.h",
"source/opt/copy_prop_arrays.cpp",
"source/opt/copy_prop_arrays.h",
+ "source/opt/dataflow.cpp",
+ "source/opt/dataflow.h",
"source/opt/dead_branch_elim_pass.cpp",
"source/opt/dead_branch_elim_pass.h",
"source/opt/dead_insert_elim_pass.cpp",
@@ -559,6 +597,8 @@ static_library("spvtools_opt") {
"source/opt/def_use_manager.h",
"source/opt/desc_sroa.cpp",
"source/opt/desc_sroa.h",
+ "source/opt/desc_sroa_util.cpp",
+ "source/opt/desc_sroa_util.h",
"source/opt/dominator_analysis.cpp",
"source/opt/dominator_analysis.h",
"source/opt/dominator_tree.cpp",
@@ -676,6 +716,10 @@ static_library("spvtools_opt") {
"source/opt/relax_float_ops_pass.h",
"source/opt/remove_duplicates_pass.cpp",
"source/opt/remove_duplicates_pass.h",
+ "source/opt/remove_unused_interface_variables_pass.cpp",
+ "source/opt/remove_unused_interface_variables_pass.h",
+ "source/opt/replace_desc_array_access_using_var_index.cpp",
+ "source/opt/replace_desc_array_access_using_var_index.h",
"source/opt/replace_invalid_opc.cpp",
"source/opt/replace_invalid_opc.h",
"source/opt/scalar_analysis.cpp",
@@ -688,14 +732,16 @@ static_library("spvtools_opt") {
"source/opt/set_spec_constant_default_value_pass.h",
"source/opt/simplification_pass.cpp",
"source/opt/simplification_pass.h",
+ "source/opt/spread_volatile_semantics.cpp",
+ "source/opt/spread_volatile_semantics.h",
"source/opt/ssa_rewrite_pass.cpp",
"source/opt/ssa_rewrite_pass.h",
"source/opt/strength_reduction_pass.cpp",
"source/opt/strength_reduction_pass.h",
"source/opt/strip_debug_info_pass.cpp",
"source/opt/strip_debug_info_pass.h",
- "source/opt/strip_reflect_info_pass.cpp",
- "source/opt/strip_reflect_info_pass.h",
+ "source/opt/strip_nonsemantic_info_pass.cpp",
+ "source/opt/strip_nonsemantic_info_pass.h",
"source/opt/struct_cfg_analysis.cpp",
"source/opt/struct_cfg_analysis.h",
"source/opt/tree_iterator.h",
@@ -719,11 +765,14 @@ static_library("spvtools_opt") {
deps = [
":spvtools",
- ":spvtools_language_header_cldebuginfo100",
":spvtools_language_header_debuginfo",
":spvtools_vendor_tables_spv-amd-shader-ballot",
]
- public_deps = [ ":spvtools_headers" ]
+ public_deps = [
+ ":spvtools_headers",
+ ":spvtools_language_header_cldebuginfo100",
+ ":spvtools_language_header_vkdebuginfo100",
+ ]
if (build_with_chromium) {
configs -= [ "//build/config/compiler:chromium_code" ]
@@ -801,12 +850,14 @@ static_library("spvtools_reduce") {
"source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h",
"source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp",
"source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h",
+ "source/reduce/structured_construct_to_block_reduction_opportunity.cpp",
+ "source/reduce/structured_construct_to_block_reduction_opportunity.h",
+ "source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp",
+ "source/reduce/structured_construct_to_block_reduction_opportunity_finder.h",
"source/reduce/structured_loop_to_selection_reduction_opportunity.cpp",
"source/reduce/structured_loop_to_selection_reduction_opportunity.h",
"source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp",
"source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h",
- "source/spirv_reducer_options.cpp",
- "source/spirv_reducer_options.h",
]
deps = [
":spvtools",
@@ -820,6 +871,423 @@ static_library("spvtools_reduce") {
configs += [ ":spvtools_internal_config" ]
}
+if (build_with_chromium) {
+ # The spirv-fuzz library is only built when in a Chromium checkout
+ # due to its dependency on protobuf.
+
+ proto_library("spvtools_fuzz_proto") {
+ sources = [ "source/fuzz/protobufs/spvtoolsfuzz.proto" ]
+ generate_python = false
+ use_protobuf_full = true
+ }
+
+ static_library("spvtools_fuzz") {
+ sources = [
+ "source/fuzz/added_function_reducer.cpp",
+ "source/fuzz/added_function_reducer.h",
+ "source/fuzz/available_instructions.cpp",
+ "source/fuzz/available_instructions.h",
+ "source/fuzz/call_graph.cpp",
+ "source/fuzz/call_graph.h",
+ "source/fuzz/comparator_deep_blocks_first.h",
+ "source/fuzz/counter_overflow_id_source.cpp",
+ "source/fuzz/counter_overflow_id_source.h",
+ "source/fuzz/data_descriptor.cpp",
+ "source/fuzz/data_descriptor.h",
+ "source/fuzz/equivalence_relation.h",
+ "source/fuzz/fact_manager/constant_uniform_facts.cpp",
+ "source/fuzz/fact_manager/constant_uniform_facts.h",
+ "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp",
+ "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h",
+ "source/fuzz/fact_manager/dead_block_facts.cpp",
+ "source/fuzz/fact_manager/dead_block_facts.h",
+ "source/fuzz/fact_manager/fact_manager.cpp",
+ "source/fuzz/fact_manager/fact_manager.h",
+ "source/fuzz/fact_manager/irrelevant_value_facts.cpp",
+ "source/fuzz/fact_manager/irrelevant_value_facts.h",
+ "source/fuzz/fact_manager/livesafe_function_facts.cpp",
+ "source/fuzz/fact_manager/livesafe_function_facts.h",
+ "source/fuzz/force_render_red.cpp",
+ "source/fuzz/force_render_red.h",
+ "source/fuzz/fuzzer.cpp",
+ "source/fuzz/fuzzer.h",
+ "source/fuzz/fuzzer_context.cpp",
+ "source/fuzz/fuzzer_context.h",
+ "source/fuzz/fuzzer_pass.cpp",
+ "source/fuzz/fuzzer_pass.h",
+ "source/fuzz/fuzzer_pass_add_access_chains.cpp",
+ "source/fuzz/fuzzer_pass_add_access_chains.h",
+ "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp",
+ "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h",
+ "source/fuzz/fuzzer_pass_add_composite_extract.cpp",
+ "source/fuzz/fuzzer_pass_add_composite_extract.h",
+ "source/fuzz/fuzzer_pass_add_composite_inserts.cpp",
+ "source/fuzz/fuzzer_pass_add_composite_inserts.h",
+ "source/fuzz/fuzzer_pass_add_composite_types.cpp",
+ "source/fuzz/fuzzer_pass_add_composite_types.h",
+ "source/fuzz/fuzzer_pass_add_copy_memory.cpp",
+ "source/fuzz/fuzzer_pass_add_copy_memory.h",
+ "source/fuzz/fuzzer_pass_add_dead_blocks.cpp",
+ "source/fuzz/fuzzer_pass_add_dead_blocks.h",
+ "source/fuzz/fuzzer_pass_add_dead_breaks.cpp",
+ "source/fuzz/fuzzer_pass_add_dead_breaks.h",
+ "source/fuzz/fuzzer_pass_add_dead_continues.cpp",
+ "source/fuzz/fuzzer_pass_add_dead_continues.h",
+ "source/fuzz/fuzzer_pass_add_equation_instructions.cpp",
+ "source/fuzz/fuzzer_pass_add_equation_instructions.h",
+ "source/fuzz/fuzzer_pass_add_function_calls.cpp",
+ "source/fuzz/fuzzer_pass_add_function_calls.h",
+ "source/fuzz/fuzzer_pass_add_global_variables.cpp",
+ "source/fuzz/fuzzer_pass_add_global_variables.h",
+ "source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp",
+ "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h",
+ "source/fuzz/fuzzer_pass_add_loads.cpp",
+ "source/fuzz/fuzzer_pass_add_loads.h",
+ "source/fuzz/fuzzer_pass_add_local_variables.cpp",
+ "source/fuzz/fuzzer_pass_add_local_variables.h",
+ "source/fuzz/fuzzer_pass_add_loop_preheaders.cpp",
+ "source/fuzz/fuzzer_pass_add_loop_preheaders.h",
+ "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp",
+ "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h",
+ "source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp",
+ "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h",
+ "source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp",
+ "source/fuzz/fuzzer_pass_add_opphi_synonyms.h",
+ "source/fuzz/fuzzer_pass_add_parameters.cpp",
+ "source/fuzz/fuzzer_pass_add_parameters.h",
+ "source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp",
+ "source/fuzz/fuzzer_pass_add_relaxed_decorations.h",
+ "source/fuzz/fuzzer_pass_add_stores.cpp",
+ "source/fuzz/fuzzer_pass_add_stores.h",
+ "source/fuzz/fuzzer_pass_add_synonyms.cpp",
+ "source/fuzz/fuzzer_pass_add_synonyms.h",
+ "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp",
+ "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h",
+ "source/fuzz/fuzzer_pass_adjust_branch_weights.cpp",
+ "source/fuzz/fuzzer_pass_adjust_branch_weights.h",
+ "source/fuzz/fuzzer_pass_adjust_function_controls.cpp",
+ "source/fuzz/fuzzer_pass_adjust_function_controls.h",
+ "source/fuzz/fuzzer_pass_adjust_loop_controls.cpp",
+ "source/fuzz/fuzzer_pass_adjust_loop_controls.h",
+ "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp",
+ "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h",
+ "source/fuzz/fuzzer_pass_adjust_selection_controls.cpp",
+ "source/fuzz/fuzzer_pass_adjust_selection_controls.h",
+ "source/fuzz/fuzzer_pass_apply_id_synonyms.cpp",
+ "source/fuzz/fuzzer_pass_apply_id_synonyms.h",
+ "source/fuzz/fuzzer_pass_construct_composites.cpp",
+ "source/fuzz/fuzzer_pass_construct_composites.h",
+ "source/fuzz/fuzzer_pass_copy_objects.cpp",
+ "source/fuzz/fuzzer_pass_copy_objects.h",
+ "source/fuzz/fuzzer_pass_donate_modules.cpp",
+ "source/fuzz/fuzzer_pass_donate_modules.h",
+ "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp",
+ "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h",
+ "source/fuzz/fuzzer_pass_expand_vector_reductions.cpp",
+ "source/fuzz/fuzzer_pass_expand_vector_reductions.h",
+ "source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp",
+ "source/fuzz/fuzzer_pass_flatten_conditional_branches.h",
+ "source/fuzz/fuzzer_pass_inline_functions.cpp",
+ "source/fuzz/fuzzer_pass_inline_functions.h",
+ "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp",
+ "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h",
+ "source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp",
+ "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h",
+ "source/fuzz/fuzzer_pass_invert_comparison_operators.cpp",
+ "source/fuzz/fuzzer_pass_invert_comparison_operators.h",
+ "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp",
+ "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h",
+ "source/fuzz/fuzzer_pass_merge_blocks.cpp",
+ "source/fuzz/fuzzer_pass_merge_blocks.h",
+ "source/fuzz/fuzzer_pass_merge_function_returns.cpp",
+ "source/fuzz/fuzzer_pass_merge_function_returns.h",
+ "source/fuzz/fuzzer_pass_mutate_pointers.cpp",
+ "source/fuzz/fuzzer_pass_mutate_pointers.h",
+ "source/fuzz/fuzzer_pass_obfuscate_constants.cpp",
+ "source/fuzz/fuzzer_pass_obfuscate_constants.h",
+ "source/fuzz/fuzzer_pass_outline_functions.cpp",
+ "source/fuzz/fuzzer_pass_outline_functions.h",
+ "source/fuzz/fuzzer_pass_permute_blocks.cpp",
+ "source/fuzz/fuzzer_pass_permute_blocks.h",
+ "source/fuzz/fuzzer_pass_permute_function_parameters.cpp",
+ "source/fuzz/fuzzer_pass_permute_function_parameters.h",
+ "source/fuzz/fuzzer_pass_permute_function_variables.cpp",
+ "source/fuzz/fuzzer_pass_permute_function_variables.h",
+ "source/fuzz/fuzzer_pass_permute_instructions.cpp",
+ "source/fuzz/fuzzer_pass_permute_instructions.h",
+ "source/fuzz/fuzzer_pass_permute_phi_operands.cpp",
+ "source/fuzz/fuzzer_pass_permute_phi_operands.h",
+ "source/fuzz/fuzzer_pass_propagate_instructions_down.cpp",
+ "source/fuzz/fuzzer_pass_propagate_instructions_down.h",
+ "source/fuzz/fuzzer_pass_propagate_instructions_up.cpp",
+ "source/fuzz/fuzzer_pass_propagate_instructions_up.h",
+ "source/fuzz/fuzzer_pass_push_ids_through_variables.cpp",
+ "source/fuzz/fuzzer_pass_push_ids_through_variables.h",
+ "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp",
+ "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h",
+ "source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp",
+ "source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h",
+ "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp",
+ "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h",
+ "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp",
+ "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h",
+ "source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp",
+ "source/fuzz/fuzzer_pass_replace_irrelevant_ids.h",
+ "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp",
+ "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h",
+ "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp",
+ "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h",
+ "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp",
+ "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h",
+ "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp",
+ "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h",
+ "source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp",
+ "source/fuzz/fuzzer_pass_replace_parameter_with_global.h",
+ "source/fuzz/fuzzer_pass_replace_params_with_struct.cpp",
+ "source/fuzz/fuzzer_pass_replace_params_with_struct.h",
+ "source/fuzz/fuzzer_pass_split_blocks.cpp",
+ "source/fuzz/fuzzer_pass_split_blocks.h",
+ "source/fuzz/fuzzer_pass_swap_commutable_operands.cpp",
+ "source/fuzz/fuzzer_pass_swap_commutable_operands.h",
+ "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp",
+ "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h",
+ "source/fuzz/fuzzer_pass_swap_functions.cpp",
+ "source/fuzz/fuzzer_pass_swap_functions.h",
+ "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp",
+ "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h",
+ "source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp",
+ "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h",
+ "source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp",
+ "source/fuzz/fuzzer_pass_wrap_vector_synonym.h",
+ "source/fuzz/fuzzer_util.cpp",
+ "source/fuzz/fuzzer_util.h",
+ "source/fuzz/id_use_descriptor.cpp",
+ "source/fuzz/id_use_descriptor.h",
+ "source/fuzz/instruction_descriptor.cpp",
+ "source/fuzz/instruction_descriptor.h",
+ "source/fuzz/instruction_message.cpp",
+ "source/fuzz/instruction_message.h",
+ "source/fuzz/overflow_id_source.cpp",
+ "source/fuzz/overflow_id_source.h",
+ "source/fuzz/pass_management/repeated_pass_instances.h",
+ "source/fuzz/pass_management/repeated_pass_manager.cpp",
+ "source/fuzz/pass_management/repeated_pass_manager.h",
+ "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.cpp",
+ "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h",
+ "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp",
+ "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h",
+ "source/fuzz/pass_management/repeated_pass_manager_simple.cpp",
+ "source/fuzz/pass_management/repeated_pass_manager_simple.h",
+ "source/fuzz/pass_management/repeated_pass_recommender.cpp",
+ "source/fuzz/pass_management/repeated_pass_recommender.h",
+ "source/fuzz/pass_management/repeated_pass_recommender_standard.cpp",
+ "source/fuzz/pass_management/repeated_pass_recommender_standard.h",
+ "source/fuzz/protobufs/spirvfuzz_protobufs.h",
+ "source/fuzz/pseudo_random_generator.cpp",
+ "source/fuzz/pseudo_random_generator.h",
+ "source/fuzz/random_generator.cpp",
+ "source/fuzz/random_generator.h",
+ "source/fuzz/replayer.cpp",
+ "source/fuzz/replayer.h",
+ "source/fuzz/shrinker.cpp",
+ "source/fuzz/shrinker.h",
+ "source/fuzz/transformation.cpp",
+ "source/fuzz/transformation.h",
+ "source/fuzz/transformation_access_chain.cpp",
+ "source/fuzz/transformation_access_chain.h",
+ "source/fuzz/transformation_add_bit_instruction_synonym.cpp",
+ "source/fuzz/transformation_add_bit_instruction_synonym.h",
+ "source/fuzz/transformation_add_constant_boolean.cpp",
+ "source/fuzz/transformation_add_constant_boolean.h",
+ "source/fuzz/transformation_add_constant_composite.cpp",
+ "source/fuzz/transformation_add_constant_composite.h",
+ "source/fuzz/transformation_add_constant_null.cpp",
+ "source/fuzz/transformation_add_constant_null.h",
+ "source/fuzz/transformation_add_constant_scalar.cpp",
+ "source/fuzz/transformation_add_constant_scalar.h",
+ "source/fuzz/transformation_add_copy_memory.cpp",
+ "source/fuzz/transformation_add_copy_memory.h",
+ "source/fuzz/transformation_add_dead_block.cpp",
+ "source/fuzz/transformation_add_dead_block.h",
+ "source/fuzz/transformation_add_dead_break.cpp",
+ "source/fuzz/transformation_add_dead_break.h",
+ "source/fuzz/transformation_add_dead_continue.cpp",
+ "source/fuzz/transformation_add_dead_continue.h",
+ "source/fuzz/transformation_add_early_terminator_wrapper.cpp",
+ "source/fuzz/transformation_add_early_terminator_wrapper.h",
+ "source/fuzz/transformation_add_function.cpp",
+ "source/fuzz/transformation_add_function.h",
+ "source/fuzz/transformation_add_global_undef.cpp",
+ "source/fuzz/transformation_add_global_undef.h",
+ "source/fuzz/transformation_add_global_variable.cpp",
+ "source/fuzz/transformation_add_global_variable.h",
+ "source/fuzz/transformation_add_image_sample_unused_components.cpp",
+ "source/fuzz/transformation_add_image_sample_unused_components.h",
+ "source/fuzz/transformation_add_local_variable.cpp",
+ "source/fuzz/transformation_add_local_variable.h",
+ "source/fuzz/transformation_add_loop_preheader.cpp",
+ "source/fuzz/transformation_add_loop_preheader.h",
+ "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp",
+ "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h",
+ "source/fuzz/transformation_add_no_contraction_decoration.cpp",
+ "source/fuzz/transformation_add_no_contraction_decoration.h",
+ "source/fuzz/transformation_add_opphi_synonym.cpp",
+ "source/fuzz/transformation_add_opphi_synonym.h",
+ "source/fuzz/transformation_add_parameter.cpp",
+ "source/fuzz/transformation_add_parameter.h",
+ "source/fuzz/transformation_add_relaxed_decoration.cpp",
+ "source/fuzz/transformation_add_relaxed_decoration.h",
+ "source/fuzz/transformation_add_spec_constant_op.cpp",
+ "source/fuzz/transformation_add_spec_constant_op.h",
+ "source/fuzz/transformation_add_synonym.cpp",
+ "source/fuzz/transformation_add_synonym.h",
+ "source/fuzz/transformation_add_type_array.cpp",
+ "source/fuzz/transformation_add_type_array.h",
+ "source/fuzz/transformation_add_type_boolean.cpp",
+ "source/fuzz/transformation_add_type_boolean.h",
+ "source/fuzz/transformation_add_type_float.cpp",
+ "source/fuzz/transformation_add_type_float.h",
+ "source/fuzz/transformation_add_type_function.cpp",
+ "source/fuzz/transformation_add_type_function.h",
+ "source/fuzz/transformation_add_type_int.cpp",
+ "source/fuzz/transformation_add_type_int.h",
+ "source/fuzz/transformation_add_type_matrix.cpp",
+ "source/fuzz/transformation_add_type_matrix.h",
+ "source/fuzz/transformation_add_type_pointer.cpp",
+ "source/fuzz/transformation_add_type_pointer.h",
+ "source/fuzz/transformation_add_type_struct.cpp",
+ "source/fuzz/transformation_add_type_struct.h",
+ "source/fuzz/transformation_add_type_vector.cpp",
+ "source/fuzz/transformation_add_type_vector.h",
+ "source/fuzz/transformation_adjust_branch_weights.cpp",
+ "source/fuzz/transformation_adjust_branch_weights.h",
+ "source/fuzz/transformation_composite_construct.cpp",
+ "source/fuzz/transformation_composite_construct.h",
+ "source/fuzz/transformation_composite_extract.cpp",
+ "source/fuzz/transformation_composite_extract.h",
+ "source/fuzz/transformation_composite_insert.cpp",
+ "source/fuzz/transformation_composite_insert.h",
+ "source/fuzz/transformation_compute_data_synonym_fact_closure.cpp",
+ "source/fuzz/transformation_compute_data_synonym_fact_closure.h",
+ "source/fuzz/transformation_context.cpp",
+ "source/fuzz/transformation_context.h",
+ "source/fuzz/transformation_duplicate_region_with_selection.cpp",
+ "source/fuzz/transformation_duplicate_region_with_selection.h",
+ "source/fuzz/transformation_equation_instruction.cpp",
+ "source/fuzz/transformation_equation_instruction.h",
+ "source/fuzz/transformation_expand_vector_reduction.cpp",
+ "source/fuzz/transformation_expand_vector_reduction.h",
+ "source/fuzz/transformation_flatten_conditional_branch.cpp",
+ "source/fuzz/transformation_flatten_conditional_branch.h",
+ "source/fuzz/transformation_function_call.cpp",
+ "source/fuzz/transformation_function_call.h",
+ "source/fuzz/transformation_inline_function.cpp",
+ "source/fuzz/transformation_inline_function.h",
+ "source/fuzz/transformation_invert_comparison_operator.cpp",
+ "source/fuzz/transformation_invert_comparison_operator.h",
+ "source/fuzz/transformation_load.cpp",
+ "source/fuzz/transformation_load.h",
+ "source/fuzz/transformation_make_vector_operation_dynamic.cpp",
+ "source/fuzz/transformation_make_vector_operation_dynamic.h",
+ "source/fuzz/transformation_merge_blocks.cpp",
+ "source/fuzz/transformation_merge_blocks.h",
+ "source/fuzz/transformation_merge_function_returns.cpp",
+ "source/fuzz/transformation_merge_function_returns.h",
+ "source/fuzz/transformation_move_block_down.cpp",
+ "source/fuzz/transformation_move_block_down.h",
+ "source/fuzz/transformation_move_instruction_down.cpp",
+ "source/fuzz/transformation_move_instruction_down.h",
+ "source/fuzz/transformation_mutate_pointer.cpp",
+ "source/fuzz/transformation_mutate_pointer.h",
+ "source/fuzz/transformation_outline_function.cpp",
+ "source/fuzz/transformation_outline_function.h",
+ "source/fuzz/transformation_permute_function_parameters.cpp",
+ "source/fuzz/transformation_permute_function_parameters.h",
+ "source/fuzz/transformation_permute_phi_operands.cpp",
+ "source/fuzz/transformation_permute_phi_operands.h",
+ "source/fuzz/transformation_propagate_instruction_down.cpp",
+ "source/fuzz/transformation_propagate_instruction_down.h",
+ "source/fuzz/transformation_propagate_instruction_up.cpp",
+ "source/fuzz/transformation_propagate_instruction_up.h",
+ "source/fuzz/transformation_push_id_through_variable.cpp",
+ "source/fuzz/transformation_push_id_through_variable.h",
+ "source/fuzz/transformation_record_synonymous_constants.cpp",
+ "source/fuzz/transformation_record_synonymous_constants.h",
+ "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp",
+ "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h",
+ "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp",
+ "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h",
+ "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp",
+ "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h",
+ "source/fuzz/transformation_replace_constant_with_uniform.cpp",
+ "source/fuzz/transformation_replace_constant_with_uniform.h",
+ "source/fuzz/transformation_replace_copy_memory_with_load_store.cpp",
+ "source/fuzz/transformation_replace_copy_memory_with_load_store.h",
+ "source/fuzz/transformation_replace_copy_object_with_store_load.cpp",
+ "source/fuzz/transformation_replace_copy_object_with_store_load.h",
+ "source/fuzz/transformation_replace_id_with_synonym.cpp",
+ "source/fuzz/transformation_replace_id_with_synonym.h",
+ "source/fuzz/transformation_replace_irrelevant_id.cpp",
+ "source/fuzz/transformation_replace_irrelevant_id.h",
+ "source/fuzz/transformation_replace_linear_algebra_instruction.cpp",
+ "source/fuzz/transformation_replace_linear_algebra_instruction.h",
+ "source/fuzz/transformation_replace_load_store_with_copy_memory.cpp",
+ "source/fuzz/transformation_replace_load_store_with_copy_memory.h",
+ "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp",
+ "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h",
+ "source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp",
+ "source/fuzz/transformation_replace_opselect_with_conditional_branch.h",
+ "source/fuzz/transformation_replace_parameter_with_global.cpp",
+ "source/fuzz/transformation_replace_parameter_with_global.h",
+ "source/fuzz/transformation_replace_params_with_struct.cpp",
+ "source/fuzz/transformation_replace_params_with_struct.h",
+ "source/fuzz/transformation_set_function_control.cpp",
+ "source/fuzz/transformation_set_function_control.h",
+ "source/fuzz/transformation_set_loop_control.cpp",
+ "source/fuzz/transformation_set_loop_control.h",
+ "source/fuzz/transformation_set_memory_operands_mask.cpp",
+ "source/fuzz/transformation_set_memory_operands_mask.h",
+ "source/fuzz/transformation_set_selection_control.cpp",
+ "source/fuzz/transformation_set_selection_control.h",
+ "source/fuzz/transformation_split_block.cpp",
+ "source/fuzz/transformation_split_block.h",
+ "source/fuzz/transformation_store.cpp",
+ "source/fuzz/transformation_store.h",
+ "source/fuzz/transformation_swap_commutable_operands.cpp",
+ "source/fuzz/transformation_swap_commutable_operands.h",
+ "source/fuzz/transformation_swap_conditional_branch_operands.cpp",
+ "source/fuzz/transformation_swap_conditional_branch_operands.h",
+ "source/fuzz/transformation_swap_function_variables.cpp",
+ "source/fuzz/transformation_swap_function_variables.h",
+ "source/fuzz/transformation_swap_two_functions.cpp",
+ "source/fuzz/transformation_swap_two_functions.h",
+ "source/fuzz/transformation_toggle_access_chain_instruction.cpp",
+ "source/fuzz/transformation_toggle_access_chain_instruction.h",
+ "source/fuzz/transformation_vector_shuffle.cpp",
+ "source/fuzz/transformation_vector_shuffle.h",
+ "source/fuzz/transformation_wrap_early_terminator_in_function.cpp",
+ "source/fuzz/transformation_wrap_early_terminator_in_function.h",
+ "source/fuzz/transformation_wrap_region_in_selection.cpp",
+ "source/fuzz/transformation_wrap_region_in_selection.h",
+ "source/fuzz/transformation_wrap_vector_synonym.cpp",
+ "source/fuzz/transformation_wrap_vector_synonym.h",
+ "source/fuzz/uniform_buffer_element_descriptor.cpp",
+ "source/fuzz/uniform_buffer_element_descriptor.h",
+ ]
+ deps = [
+ ":spvtools",
+ ":spvtools_fuzz_proto",
+ ":spvtools_opt",
+ ":spvtools_reduce",
+ "//third_party/protobuf:protobuf_full",
+ ]
+ public_deps = [ ":spvtools_headers" ]
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [ "//build/config/compiler:no_chromium_code" ]
+ configs += [ ":spvtools_internal_config" ]
+ }
+}
+
group("SPIRV-Tools") {
public_deps = [
":spvtools",
@@ -899,6 +1367,7 @@ if (build_with_chromium) {
":spvtools",
":spvtools_language_header_cldebuginfo100",
":spvtools_language_header_debuginfo",
+ ":spvtools_language_header_vkdebuginfo100",
":spvtools_val",
"//testing/gmock",
"//testing/gtest",
@@ -1006,8 +1475,31 @@ executable("spirv-link") {
configs += [ ":spvtools_internal_config" ]
}
+if (!is_ios && !spirv_is_winuwp && build_with_chromium) {
+ # 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.
+
+ executable("spirv-fuzz") {
+ sources = [ "tools/fuzz/fuzz.cpp" ]
+ deps = [
+ ":spvtools",
+ ":spvtools_fuzz",
+ ":spvtools_opt",
+ ":spvtools_reduce",
+ ":spvtools_software_version",
+ ":spvtools_util_cli_consumer",
+ ":spvtools_val",
+ "//third_party/protobuf:protobuf_full",
+ ]
+ configs += [ ":spvtools_internal_config" ]
+ }
+}
+
if (!is_ios && !spirv_is_winuwp) {
- # iOS and UWP do not allow std::system calls which spirv-reduce requires
+ # iOS and UWP do not allow std::system calls which spirv-reduce
+ # requires.
+
executable("spirv-reduce") {
sources = [ "tools/reduce/reduce.cpp" ]
deps = [
@@ -1031,6 +1523,9 @@ group("all_spirv_tools") {
":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 467655cc..246e4839 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,7 +1,69 @@
Revision history for SPIRV-Tools
-v2021.3-dev 2021-06-22
- - Start v2021.3-dev
+v2022.2-dev 2022-01-26
+ - Start v2022.2-dev
+
+v2022.1 2022-01-26
+ - General
+ - Add SPIR-V 1.6 support to wasm build (#4674)
+ - Improvements to disassembly within PassManager (#4677)
+ - Basic support for SPIR-V 1.6 (#4663)
+ - reflect debug (#4662)
+ - Fix endianness of string literals (#4622)
+ - Optimizer
+ - spirv-opt: add pass to Spread Volatile semantics (#4667)
+ - Fix constant propagation and folding of FClamp instructions (#4651)
+ - Manually fold floating point division by zero (#4637)
+ - Allow ADCE to remove dead inputs (#4629)
+ - Linker
+ - Linker improvements (#4679)
+ * test/linker: Code factorisation and small tweaks
+ * linker: Do not fail when going over limits
+ - Validator
+ - val: interface struct with builtins must be Block (#4665)
+ - Fuzzer
+ - Avoid id bound errors during opt fuzzing (#4658)
+ - Avoid uninitialised read when parsing hex float (#4646)
+
+v2021.4 2021-11-11
+ - General
+ - Add a WebAssembly build (#3752)
+ - Make cxx exceptions controllable (#4591)
+ - Validator
+ - Improve decoration validation (#4490)
+ - Optimizer
+ - Add spirv-opt pass to replace descriptor accesses based on variable indices (#4574)
+ - Do not fold snegate feeding sdiv (#4600)
+ - Handle overflowing id in merge return (#4606)
+ - Fuzzer
+ - Add libFuzzer target for spirv-fuzz (#4434)
+ - Linter
+
+v2021.3 2021-08-24
+ - General
+ - Initial support for SPV_KHR_integer_dot_product (#4327)
+ - Add non-semantic vulkan extended instruction set (#4362)
+ - Add common enum for debug info instructions from either opencl or vulkan (#4377)
+ - Validator
+ - Add validation for SPV_EXT_shader_atomic_float16_add (#4325)
+ - Disallow loading a runtime-sized array (#4473)
+ - spirv-val: Validate vulkan debug info similarly to opencl debug info (#4466)
+ - Optimizer
+ - spirv-opt: support SPV_EXT_shader_image_int64 (#4379)
+ - spirv-opt: Add dataflow analysis framework (#4402)
+ - Add control dependence analysis to opt (#4380)
+ - Add spirv-opt convert-to-sampled-image pass (#4340)
+ - spirv-opt: Add handling of vulkan debug info to DebugInfoManager (#4423)
+ - Fuzz
+ - spirv-fuzz: support AtomicLoad (#4330)
+ - spirv-fuzz: Support AtomicStore (#4440)
+ - spirv-fuzz: TransformationWrapVectorSynonym that rewrites scalar operations using vectors (#4376)
+ - spirv-fuzz: Add minimal SPIR-V example to test shaders (#4415)
+ - spirv-fuzz: support building using gn (#4365)
+ - Linter
+ - Add new target for spirv-lint (#4446)
+ - spirv-lint: add basic CLI argument handling (#4478)
+ - Add divergence analysis to linter (#4465)
v2021.2 2021-06-18
- General
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 55f84e6d..76b87d8c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -33,6 +33,7 @@ include(GNUInstallDirs)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_CXX_STANDARD 11)
+option(ENABLE_RTTI "Enables RTTI" OFF)
option(SPIRV_ALLOW_TIMERS "Allow timers via clock_gettime on supported platforms" ON)
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
@@ -48,6 +49,8 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
add_definitions(-DSPIRV_MAC)
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "iOS")
add_definitions(-DSPIRV_IOS)
+elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "tvOS")
+ add_definitions(-DSPIRV_TVOS)
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Android")
add_definitions(-DSPIRV_ANDROID)
set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS})
@@ -81,6 +84,10 @@ endif(SPIRV_BUILD_COMPRESSION)
option(SPIRV_BUILD_FUZZER "Build spirv-fuzz" OFF)
+set(SPIRV_LIB_FUZZING_ENGINE_LINK_OPTIONS "" CACHE STRING "Used by OSS-Fuzz to control, via link options, which fuzzing engine should be used")
+
+option(SPIRV_BUILD_LIBFUZZER_TARGETS "Build libFuzzer targets" OFF)
+
option(SPIRV_WERROR "Enable error on warning" ON)
if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") AND (NOT CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")))
set(COMPILER_IS_LIKE_GNU TRUE)
@@ -109,7 +116,7 @@ if(${COMPILER_IS_LIKE_GNU})
set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Werror)
endif()
elseif(MSVC)
- set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800)
+ set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800 /wd4819)
if(${SPIRV_WERROR})
set(SPIRV_WARNINGS ${SPIRV_WARNINGS} /WX)
@@ -157,6 +164,7 @@ endif()
# Note this target provides no API stability guarantees.
#
# Ideally, all of these will go away - see https://github.com/KhronosGroup/SPIRV-Tools/issues/3909.
+option(ENABLE_EXCEPTIONS_ON_MSVC "Build SPIRV-TOOLS with c++ exceptions enabled in MSVC" ON)
option(SPIRV_TOOLS_BUILD_STATIC "Build ${SPIRV_TOOLS}-static target. ${SPIRV_TOOLS} will alias to ${SPIRV_TOOLS}-static or ${SPIRV_TOOLS}-shared based on BUILD_SHARED_LIBS" ON)
if(SPIRV_TOOLS_BUILD_STATIC)
set(SPIRV_TOOLS_FULL_VISIBILITY ${SPIRV_TOOLS}-static)
@@ -176,11 +184,14 @@ function(spvtools_default_compile_options TARGET)
target_compile_options(${TARGET} PRIVATE ${SPIRV_WARNINGS})
if (${COMPILER_IS_LIKE_GNU})
- target_compile_options(${TARGET} PRIVATE
- -std=c++11 -fno-exceptions -fno-rtti)
+ target_compile_options(${TARGET} PRIVATE -std=c++11 -fno-exceptions)
target_compile_options(${TARGET} PRIVATE
-Wall -Wextra -Wno-long-long -Wshadow -Wundef -Wconversion
-Wno-sign-conversion)
+
+ if(NOT ENABLE_RTTI)
+ add_compile_options(-fno-rtti)
+ endif()
# For good call stacks in profiles, keep the frame pointers.
if(NOT "${SPIRV_PERF}" STREQUAL "")
target_compile_options(${TARGET} PRIVATE -fno-omit-frame-pointer)
@@ -205,7 +216,9 @@ function(spvtools_default_compile_options TARGET)
if (MSVC)
# Specify /EHs for exception handling. This makes using SPIRV-Tools as
# dependencies in other projects easier.
- target_compile_options(${TARGET} PRIVATE /EHs)
+ if(ENABLE_EXCEPTIONS_ON_MSVC)
+ target_compile_options(${TARGET} PRIVATE /EHs)
+ endif()
endif()
# For MinGW cross compile, statically link to the C++ runtime.
@@ -286,7 +299,7 @@ endif()
# Turn off if they take too long.
option(SPIRV_CHECK_CONTEXT "In a debug build, check if the IR context is in a valid state." ON)
if (${SPIRV_CHECK_CONTEXT})
- add_definitions(-DSPIRV_CHECK_CONTEXT)
+ add_compile_options($<$<CONFIG:Debug>:-DSPIRV_CHECK_CONTEXT>)
endif()
# Precompiled header macro. Parameters are source file list and filename for pch cpp file.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b46ae31e..1eb8b689 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -98,13 +98,6 @@ should pay particular attention to:
scenarios? The respective SPIR-V dialects are slightly different.
* Changes are made to a container while iterating through it. You have to be
careful that iterators are not invalidated or that elements are not skipped.
-* C++11 and VS2013. We generally assume that we have a C++11 compliant
- compiler. However, on Windows, we still support Visual Studio 2013, which is
- not fully C++11 compliant. See
- [here](https://msdn.microsoft.com/en-us/library/hh567368.aspx). In
- particular, note that it does not provide default move-constructors or
- move-assignments for classes. In general, r-value references do not work the
- way you might assume they do.
* For SPIR-V transforms: The module is changed, but the analyses are not
updated. For example, a new instruction is added, but the def-use manager is
not updated. Later on, it is possible that the def-use manager will be used,
diff --git a/DEPS b/DEPS
index 5f64ecaa..81e1b5d6 100644
--- a/DEPS
+++ b/DEPS
@@ -3,10 +3,10 @@ use_relative_paths = True
vars = {
'github': 'https://github.com',
- 'effcee_revision': '2ec8f8738118cc483b67c04a759fee53496c5659',
- 'googletest_revision': 'b7d472f1225c5a64943821d8483fecb469d3f382',
- 're2_revision': 'f8e389f3acdc2517562924239e2a188037393683',
- 'spirv_headers_revision': 'f95c3b3761ee1b1903f54ae69b526ed6f0edc3b9',
+ 'effcee_revision': 'ddf5e2bb92957dc8a12c5392f8495333d6844133',
+ 'googletest_revision': 'f45d5865ed0b2b8912244627cdf508a24cc6ccb4',
+ 're2_revision': '611baecbcedc9cec1f46e38616b6d8880b676c03',
+ 'spirv_headers_revision': 'b42ba6d92faf6b4938e6f22ddd186dbdacc98d78',
}
deps = {
diff --git a/README.md b/README.md
index 3637305b..14db1e70 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,6 @@ headers, and XML registry.
## Downloads
-[![Build status](https://ci.appveyor.com/api/projects/status/gpue87cesrx3pi0d/branch/master?svg=true)](https://ci.appveyor.com/project/Khronoswebmaster/spirv-tools/branch/master)
<img alt="Linux" src="kokoro/img/linux.png" width="20px" height="20px" hspace="2px"/>[![Linux Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_linux_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html)
<img alt="MacOS" src="kokoro/img/macos.png" width="20px" height="20px" hspace="2px"/>[![MacOS Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_macos_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html)
<img alt="Windows" src="kokoro/img/windows.png" width="20px" height="20px" hspace="2px"/>[![Windows Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_windows_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html)
@@ -45,6 +44,20 @@ following versions are ordered from oldest to newest:
Use the `--version` option on each command line tool to see the software
version. An API call reports the software version as a C-style string.
+## Releases
+
+Some versions of SPIRV-Tools are tagged as stable releases (see
+[tags](https://github.com/KhronosGroup/SPIRV-Tools/tags) on github).
+These versions undergo extra testing.
+Releases are not directly related to releases (or versions) of
+[SPIRV-Headers][spirv-headers].
+Releases of SPIRV-Tools are tested against the version of SPIRV-Headers listed
+in the [DEPS](DEPS) file.
+The release generally uses the most recent compatible version of SPIRV-Headers
+available at the time of release.
+No version of SPIRV-Headers other than the one listed in the DEPS file is
+guaranteed to work with the SPIRV-Tools release.
+
## Supported features
### Assembler, binary parser, and disassembler
@@ -242,6 +255,34 @@ Contributions via merge request are welcome. Changes should:
We intend to maintain a linear history on the GitHub `master` branch.
+### Getting the source
+
+Example of getting sources, assuming SPIRV-Tools is configured as a standalone project:
+
+ git clone https://github.com/KhronosGroup/SPIRV-Tools.git spirv-tools
+ cd spirv-tools
+
+ # Check out sources for dependencies, at versions known to work together,
+ # as listed in the DEPS file.
+ python3 utils/git-sync-deps
+
+For some kinds of development, you may need the latest sources from the third-party projects:
+
+ git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-tools/external/spirv-headers
+ git clone https://github.com/google/googletest.git spirv-tools/external/googletest
+ git clone https://github.com/google/effcee.git spirv-tools/external/effcee
+ git clone https://github.com/google/re2.git spirv-tools/external/re2
+
+#### Dependency on Effcee
+
+Some tests depend on the [Effcee][effcee] library for stateful matching.
+Effcee itself depends on [RE2][re2].
+
+* If SPIRV-Tools is configured as part of a larger project that already uses
+ Effcee, then that project should include Effcee before SPIRV-Tools.
+* Otherwise, SPIRV-Tools expects Effcee sources to appear in `external/effcee`
+ and RE2 sources to appear in `external/re2`.
+
### Source code organization
* `example`: demo code of using SPIRV-Tools APIs
@@ -260,14 +301,6 @@ We intend to maintain a linear history on the GitHub `master` branch.
* `test/`: Tests, using the [googletest][googletest] framework
* `tools/`: Command line executables
-Example of getting sources, assuming SPIRV-Tools is configured as a standalone project:
-
- git clone https://github.com/KhronosGroup/SPIRV-Tools.git spirv-tools
- git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-tools/external/spirv-headers
- git clone https://github.com/google/googletest.git spirv-tools/external/googletest
- git clone https://github.com/google/effcee.git spirv-tools/external/effcee
- git clone https://github.com/google/re2.git spirv-tools/external/re2
-
### Tests
The project contains a number of tests, used to drive development
@@ -281,46 +314,12 @@ tests:
`googletest` source into the `<spirv-dir>/external/googletest` directory before
configuring and building the project.
-*Note*: You must use a version of googletest that includes
-[a fix][googletest-pull-612] for [googletest issue 610][googletest-issue-610].
-The fix is included on the googletest master branch any time after 2015-11-10.
-In particular, googletest must be newer than version 1.7.0.
-
-### Dependency on Effcee
-
-Some tests depend on the [Effcee][effcee] library for stateful matching.
-Effcee itself depends on [RE2][re2].
-
-* If SPIRV-Tools is configured as part of a larger project that already uses
- Effcee, then that project should include Effcee before SPIRV-Tools.
-* Otherwise, SPIRV-Tools expects Effcee sources to appear in `external/effcee`
- and RE2 sources to appear in `external/re2`.
-
-
## Build
-Instead of building manually, you can also download the binaries for your
-platform directly from the [master-tot release][master-tot-release] on GitHub.
-Those binaries are automatically uploaded by the buildbots after successful
-testing and they always reflect the current top of the tree of the master
-branch.
-
-In order to build the code, you first need to sync the external repositories
-that it depends on. Assume that `<spirv-dir>` is the root directory of the
-checked out code:
-
-```sh
-cd <spirv-dir>
-git clone https://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers
-git clone https://github.com/google/effcee.git external/effcee
-git clone https://github.com/google/re2.git external/re2
-git clone https://github.com/google/googletest.git external/googletest # optional
-
-```
+*Note*: Prebuilt binaries are available from the [downloads](docs/downloads.md) page.
-*Note*:
-The script `utils/git-sync-deps` can be used to checkout and/or update the
-contents of the repos under `external/` instead of manually maintaining them.
+First [get the sources](#getting-the-source).
+Then build using CMake, Bazel, Android ndk-build, or the Emscripten SDK.
### Build using CMake
You can build the project using [CMake][cmake]:
@@ -348,7 +347,7 @@ option, like so:
```sh
# In <spirv-dir> (the SPIRV-Tools repo root):
-git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf
+git clone --depth=1 --branch v3.13.0.1 https://github.com/protocolbuffers/protobuf external/protobuf
# In your build directory:
cmake [-G <platform-generator>] <spirv-dir> -DSPIRV_BUILD_FUZZER=ON
@@ -365,6 +364,30 @@ You can also use [Bazel](https://bazel.build/) to build the project.
cd <spirv-dir>
bazel build :all
```
+### Build a node.js package using Emscripten
+
+The SPIRV-Tools core library can be built to a WebAssembly [node.js](https://nodejs.org)
+module. The resulting `SpirvTools` WebAssembly module only exports methods to
+assemble and disassemble SPIR-V modules.
+
+First, make sure you have the [Emscripten SDK](https://emscripten.org).
+Then:
+
+```sh
+cd <spirv-dir>
+./source/wasm/build.sh
+```
+
+The resulting node package, with JavaScript and TypeScript bindings, is
+written to `<spirv-dir>/out/web`.
+
+Note: This builds the package locally. It does *not* publish it to [npm](https://npmjs.org).
+
+To test the result:
+
+```sh
+node ./test/wasm/test.js
+```
### Tools you'll need
@@ -378,15 +401,17 @@ 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
also work, but are not verified.
+- [Emscripten SDK](https://emscripten.org) (optional): if building the
+ WebAssembly module.
SPIRV-Tools is regularly tested with the following compilers:
On Linux
-- GCC version 4.8.5
-- Clang version 3.8
+- GCC version 9.3
+- Clang version 10.0
On MacOS
-- AppleClang 10.0
+- AppleClang 11.0
On Windows
- Visual Studio 2015
@@ -421,7 +446,7 @@ via setting `SPIRV_TOOLS_EXTRA_DEFINITIONS`. For example, by setting it to
`/D_ITERATOR_DEBUG_LEVEL=0` on Windows, you can disable checked iterators and
iterator debugging.
-### Android
+### Android ndk-build
SPIR-V Tools supports building static libraries `libSPIRV-Tools.a` and
`libSPIRV-Tools-opt.a` for Android:
@@ -442,11 +467,12 @@ $ANDROID_NDK/ndk-build -C ../android_test \
```
### Updating DEPS
-Occasionally the entries in DEPS will need to be updated. This is done on demand
-when there is a request to do this, often due to downstream breakages. There is
-a script `utils/roll_deps.sh` provided, which will generate a patch with the
-updated DEPS values. This will still need to be tested in your checkout to
-confirm that there are no integration issues that need to be resolved.
+
+Occasionally the entries in [DEPS](DEPS) will need to be updated. This is done on
+demand when there is a request to do this, often due to downstream breakages.
+To update `DEPS`, run `utils/roll_deps.sh` and confirm that tests pass.
+The script requires Chromium's
+[`depot_tools`](https://chromium.googlesource.com/chromium/tools/depot_tools).
## Library
@@ -643,8 +669,32 @@ This is experimental.
### Tests
-Tests are only built when googletest is found. Use `ctest` to run all the
-tests.
+Tests are only built when googletest is found.
+
+#### Running test with CMake
+
+Use `ctest -j <num threads>` to run all the tests. To run tests using all threads:
+```shell
+ctest -j$(nproc)
+```
+
+To run a single test target, use `ctest [-j <N>] -R <test regex>`. For example,
+you can run all `opt` tests with:
+```shell
+ctest -R 'spirv-tools-test_opt'
+```
+
+#### Running test with Bazel
+
+Use `bazel test :all` to run all tests. This will run tests in parallel by default.
+
+To run a single test target, specify `:my_test_target` instead of `:all`. Test target
+names get printed when you run `bazel test :all`. For example, you can run
+`opt_def_use_test` with:
+```shell
+bazel test :opt_def_use_test
+```
+
## Future Work
<a name="future"></a>
@@ -705,4 +755,3 @@ limitations under the License.
[CMake]: https://cmake.org/
[cpp-style-guide]: https://google.github.io/styleguide/cppguide.html
[clang-sanitizers]: http://clang.llvm.org/docs/UsersManual.html#controlling-code-generation
-[master-tot-release]: https://github.com/KhronosGroup/SPIRV-Tools/releases/tag/master-tot
diff --git a/build_defs.bzl b/build_defs.bzl
index 30af3bd6..b2cd41b9 100644
--- a/build_defs.bzl
+++ b/build_defs.bzl
@@ -41,6 +41,7 @@ TEST_COPTS = COMMON_COPTS + select({
DEBUGINFO_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_debuginfo_grammar_unified1"
CLDEBUGINFO100_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_opencl_debuginfo_100_grammar_unified1"
+SHDEBUGINFO100_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_nonsemantic_shader_debuginfo_100_grammar_unified1"
def generate_core_tables(version = None):
if not version:
@@ -201,6 +202,23 @@ def base_test(name, srcs, deps = []):
] + deps,
)
+def lint_test(name, srcs, deps = []):
+ if name[-5:] != "_test":
+ name = name + "_test"
+ native.cc_test(
+ name = "lint_" + name,
+ srcs = srcs,
+ compatible_with = [],
+ copts = TEST_COPTS,
+ size = "large",
+ deps = [
+ ":spirv_tools_lint",
+ "@com_google_googletest//:gtest_main",
+ "@com_google_googletest//:gtest",
+ "@com_google_effcee//:effcee",
+ ] + deps,
+ )
+
def link_test(name, srcs, deps = []):
if name[-5:] != "_test":
name = name + "_test"
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..fb6d114f
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,10 @@
+version: "3"
+services:
+ build:
+ image: emscripten/emsdk:2.0.2
+ environment:
+ GITHUB_RUN_NUMBER: ${GITHUB_RUN_NUMBER:-}
+ working_dir: /app
+ command: ./source/wasm/build.sh
+ volumes:
+ - ./:/app
diff --git a/docs/downloads.md b/docs/downloads.md
index 9c7d8567..168937a7 100644
--- a/docs/downloads.md
+++ b/docs/downloads.md
@@ -1,14 +1,28 @@
# Downloads
-Download the latest builds.
-## Release
+## Latest builds
+
+Download the latest builds of the [master](https://github.com/KhronosGroup/SPIRV-Tools/tree/master) branch.
+
+### Release build
| Windows | Linux | MacOS |
| --- | --- | --- |
| [MSVC 2017](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html) |
| | [gcc](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_release.html) | |
-## Debug
+### Debug build
| Windows | Linux | MacOS |
| --- | --- | --- |
| [MSVC 2017](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_debug.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_debug.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_debug.html) |
| | [gcc](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_debug.html) | |
+
+
+## Vulkan SDK
+
+SPIRV-Tools is published as part of the [LunarG Vulkan SDK](https://www.lunarg.com/vulkan-sdk/).
+The Vulkan SDK is updated approximately every six weeks.
+
+## Android NDK
+
+SPIRV-Tools host executables, and library sources are published as
+part of the [Android NDK](https://developer.android.com/ndk/downloads).
diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h
index 039dab18..e1b8890e 100644
--- a/include/spirv-tools/libspirv.h
+++ b/include/spirv-tools/libspirv.h
@@ -309,6 +309,7 @@ typedef enum spv_ext_inst_type_t {
SPV_EXT_INST_TYPE_DEBUGINFO,
SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION,
+ SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100,
// Multiple distinct extended instruction set types could return this
// value, if they are prefixed with NonSemantic. and are otherwise
@@ -481,6 +482,7 @@ SPIRV_TOOLS_EXPORT const char* spvSoftwareVersionDetailsString(void);
// SPV_ENV_VULKAN_1_1 -> SPIR-V 1.3
// SPV_ENV_VULKAN_1_1_SPIRV_1_4 -> SPIR-V 1.4
// SPV_ENV_VULKAN_1_2 -> SPIR-V 1.5
+// SPV_ENV_VULKAN_1_3 -> SPIR-V 1.6
// Consult the description of API entry points for specific rules.
typedef enum {
SPV_ENV_UNIVERSAL_1_0, // SPIR-V 1.0 latest revision, no other restrictions.
@@ -515,6 +517,11 @@ typedef enum {
SPV_ENV_UNIVERSAL_1_5, // SPIR-V 1.5 latest revision, no other restrictions.
SPV_ENV_VULKAN_1_2, // Vulkan 1.2 latest revision.
+
+ SPV_ENV_UNIVERSAL_1_6, // SPIR-V 1.6 latest revision, no other restrictions.
+ SPV_ENV_VULKAN_1_3, // Vulkan 1.3 latest revision.
+
+ SPV_ENV_MAX // Keep this as the last enum value.
} spv_target_env;
// SPIR-V Validator can be parameterized with the following Universal Limits.
@@ -553,7 +560,7 @@ SPIRV_TOOLS_EXPORT bool spvParseVulkanEnv(uint32_t vulkan_ver,
// Creates a context object for most of the SPIRV-Tools API.
// Returns null if env is invalid.
//
-// See specific API calls for how the target environment is interpeted
+// See specific API calls for how the target environment is interpreted
// (particularly assembly and validation).
SPIRV_TOOLS_EXPORT spv_context spvContextCreate(spv_target_env env);
@@ -605,7 +612,7 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxLogicalPointer(
// set that option.
// 2) Pointers that are pass as parameters to function calls do not have to
// match the storage class of the formal parameter.
-// 3) Pointers that are actaul parameters on function calls do not have to point
+// 3) Pointers that are actual parameters on function calls do not have to point
// to the same type pointed as the formal parameter. The types just need to
// logically match.
// 4) GLSLstd450 Interpolate* instructions can have a load of an interpolant
@@ -631,7 +638,7 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetUniformBufferStandardLayout(
// Records whether the validator should use "scalar" block layout rules.
// Scalar layout rules are more permissive than relaxed block layout.
//
-// See Vulkan extnesion VK_EXT_scalar_block_layout. The scalar alignment is
+// See Vulkan extension VK_EXT_scalar_block_layout. The scalar alignment is
// defined as follows:
// - scalar alignment of a scalar is the scalar size
// - scalar alignment of a vector is the scalar alignment of its component
@@ -658,6 +665,11 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetWorkgroupScalarBlockLayout(
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetSkipBlockLayout(
spv_validator_options options, bool val);
+// Records whether or not the validator should allow the LocalSizeId
+// decoration where the environment otherwise would not allow it.
+SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetAllowLocalSizeId(
+ spv_validator_options options, bool val);
+
// Creates an optimizer options object with default options. Returns a valid
// options object. The object remains valid until it is passed into
// |spvOptimizerOptionsDestroy|.
diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp
index 0c31a182..25eb8a1d 100644
--- a/include/spirv-tools/libspirv.hpp
+++ b/include/spirv-tools/libspirv.hpp
@@ -36,7 +36,7 @@ class Context {
public:
// Constructs a context targeting the given environment |env|.
//
- // See specific API calls for how the target environment is interpeted
+ // See specific API calls for how the target environment is interpreted
// (particularly assembly and validation).
//
// The constructed instance will have an empty message consumer, which just
@@ -115,6 +115,12 @@ class ValidatorOptions {
spvValidatorOptionsSetSkipBlockLayout(options_, val);
}
+ // Enables LocalSizeId decorations where the environment would not otherwise
+ // allow them.
+ void SetAllowLocalSizeId(bool val) {
+ spvValidatorOptionsSetAllowLocalSizeId(options_, val);
+ }
+
// Records whether or not the validator should relax the rules on pointer
// usage in logical addressing mode.
//
@@ -133,7 +139,7 @@ class ValidatorOptions {
// set that option.
// 2) Pointers that are pass as parameters to function calls do not have to
// match the storage class of the formal parameter.
- // 3) Pointers that are actaul parameters on function calls do not have to
+ // 3) Pointers that are actual parameters on function calls do not have to
// point to the same type pointed as the formal parameter. The types just
// need to logically match.
// 4) GLSLstd450 Interpolate* instructions can have a load of an interpolant
diff --git a/include/spirv-tools/linter.hpp b/include/spirv-tools/linter.hpp
new file mode 100644
index 00000000..52ed5a46
--- /dev/null
+++ b/include/spirv-tools/linter.hpp
@@ -0,0 +1,48 @@
+// Copyright (c) 2021 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef INCLUDE_SPIRV_TOOLS_LINTER_HPP_
+#define INCLUDE_SPIRV_TOOLS_LINTER_HPP_
+
+#include "libspirv.hpp"
+
+namespace spvtools {
+
+// C++ interface for SPIR-V linting functionalities. It wraps the context
+// (including target environment and the corresponding SPIR-V grammar) and
+// provides a method for linting.
+//
+// Instances of this class provides basic thread-safety guarantee.
+class Linter {
+ public:
+ explicit Linter(spv_target_env env);
+
+ ~Linter();
+
+ // Sets the message consumer to the given |consumer|. The |consumer| will be
+ // invoked once for each message communicated from the library.
+ void SetMessageConsumer(MessageConsumer consumer);
+
+ // Returns a reference to the registered message consumer.
+ const MessageConsumer& Consumer() const;
+
+ bool Run(const uint32_t* binary, size_t binary_size);
+
+ private:
+ struct Impl;
+ std::unique_ptr<Impl> impl_;
+};
+} // namespace spvtools
+
+#endif // INCLUDE_SPIRV_TOOLS_LINTER_HPP_
diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp
index e8b5b698..fdb2e648 100644
--- a/include/spirv-tools/optimizer.hpp
+++ b/include/spirv-tools/optimizer.hpp
@@ -19,6 +19,7 @@
#include <ostream>
#include <string>
#include <unordered_map>
+#include <utility>
#include <vector>
#include "libspirv.hpp"
@@ -27,7 +28,8 @@ namespace spvtools {
namespace opt {
class Pass;
-}
+struct DescriptorSetAndBinding;
+} // namespace opt
// C++ interface for SPIR-V optimization functionalities. It wraps the context
// (including target environment and the corresponding SPIR-V grammar) and
@@ -41,7 +43,7 @@ class Optimizer {
// consumed by the RegisterPass() method. Tokens are one-time objects that
// only support move; copying is not allowed.
struct PassToken {
- struct Impl; // Opaque struct for holding inernal data.
+ struct Impl; // Opaque struct for holding internal data.
PassToken(std::unique_ptr<Impl>);
@@ -225,16 +227,17 @@ Optimizer::PassToken CreateNullPass();
// Creates a strip-debug-info pass.
// A strip-debug-info pass removes all debug instructions (as documented in
-// Section 3.32.2 of the SPIR-V spec) of the SPIR-V module to be optimized.
+// Section 3.42.2 of the SPIR-V spec) of the SPIR-V module to be optimized.
Optimizer::PassToken CreateStripDebugInfoPass();
-// Creates a strip-reflect-info pass.
-// A strip-reflect-info pass removes all reflections instructions.
-// For now, this is limited to removing decorations defined in
-// SPV_GOOGLE_hlsl_functionality1. The coverage may expand in
-// the future.
+// [Deprecated] This will create a strip-nonsemantic-info pass. See below.
Optimizer::PassToken CreateStripReflectInfoPass();
+// Creates a strip-nonsemantic-info pass.
+// A strip-nonsemantic-info pass removes all reflections and explicitly
+// non-semantic instructions.
+Optimizer::PassToken CreateStripNonSemanticInfoPass();
+
// Creates an eliminate-dead-functions pass.
// An eliminate-dead-functions pass will remove all functions that are not in
// the call trees rooted at entry points and exported functions. These
@@ -293,11 +296,11 @@ Optimizer::PassToken CreateFreezeSpecConstantValuePass();
// and can be changed in future. A spec constant is foldable if all of its
// value(s) can be determined from the module. E.g., an integer spec constant
// defined with OpSpecConstantOp instruction can be folded if its value won't
-// change later. This pass will replace the original OpSpecContantOp instruction
-// with an OpConstant instruction. When folding composite spec constants,
-// new instructions may be inserted to define the components of the composite
-// constant first, then the original spec constants will be replaced by
-// OpConstantComposite instructions.
+// change later. This pass will replace the original OpSpecConstantOp
+// instruction with an OpConstant instruction. When folding composite spec
+// constants, new instructions may be inserted to define the components of the
+// composite constant first, then the original spec constants will be replaced
+// by OpConstantComposite instructions.
//
// There are some operations not supported yet:
// OpSConvert, OpFConvert, OpQuantizeToF16 and
@@ -323,7 +326,7 @@ Optimizer::PassToken CreateUnifyConstantPass();
// Creates a eliminate-dead-constant pass.
// A eliminate-dead-constant pass removes dead constants, including normal
-// contants defined by OpConstant, OpConstantComposite, OpConstantTrue, or
+// constants defined by OpConstant, OpConstantComposite, OpConstantTrue, or
// OpConstantFalse and spec constants defined by OpSpecConstant,
// OpSpecConstantComposite, OpSpecConstantTrue, OpSpecConstantFalse or
// OpSpecConstantOp.
@@ -387,7 +390,7 @@ Optimizer::PassToken CreateInlineOpaquePass();
// Only modules with relaxed logical addressing (see opt/instruction.h) are
// currently processed.
//
-// This pass is most effective if preceeded by Inlining and
+// This pass is most effective if preceded by Inlining and
// LocalAccessChainConvert. This pass will reduce the work needed to be done
// by LocalSingleStoreElim and LocalMultiStoreElim.
//
@@ -405,7 +408,7 @@ Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass();
// Note that some branches and blocks may be left to avoid creating invalid
// control flow. Improving this is left to future work.
//
-// This pass is most effective when preceeded by passes which eliminate
+// This pass is most effective when preceded by passes which eliminate
// local loads and stores, effectively propagating constant values where
// possible.
Optimizer::PassToken CreateDeadBranchElimPass();
@@ -422,7 +425,7 @@ Optimizer::PassToken CreateDeadBranchElimPass();
// are currently processed. Currently modules with any extensions enabled are
// not processed. This is left for future work.
//
-// This pass is most effective if preceeded by Inlining and
+// This pass is most effective if preceded by Inlining and
// LocalAccessChainConvert. LocalSingleStoreElim and LocalSingleBlockElim
// will reduce the work that this pass has to do.
Optimizer::PassToken CreateLocalMultiStoreElimPass();
@@ -512,7 +515,20 @@ Optimizer::PassToken CreateDeadInsertElimPass();
// Conversion, which tends to cause cycles of dead code to be left after
// Store/Load elimination passes are completed. These cycles cannot be
// eliminated with standard dead code elimination.
+//
+// If |preserve_interface| is true, all non-io variables in the entry point
+// interface are considered live and are not eliminated. This mode is needed
+// by GPU-Assisted validation instrumentation, where a change in the interface
+// is not allowed.
Optimizer::PassToken CreateAggressiveDCEPass();
+Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface);
+
+// Creates a remove-unused-interface-variables pass.
+// Removes variables referenced on the |OpEntryPoint| instruction that are not
+// referenced in the entry point function or any function in its call tree. Note
+// that this could cause the shader interface to no longer match other shader
+// stages.
+Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass();
// Creates an empty pass.
// This is deprecated and will be removed.
@@ -614,7 +630,7 @@ Optimizer::PassToken CreateRedundancyEliminationPass();
Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit = 100);
// Create a private to local pass.
-// This pass looks for variables delcared in the private storage class that are
+// This pass looks for variables declared in the private storage class that are
// used in only one function. Those variables are moved to the function storage
// class in the function that they are used.
Optimizer::PassToken CreatePrivateToLocalPass();
@@ -692,8 +708,11 @@ Optimizer::PassToken CreateVectorDCEPass();
// Create a pass to reduce the size of loads.
// This pass looks for loads of structures where only a few of its members are
// used. It replaces the loads feeding an OpExtract with an OpAccessChain and
-// a load of the specific elements.
-Optimizer::PassToken CreateReduceLoadSizePass();
+// a load of the specific elements. The parameter is a threshold to determine
+// whether we have to replace the load or not. If the ratio of the used
+// components of the load is less than the threshold, we replace the load.
+Optimizer::PassToken CreateReduceLoadSizePass(
+ double load_replacement_threshold = 0.9);
// Create a pass to combine chained access chains.
// This pass looks for access chains fed by other access chains and combines
@@ -816,6 +835,26 @@ Optimizer::PassToken CreateFixStorageClassPass();
// inclusive.
Optimizer::PassToken CreateGraphicsRobustAccessPass();
+// Create a pass to spread Volatile semantics to variables with SMIDNV,
+// WarpIDNV, SubgroupSize, SubgroupLocalInvocationId, SubgroupEqMask,
+// SubgroupGeMask, SubgroupGtMask, SubgroupLeMask, or SubgroupLtMask BuiltIn
+// decorations or OpLoad for them when the shader model is the ray generation,
+// closest hit, miss, intersection, or callable. This pass can be used for
+// VUID-StandaloneSpirv-VulkanMemoryModel-04678 and
+// VUID-StandaloneSpirv-VulkanMemoryModel-04679 (See "Standalone SPIR-V
+// Validation" section of Vulkan spec "Appendix A: Vulkan Environment for
+// SPIR-V"). When the SPIR-V version is 1.6 or above, the pass also spreads
+// the Volatile semantics to a variable with HelperInvocation BuiltIn decoration
+// in the fragement shader.
+Optimizer::PassToken CreateSpreadVolatileSemanticsPass();
+
+// Create a pass to replace a descriptor access using variable index.
+// This pass replaces every access using a variable index to array variable
+// |desc| that has a DescriptorSet and Binding decorations with a constant
+// element of the array. In order to replace the access using a variable index
+// with the constant element, it uses a switch statement.
+Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass();
+
// Create descriptor scalar replacement pass.
// This pass replaces every array variable |desc| that has a DescriptorSet and
// Binding decorations with a new variable for each element of the array.
@@ -847,6 +886,16 @@ Optimizer::PassToken CreateAmdExtToKhrPass();
// propagated into their final positions.
Optimizer::PassToken CreateInterpolateFixupPass();
+// 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
+// binding that is one of the given pairs, they will be converted to a sampled
+// image. In addition, if only an image has the descriptor set and binding that
+// is one of the given pairs, it will be converted to a sampled image as well.
+Optimizer::PassToken CreateConvertToSampledImagePass(
+ const std::vector<opt::DescriptorSetAndBinding>&
+ descriptor_set_binding_pairs);
+
} // namespace spvtools
#endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_
diff --git a/kokoro/linux-clang-ubsan/build.sh b/kokoro/linux-clang-ubsan/build.sh
new file mode 100755
index 00000000..b5941e34
--- /dev/null
+++ b/kokoro/linux-clang-ubsan/build.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+# Copyright (c) 2021 Google LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Linux Build Script.
+
+# Fail on any error.
+set -e
+# Display commands being run.
+set -x
+
+SCRIPT_DIR=`dirname "$BASH_SOURCE"`
+source $SCRIPT_DIR/../scripts/linux/build.sh UBSAN clang cmake
diff --git a/kokoro/linux-clang-ubsan/continuous.cfg b/kokoro/linux-clang-ubsan/continuous.cfg
new file mode 100644
index 00000000..cb5535e1
--- /dev/null
+++ b/kokoro/linux-clang-ubsan/continuous.cfg
@@ -0,0 +1,16 @@
+# Copyright (c) 2021 Google LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Continuous build configuration.
+build_file: "SPIRV-Tools/kokoro/linux-clang-ubsan/build.sh"
diff --git a/kokoro/linux-clang-ubsan/presubmit.cfg b/kokoro/linux-clang-ubsan/presubmit.cfg
new file mode 100644
index 00000000..029c74a5
--- /dev/null
+++ b/kokoro/linux-clang-ubsan/presubmit.cfg
@@ -0,0 +1,16 @@
+# Copyright (c) 2021 Google LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Presubmit build configuration.
+build_file: "SPIRV-Tools/kokoro/linux-clang-ubsan/build.sh"
diff --git a/kokoro/scripts/linux/build-docker.sh b/kokoro/scripts/linux/build-docker.sh
index ba216987..8f76803c 100755
--- a/kokoro/scripts/linux/build-docker.sh
+++ b/kokoro/scripts/linux/build-docker.sh
@@ -51,14 +51,14 @@ clone_if_missing https://github.com/google/googletest external/googlete
pushd external/googletest; git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7; popd
clone_if_missing https://github.com/google/effcee external/effcee --depth=1
clone_if_missing https://github.com/google/re2 external/re2 --depth=1
-clone_if_missing https://github.com/protocolbuffers/protobuf external/protobuf --branch v3.13.0
+clone_if_missing https://github.com/protocolbuffers/protobuf external/protobuf --branch v3.13.0.1
if [ $TOOL = "cmake" ]; then
using cmake-3.17.2
using ninja-1.10.0
# Possible configurations are:
- # ASAN, COVERAGE, RELEASE, DEBUG, DEBUG_EXCEPTION, RELEASE_MINGW
+ # ASAN, UBSAN, COVERAGE, RELEASE, DEBUG, DEBUG_EXCEPTION, RELEASE_MINGW
BUILD_TYPE="Debug"
if [ $CONFIG = "RELEASE" ] || [ $CONFIG = "RELEASE_MINGW" ]; then
BUILD_TYPE="RelWithDebInfo"
@@ -69,6 +69,13 @@ if [ $TOOL = "cmake" ]; then
if [ $CONFIG = "ASAN" ]; then
ADDITIONAL_CMAKE_FLAGS="-DSPIRV_USE_SANITIZER=address,bounds,null"
[ $COMPILER = "clang" ] || { echo "$CONFIG requires clang"; exit 1; }
+ elif [ $CONFIG = "UBSAN" ]; then
+ # UBSan requires RTTI, and by default UBSan does not exit when errors are
+ # encountered - additional compiler options are required to force this.
+ # The -DSPIRV_USE_SANITIZER=undefined option instructs SPIR-V Tools to be
+ # built with UBSan enabled.
+ ADDITIONAL_CMAKE_FLAGS="-DSPIRV_USE_SANITIZER=undefined -DENABLE_RTTI=ON -DCMAKE_C_FLAGS=-fno-sanitize-recover=all -DCMAKE_CXX_FLAGS=-fno-sanitize-recover=all"
+ [ $COMPILER = "clang" ] || { echo "$CONFIG requires clang"; exit 1; }
elif [ $CONFIG = "COVERAGE" ]; then
ADDITIONAL_CMAKE_FLAGS="-DENABLE_CODE_COVERAGE=ON"
SKIP_TESTS="True"
@@ -79,6 +86,10 @@ if [ $TOOL = "cmake" ]; then
SKIP_TESTS="True"
fi
+ if [ $COMPILER = "clang" ]; then
+ ADDITIONAL_CMAKE_FLAGS="$ADDITIONAL_CMAKE_FLAGS -DSPIRV_BUILD_LIBFUZZER_TARGETS=ON"
+ fi
+
clean_dir "$ROOT_DIR/build"
cd "$ROOT_DIR/build"
diff --git a/kokoro/scripts/linux/build.sh b/kokoro/scripts/linux/build.sh
index 4731ebdc..85d4b61a 100644
--- a/kokoro/scripts/linux/build.sh
+++ b/kokoro/scripts/linux/build.sh
@@ -26,7 +26,9 @@ COMPILER=$2
TOOL=$3
BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT}
+# "--privileged" is required to run ptrace in the asan builds.
docker run --rm -i \
+ --privileged \
--volume "${ROOT_DIR}:${ROOT_DIR}" \
--volume "${KOKORO_ARTIFACTS_DIR}:${KOKORO_ARTIFACTS_DIR}" \
--workdir "${ROOT_DIR}" \
diff --git a/kokoro/scripts/macos/build.sh b/kokoro/scripts/macos/build.sh
index 44c9a412..46128238 100644
--- a/kokoro/scripts/macos/build.sh
+++ b/kokoro/scripts/macos/build.sh
@@ -36,7 +36,7 @@ git clone https://github.com/google/googletest external/googletest
cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd ..
git clone --depth=1 https://github.com/google/effcee external/effcee
git clone --depth=1 https://github.com/google/re2 external/re2
-git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf
+git clone --depth=1 --branch v3.13.0.1 https://github.com/protocolbuffers/protobuf external/protobuf
mkdir build && cd $SRC/build
diff --git a/kokoro/scripts/windows/build.bat b/kokoro/scripts/windows/build.bat
index c964320d..24e29ccf 100644
--- a/kokoro/scripts/windows/build.bat
+++ b/kokoro/scripts/windows/build.bat
@@ -30,7 +30,7 @@ git clone https://github.com/google/googletest external/googletest
cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd ..
git clone --depth=1 https://github.com/google/effcee external/effcee
git clone --depth=1 https://github.com/google/re2 external/re2
-git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf
+git clone --depth=1 --branch v3.13.0.1 https://github.com/protocolbuffers/protobuf external/protobuf
:: #########################################
:: set up msvc build env
@@ -93,4 +93,4 @@ zip -r install.zip install
rm -rf %SRC%\build
rm -rf %SRC%\external
-exit /b 0 \ No newline at end of file
+exit /b 0
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 6633bc91..331ff675 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -17,10 +17,10 @@ set(VIMSYNTAX_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_vim_sy
set(XML_REGISTRY_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_registry_tables.py")
set(LANG_HEADER_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_language_headers.py")
-# For now, assume the DebugInfo grammar file is in the current directory.
-# It might migrate to SPIRV-Headers.
+# Pull in grammar files that have migrated to SPIRV-Headers
set(DEBUGINFO_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.debuginfo.grammar.json")
set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json")
+set(VKDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json")
# macro() definitions are used in the following because we need to append .inc
# file paths into some global lists (*_CPP_DEPENDS). And those global lists are
@@ -113,6 +113,9 @@ endmacro(spvtools_opencl_tables)
macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME OPERAND_KIND_PREFIX)
set(INSTS_FILE "${spirv-tools_BINARY_DIR}/${VENDOR_TABLE}.insts.inc")
set(GRAMMAR_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.${VENDOR_TABLE}.grammar.json")
+ if(NOT EXISTS ${GRAMMAR_FILE})
+ set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json")
+ endif()
add_custom_command(OUTPUT ${INSTS_FILE}
COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
--extinst-vendor-grammar=${GRAMMAR_FILE}
@@ -148,9 +151,11 @@ spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs" "")
spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb" "")
spvtools_vendor_tables("debuginfo" "debuginfo" "")
spvtools_vendor_tables("opencl.debuginfo.100" "cldi100" "CLDEBUG100_")
+spvtools_vendor_tables("nonsemantic.shader.debuginfo.100" "shdi100" "SHDEBUG100_")
spvtools_vendor_tables("nonsemantic.clspvreflection" "clspvreflection" "")
spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE})
spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE})
+spvtools_extinst_lang_headers("NonSemanticShaderDebugInfo100" ${VKDEBUGINFO100_GRAMMAR_JSON_FILE})
spvtools_vimsyntax("unified1" "1.0")
add_custom_target(spirv-tools-vimsyntax DEPENDS ${VIMSYNTAX_FILE})
@@ -211,6 +216,7 @@ add_subdirectory(opt)
add_subdirectory(reduce)
add_subdirectory(fuzz)
add_subdirectory(link)
+add_subdirectory(lint)
set(SPIRV_SOURCES
${spirv-tools_SOURCE_DIR}/include/spirv-tools/libspirv.h
@@ -226,6 +232,7 @@ set(SPIRV_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/assembly_grammar.h
${CMAKE_CURRENT_SOURCE_DIR}/binary.h
${CMAKE_CURRENT_SOURCE_DIR}/cfa.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/common_debug_info.h
${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.h
${CMAKE_CURRENT_SOURCE_DIR}/disassemble.h
${CMAKE_CURRENT_SOURCE_DIR}/enum_set.h
@@ -421,8 +428,10 @@ if(ENABLE_SPIRV_TOOLS_INSTALL)
# Special config file for root library compared to other libs.
file(WRITE ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake
"include(\${CMAKE_CURRENT_LIST_DIR}/${SPIRV_TOOLS}Target.cmake)\n"
- "set(${SPIRV_TOOLS}_LIBRARIES ${SPIRV_TOOLS})\n"
- "get_target_property(${SPIRV_TOOLS}_INCLUDE_DIRS ${SPIRV_TOOLS} INTERFACE_INCLUDE_DIRECTORIES)\n")
+ "if(TARGET ${SPIRV_TOOLS})\n"
+ " set(${SPIRV_TOOLS}_LIBRARIES ${SPIRV_TOOLS})\n"
+ " get_target_property(${SPIRV_TOOLS}_INCLUDE_DIRS ${SPIRV_TOOLS} INTERFACE_INCLUDE_DIRECTORIES)\n"
+ "endif()\n")
install(FILES ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake DESTINATION ${PACKAGE_DIR})
endif(ENABLE_SPIRV_TOOLS_INSTALL)
diff --git a/source/binary.cpp b/source/binary.cpp
index 090cccfe..24d32f8c 100644
--- a/source/binary.cpp
+++ b/source/binary.cpp
@@ -33,6 +33,7 @@
#include "source/operand.h"
#include "source/spirv_constant.h"
#include "source/spirv_endian.h"
+#include "source/util/string_utils.h"
spv_result_t spvBinaryHeaderGet(const spv_const_binary binary,
const spv_endianness_t endian,
@@ -62,6 +63,15 @@ spv_result_t spvBinaryHeaderGet(const spv_const_binary binary,
return SPV_SUCCESS;
}
+std::string spvDecodeLiteralStringOperand(const spv_parsed_instruction_t& inst,
+ const uint16_t operand_index) {
+ assert(operand_index < inst.num_operands);
+ const spv_parsed_operand_t& operand = inst.operands[operand_index];
+
+ return spvtools::utils::MakeString(inst.words + operand.offset,
+ operand.num_words);
+}
+
namespace {
// A SPIR-V binary parser. A parser instance communicates detailed parse
@@ -205,7 +215,7 @@ class Parser {
size_t word_index; // The current position in words.
size_t instruction_count; // The count of processed instructions
spv_endianness_t endian; // The endianness of the binary.
- // Is the SPIR-V binary in a different endiannes from the host native
+ // Is the SPIR-V binary in a different endianness from the host native
// endianness?
bool requires_endian_conversion;
@@ -290,7 +300,7 @@ spv_result_t Parser::parseInstruction() {
const uint32_t first_word = peek();
// If the module's endianness is different from the host native endianness,
- // then converted_words contains the the endian-translated words in the
+ // then converted_words contains the endian-translated words in the
// instruction.
_.endian_converted_words.clear();
_.endian_converted_words.push_back(first_word);
@@ -507,7 +517,8 @@ spv_result_t Parser::parseOperand(size_t inst_offset,
case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
assert(SpvOpSpecConstantOp == opcode);
- if (grammar_.lookupSpecConstantOpcode(SpvOp(word))) {
+ if (word > static_cast<uint32_t>(SpvOp::SpvOpMax) ||
+ grammar_.lookupSpecConstantOpcode(SpvOp(word))) {
return diagnostic()
<< "Invalid " << spvOperandTypeStr(type) << ": " << word;
}
@@ -576,27 +587,18 @@ spv_result_t Parser::parseOperand(size_t inst_offset,
case SPV_OPERAND_TYPE_LITERAL_STRING:
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: {
- convert_operand_endianness = false;
- const char* string =
- reinterpret_cast<const char*>(_.words + _.word_index);
- // Compute the length of the string, but make sure we don't run off the
- // end of the input.
- const size_t remaining_input_bytes =
- sizeof(uint32_t) * (_.num_words - _.word_index);
- const size_t string_num_content_bytes =
- spv_strnlen_s(string, remaining_input_bytes);
- // If there was no terminating null byte, then that's an end-of-input
- // error.
- if (string_num_content_bytes == remaining_input_bytes)
+ const size_t max_words = _.num_words - _.word_index;
+ std::string string =
+ spvtools::utils::MakeString(_.words + _.word_index, max_words, false);
+
+ if (string.length() == max_words * 4)
return exhaustedInputDiagnostic(inst_offset, opcode, type);
- // Account for null in the word length, so add 1 for null, then add 3 to
- // make sure we round up. The following is equivalent to:
- // (string_num_content_bytes + 1 + 3) / 4
- const size_t string_num_words = string_num_content_bytes / 4 + 1;
+
// Make sure we can record the word count without overflow.
//
// This error can't currently be triggered because of validity
// checks elsewhere.
+ const size_t string_num_words = string.length() / 4 + 1;
if (string_num_words > std::numeric_limits<uint16_t>::max()) {
return diagnostic() << "Literal string is longer than "
<< std::numeric_limits<uint16_t>::max()
@@ -610,7 +612,7 @@ spv_result_t Parser::parseOperand(size_t inst_offset,
// There is only one string literal argument to OpExtInstImport,
// so it's sufficient to guard this just on the opcode.
const spv_ext_inst_type_t ext_inst_type =
- spvExtInstImportTypeGet(string);
+ spvExtInstImportTypeGet(string.c_str());
if (SPV_EXT_INST_TYPE_NONE == ext_inst_type) {
return diagnostic()
<< "Invalid extended instruction import '" << string << "'";
diff --git a/source/binary.h b/source/binary.h
index 66d24c7e..eb3beaca 100644
--- a/source/binary.h
+++ b/source/binary.h
@@ -15,6 +15,8 @@
#ifndef SOURCE_BINARY_H_
#define SOURCE_BINARY_H_
+#include <string>
+
#include "source/spirv_definition.h"
#include "spirv-tools/libspirv.h"
@@ -33,4 +35,9 @@ spv_result_t spvBinaryHeaderGet(const spv_const_binary binary,
// replacement for C11's strnlen_s which might not exist in all environments.
size_t spv_strnlen_s(const char* str, size_t strsz);
+// Decode the string literal operand with index operand_index from instruction
+// inst.
+std::string spvDecodeLiteralStringOperand(const spv_parsed_instruction_t& inst,
+ const uint16_t operand_index);
+
#endif // SOURCE_BINARY_H_
diff --git a/source/cfa.h b/source/cfa.h
index 0d09014c..7cadf55f 100644
--- a/source/cfa.h
+++ b/source/cfa.h
@@ -42,7 +42,7 @@ class CFA {
/// Returns true if a block with @p id is found in the @p work_list vector
///
- /// @param[in] work_list Set of blocks visited in the the depth first
+ /// @param[in] work_list Set of blocks visited in the depth first
/// traversal
/// of the CFG
/// @param[in] id The ID of the block being checked
diff --git a/source/common_debug_info.h b/source/common_debug_info.h
new file mode 100644
index 00000000..ffa5d340
--- /dev/null
+++ b/source/common_debug_info.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2021 The Khronos Group Inc.
+// Copyright (c) 2021 Valve Corporation
+// Copyright (c) 2021 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_COMMON_DEBUG_INFO_HEADER_H_
+#define SOURCE_COMMON_DEBUG_INFO_HEADER_H_
+
+// This enum defines the known common set of instructions that are the same
+// between OpenCL.DebugInfo.100 and NonSemantic.Shader.DebugInfo.100.
+// Note that NonSemantic.Shader.* instructions can still have slightly
+// different encoding, as it does not use literals anywhere and only constants.
+enum CommonDebugInfoInstructions {
+ CommonDebugInfoDebugInfoNone = 0,
+ CommonDebugInfoDebugCompilationUnit = 1,
+ CommonDebugInfoDebugTypeBasic = 2,
+ CommonDebugInfoDebugTypePointer = 3,
+ CommonDebugInfoDebugTypeQualifier = 4,
+ CommonDebugInfoDebugTypeArray = 5,
+ CommonDebugInfoDebugTypeVector = 6,
+ CommonDebugInfoDebugTypedef = 7,
+ CommonDebugInfoDebugTypeFunction = 8,
+ CommonDebugInfoDebugTypeEnum = 9,
+ CommonDebugInfoDebugTypeComposite = 10,
+ CommonDebugInfoDebugTypeMember = 11,
+ CommonDebugInfoDebugTypeInheritance = 12,
+ CommonDebugInfoDebugTypePtrToMember = 13,
+ CommonDebugInfoDebugTypeTemplate = 14,
+ CommonDebugInfoDebugTypeTemplateParameter = 15,
+ CommonDebugInfoDebugTypeTemplateTemplateParameter = 16,
+ CommonDebugInfoDebugTypeTemplateParameterPack = 17,
+ CommonDebugInfoDebugGlobalVariable = 18,
+ CommonDebugInfoDebugFunctionDeclaration = 19,
+ CommonDebugInfoDebugFunction = 20,
+ CommonDebugInfoDebugLexicalBlock = 21,
+ CommonDebugInfoDebugLexicalBlockDiscriminator = 22,
+ CommonDebugInfoDebugScope = 23,
+ CommonDebugInfoDebugNoScope = 24,
+ CommonDebugInfoDebugInlinedAt = 25,
+ CommonDebugInfoDebugLocalVariable = 26,
+ CommonDebugInfoDebugInlinedVariable = 27,
+ CommonDebugInfoDebugDeclare = 28,
+ CommonDebugInfoDebugValue = 29,
+ CommonDebugInfoDebugOperation = 30,
+ CommonDebugInfoDebugExpression = 31,
+ CommonDebugInfoDebugMacroDef = 32,
+ CommonDebugInfoDebugMacroUndef = 33,
+ CommonDebugInfoDebugImportedEntity = 34,
+ CommonDebugInfoDebugSource = 35,
+ CommonDebugInfoInstructionsMax = 0x7ffffff
+};
+
+#endif // SOURCE_COMMON_DEBUG_INFO_HEADER_H_
diff --git a/source/diagnostic.cpp b/source/diagnostic.cpp
index edc27c8f..f3aa2594 100644
--- a/source/diagnostic.cpp
+++ b/source/diagnostic.cpp
@@ -37,7 +37,7 @@ spv_diagnostic spvDiagnosticCreate(const spv_position position,
diagnostic->position = *position;
diagnostic->isTextSource = false;
memset(diagnostic->error, 0, length);
- strncpy(diagnostic->error, message, length);
+ strcpy(diagnostic->error, message);
return diagnostic;
}
diff --git a/source/disassemble.cpp b/source/disassemble.cpp
index c553988f..1d61b9f9 100644
--- a/source/disassemble.cpp
+++ b/source/disassemble.cpp
@@ -17,6 +17,8 @@
// This file contains a disassembler: It converts a SPIR-V binary
// to text.
+#include "source/disassemble.h"
+
#include <algorithm>
#include <cassert>
#include <cstring>
@@ -28,9 +30,7 @@
#include "source/assembly_grammar.h"
#include "source/binary.h"
#include "source/diagnostic.h"
-#include "source/disassemble.h"
#include "source/ext_inst.h"
-#include "source/name_mapper.h"
#include "source/opcode.h"
#include "source/parsed_operand.h"
#include "source/print.h"
@@ -40,29 +40,21 @@
#include "source/util/make_unique.h"
#include "spirv-tools/libspirv.h"
+namespace spvtools {
namespace {
// A Disassembler instance converts a SPIR-V binary to its assembly
// representation.
class Disassembler {
public:
- Disassembler(const spvtools::AssemblyGrammar& grammar, uint32_t options,
- spvtools::NameMapper name_mapper)
- : grammar_(grammar),
- print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)),
- color_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options)),
- indent_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_INDENT, options)
- ? kStandardIndent
- : 0),
- comment_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COMMENT, options)),
+ Disassembler(const AssemblyGrammar& grammar, uint32_t options,
+ NameMapper name_mapper)
+ : print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)),
text_(),
out_(print_ ? out_stream() : out_stream(text_)),
- stream_(out_.get()),
+ instruction_disassembler_(grammar, out_.get(), options, name_mapper),
header_(!spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER, options)),
- show_byte_offset_(spvIsInBitfield(
- SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET, options)),
- byte_offset_(0),
- name_mapper_(std::move(name_mapper)) {}
+ byte_offset_(0) {}
// Emits the assembly header for the module, and sets up internal state
// so subsequent callbacks can handle the cases where the entire module
@@ -78,56 +70,13 @@ class Disassembler {
spv_result_t SaveTextResult(spv_text* text_result) const;
private:
- enum { kStandardIndent = 15 };
-
- using out_stream = spvtools::out_stream;
-
- // Emits an operand for the given instruction, where the instruction
- // is at offset words from the start of the binary.
- void EmitOperand(const spv_parsed_instruction_t& inst,
- const uint16_t operand_index);
-
- // Emits a mask expression for the given mask word of the specified type.
- void EmitMaskOperand(const spv_operand_type_t type, const uint32_t word);
-
- // Resets the output color, if color is turned on.
- void ResetColor() {
- if (color_) out_.get() << spvtools::clr::reset{print_};
- }
- // Sets the output to grey, if color is turned on.
- void SetGrey() {
- if (color_) out_.get() << spvtools::clr::grey{print_};
- }
- // Sets the output to blue, if color is turned on.
- void SetBlue() {
- if (color_) out_.get() << spvtools::clr::blue{print_};
- }
- // Sets the output to yellow, if color is turned on.
- void SetYellow() {
- if (color_) out_.get() << spvtools::clr::yellow{print_};
- }
- // Sets the output to red, if color is turned on.
- void SetRed() {
- if (color_) out_.get() << spvtools::clr::red{print_};
- }
- // Sets the output to green, if color is turned on.
- void SetGreen() {
- if (color_) out_.get() << spvtools::clr::green{print_};
- }
-
- const spvtools::AssemblyGrammar& grammar_;
const bool print_; // Should we also print to the standard output stream?
- const bool color_; // Should we print in colour?
- const int indent_; // How much to indent. 0 means don't indent
- const int comment_; // Should we comment the source
spv_endianness_t endian_; // The detected endianness of the binary.
std::stringstream text_; // Captures the text, if not printing.
out_stream out_; // The Output stream. Either to text_ or standard output.
- std::ostream& stream_; // The output std::stream.
- const bool header_; // Should we output header as the leading comment?
- const bool show_byte_offset_; // Should we print byte offset, in hex?
- size_t byte_offset_; // The number of bytes processed so far.
- spvtools::NameMapper name_mapper_;
+ disassemble::InstructionDisassembler instruction_disassembler_;
+ const bool header_; // Should we output header as the leading comment?
+ size_t byte_offset_; // The number of bytes processed so far.
bool inserted_decoration_space_ = false;
bool inserted_debug_space_ = false;
bool inserted_type_space_ = false;
@@ -139,21 +88,11 @@ spv_result_t Disassembler::HandleHeader(spv_endianness_t endian,
endian_ = endian;
if (header_) {
- const char* generator_tool =
- spvGeneratorStr(SPV_GENERATOR_TOOL_PART(generator));
- stream_ << "; SPIR-V\n"
- << "; Version: " << SPV_SPIRV_VERSION_MAJOR_PART(version) << "."
- << SPV_SPIRV_VERSION_MINOR_PART(version) << "\n"
- << "; Generator: " << generator_tool;
- // For unknown tools, print the numeric tool value.
- if (0 == strcmp("Unknown", generator_tool)) {
- stream_ << "(" << SPV_GENERATOR_TOOL_PART(generator) << ")";
- }
- // Print the miscellaneous part of the generator word on the same
- // line as the tool name.
- stream_ << "; " << SPV_GENERATOR_MISC_PART(generator) << "\n"
- << "; Bound: " << id_bound << "\n"
- << "; Schema: " << schema << "\n";
+ instruction_disassembler_.EmitHeaderSpirv();
+ instruction_disassembler_.EmitHeaderVersion(version);
+ instruction_disassembler_.EmitHeaderGenerator(generator);
+ instruction_disassembler_.EmitHeaderIdBound(id_bound);
+ instruction_disassembler_.EmitHeaderSchema(schema);
}
byte_offset_ = SPV_INDEX_INSTRUCTION * sizeof(uint32_t);
@@ -163,31 +102,149 @@ spv_result_t Disassembler::HandleHeader(spv_endianness_t endian,
spv_result_t Disassembler::HandleInstruction(
const spv_parsed_instruction_t& inst) {
- auto opcode = static_cast<SpvOp>(inst.opcode);
- if (comment_ && opcode == SpvOpFunction) {
- stream_ << std::endl;
- stream_ << std::string(indent_, ' ');
- stream_ << "; Function " << name_mapper_(inst.result_id) << std::endl;
- }
- if (comment_ && !inserted_decoration_space_ &&
- spvOpcodeIsDecoration(opcode)) {
- inserted_decoration_space_ = true;
- stream_ << std::endl;
- stream_ << std::string(indent_, ' ');
- stream_ << "; Annotations" << std::endl;
+ instruction_disassembler_.EmitSectionComment(inst, inserted_decoration_space_,
+ inserted_debug_space_,
+ inserted_type_space_);
+
+ instruction_disassembler_.EmitInstruction(inst, byte_offset_);
+
+ byte_offset_ += inst.num_words * sizeof(uint32_t);
+
+ return SPV_SUCCESS;
+}
+
+spv_result_t Disassembler::SaveTextResult(spv_text* text_result) const {
+ if (!print_) {
+ size_t length = text_.str().size();
+ char* str = new char[length + 1];
+ if (!str) return SPV_ERROR_OUT_OF_MEMORY;
+ strncpy(str, text_.str().c_str(), length + 1);
+ spv_text text = new spv_text_t();
+ if (!text) {
+ delete[] str;
+ return SPV_ERROR_OUT_OF_MEMORY;
+ }
+ text->str = str;
+ text->length = length;
+ *text_result = text;
}
- if (comment_ && !inserted_debug_space_ && spvOpcodeIsDebug(opcode)) {
- inserted_debug_space_ = true;
- stream_ << std::endl;
- stream_ << std::string(indent_, ' ');
- stream_ << "; Debug Information" << std::endl;
+ return SPV_SUCCESS;
+}
+
+spv_result_t DisassembleHeader(void* user_data, spv_endianness_t endian,
+ uint32_t /* magic */, uint32_t version,
+ uint32_t generator, uint32_t id_bound,
+ uint32_t schema) {
+ assert(user_data);
+ auto disassembler = static_cast<Disassembler*>(user_data);
+ return disassembler->HandleHeader(endian, version, generator, id_bound,
+ schema);
+}
+
+spv_result_t DisassembleInstruction(
+ void* user_data, const spv_parsed_instruction_t* parsed_instruction) {
+ assert(user_data);
+ auto disassembler = static_cast<Disassembler*>(user_data);
+ return disassembler->HandleInstruction(*parsed_instruction);
+}
+
+// Simple wrapper class to provide extra data necessary for targeted
+// instruction disassembly.
+class WrappedDisassembler {
+ public:
+ WrappedDisassembler(Disassembler* dis, const uint32_t* binary, size_t wc)
+ : disassembler_(dis), inst_binary_(binary), word_count_(wc) {}
+
+ Disassembler* disassembler() { return disassembler_; }
+ const uint32_t* inst_binary() const { return inst_binary_; }
+ size_t word_count() const { return word_count_; }
+
+ private:
+ Disassembler* disassembler_;
+ const uint32_t* inst_binary_;
+ const size_t word_count_;
+};
+
+spv_result_t DisassembleTargetHeader(void* user_data, spv_endianness_t endian,
+ uint32_t /* magic */, uint32_t version,
+ uint32_t generator, uint32_t id_bound,
+ uint32_t schema) {
+ assert(user_data);
+ auto wrapped = static_cast<WrappedDisassembler*>(user_data);
+ return wrapped->disassembler()->HandleHeader(endian, version, generator,
+ id_bound, schema);
+}
+
+spv_result_t DisassembleTargetInstruction(
+ void* user_data, const spv_parsed_instruction_t* parsed_instruction) {
+ assert(user_data);
+ auto wrapped = static_cast<WrappedDisassembler*>(user_data);
+ // Check if this is the instruction we want to disassemble.
+ if (wrapped->word_count() == parsed_instruction->num_words &&
+ std::equal(wrapped->inst_binary(),
+ wrapped->inst_binary() + wrapped->word_count(),
+ parsed_instruction->words)) {
+ // Found the target instruction. Disassemble it and signal that we should
+ // stop searching so we don't output the same instruction again.
+ if (auto error =
+ wrapped->disassembler()->HandleInstruction(*parsed_instruction))
+ return error;
+ return SPV_REQUESTED_TERMINATION;
}
- if (comment_ && !inserted_type_space_ && spvOpcodeGeneratesType(opcode)) {
- inserted_type_space_ = true;
- stream_ << std::endl;
- stream_ << std::string(indent_, ' ');
- stream_ << "; Types, variables and constants" << std::endl;
+ return SPV_SUCCESS;
+}
+
+constexpr int kStandardIndent = 15;
+} // namespace
+
+namespace disassemble {
+InstructionDisassembler::InstructionDisassembler(const AssemblyGrammar& grammar,
+ std::ostream& stream,
+ uint32_t options,
+ NameMapper name_mapper)
+ : grammar_(grammar),
+ stream_(stream),
+ print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)),
+ color_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options)),
+ indent_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_INDENT, options)
+ ? kStandardIndent
+ : 0),
+ comment_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COMMENT, options)),
+ show_byte_offset_(
+ spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET, options)),
+ name_mapper_(std::move(name_mapper)) {}
+
+void InstructionDisassembler::EmitHeaderSpirv() { stream_ << "; SPIR-V\n"; }
+
+void InstructionDisassembler::EmitHeaderVersion(uint32_t version) {
+ stream_ << "; Version: " << SPV_SPIRV_VERSION_MAJOR_PART(version) << "."
+ << SPV_SPIRV_VERSION_MINOR_PART(version) << "\n";
+}
+
+void InstructionDisassembler::EmitHeaderGenerator(uint32_t generator) {
+ const char* generator_tool =
+ spvGeneratorStr(SPV_GENERATOR_TOOL_PART(generator));
+ stream_ << "; Generator: " << generator_tool;
+ // For unknown tools, print the numeric tool value.
+ if (0 == strcmp("Unknown", generator_tool)) {
+ stream_ << "(" << SPV_GENERATOR_TOOL_PART(generator) << ")";
}
+ // Print the miscellaneous part of the generator word on the same
+ // line as the tool name.
+ stream_ << "; " << SPV_GENERATOR_MISC_PART(generator) << "\n";
+}
+
+void InstructionDisassembler::EmitHeaderIdBound(uint32_t id_bound) {
+ stream_ << "; Bound: " << id_bound << "\n";
+}
+
+void InstructionDisassembler::EmitHeaderSchema(uint32_t schema) {
+ stream_ << "; Schema: " << schema << "\n";
+}
+
+void InstructionDisassembler::EmitInstruction(
+ const spv_parsed_instruction_t& inst, size_t inst_byte_offset) {
+ auto opcode = static_cast<SpvOp>(inst.opcode);
if (inst.result_id) {
SetBlue();
@@ -222,20 +279,45 @@ spv_result_t Disassembler::HandleInstruction(
auto saved_flags = stream_.flags();
auto saved_fill = stream_.fill();
stream_ << " ; 0x" << std::setw(8) << std::hex << std::setfill('0')
- << byte_offset_;
+ << inst_byte_offset;
stream_.flags(saved_flags);
stream_.fill(saved_fill);
ResetColor();
}
-
- byte_offset_ += inst.num_words * sizeof(uint32_t);
-
stream_ << "\n";
- return SPV_SUCCESS;
}
-void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst,
- const uint16_t operand_index) {
+void InstructionDisassembler::EmitSectionComment(
+ const spv_parsed_instruction_t& inst, bool& inserted_decoration_space,
+ bool& inserted_debug_space, bool& inserted_type_space) {
+ auto opcode = static_cast<SpvOp>(inst.opcode);
+ if (comment_ && opcode == SpvOpFunction) {
+ stream_ << std::endl;
+ stream_ << std::string(indent_, ' ');
+ stream_ << "; Function " << name_mapper_(inst.result_id) << std::endl;
+ }
+ if (comment_ && !inserted_decoration_space && spvOpcodeIsDecoration(opcode)) {
+ inserted_decoration_space = true;
+ stream_ << std::endl;
+ stream_ << std::string(indent_, ' ');
+ stream_ << "; Annotations" << std::endl;
+ }
+ if (comment_ && !inserted_debug_space && spvOpcodeIsDebug(opcode)) {
+ inserted_debug_space = true;
+ stream_ << std::endl;
+ stream_ << std::string(indent_, ' ');
+ stream_ << "; Debug Information" << std::endl;
+ }
+ if (comment_ && !inserted_type_space && spvOpcodeGeneratesType(opcode)) {
+ inserted_type_space = true;
+ stream_ << std::endl;
+ stream_ << std::string(indent_, ' ');
+ stream_ << "; Types, variables and constants" << std::endl;
+ }
+}
+
+void InstructionDisassembler::EmitOperand(const spv_parsed_instruction_t& inst,
+ const uint16_t operand_index) {
assert(operand_index < inst.num_operands);
const spv_parsed_operand_t& operand = inst.operands[operand_index];
const uint32_t word = inst.words[operand.offset];
@@ -277,19 +359,17 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst,
case SPV_OPERAND_TYPE_LITERAL_INTEGER:
case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: {
SetRed();
- spvtools::EmitNumericLiteral(&stream_, inst, operand);
+ EmitNumericLiteral(&stream_, inst, operand);
ResetColor();
} break;
case SPV_OPERAND_TYPE_LITERAL_STRING: {
stream_ << "\"";
SetGreen();
- // Strings are always little-endian, and null-terminated.
- // Write out the characters, escaping as needed, and without copying
- // the entire string.
- auto c_str = reinterpret_cast<const char*>(inst.words + operand.offset);
- for (auto p = c_str; *p; ++p) {
- if (*p == '"' || *p == '\\') stream_ << '\\';
- stream_ << *p;
+
+ std::string str = spvDecodeLiteralStringOperand(inst, operand_index);
+ for (char const& c : str) {
+ if (c == '"' || c == '\\') stream_ << '\\';
+ stream_ << c;
}
ResetColor();
stream_ << '"';
@@ -362,8 +442,8 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst,
ResetColor();
}
-void Disassembler::EmitMaskOperand(const spv_operand_type_t type,
- const uint32_t word) {
+void InstructionDisassembler::EmitMaskOperand(const spv_operand_type_t type,
+ const uint32_t word) {
// Scan the mask from least significant bit to most significant bit. For each
// set bit, emit the name of that bit. Separate multiple names with '|'.
uint32_t remaining_word = word;
@@ -389,88 +469,66 @@ void Disassembler::EmitMaskOperand(const spv_operand_type_t type,
}
}
-spv_result_t Disassembler::SaveTextResult(spv_text* text_result) const {
- if (!print_) {
- size_t length = text_.str().size();
- char* str = new char[length + 1];
- if (!str) return SPV_ERROR_OUT_OF_MEMORY;
- strncpy(str, text_.str().c_str(), length + 1);
- spv_text text = new spv_text_t();
- if (!text) {
- delete[] str;
- return SPV_ERROR_OUT_OF_MEMORY;
- }
- text->str = str;
- text->length = length;
- *text_result = text;
- }
- return SPV_SUCCESS;
+void InstructionDisassembler::ResetColor() {
+ if (color_) stream_ << spvtools::clr::reset{print_};
}
-
-spv_result_t DisassembleHeader(void* user_data, spv_endianness_t endian,
- uint32_t /* magic */, uint32_t version,
- uint32_t generator, uint32_t id_bound,
- uint32_t schema) {
- assert(user_data);
- auto disassembler = static_cast<Disassembler*>(user_data);
- return disassembler->HandleHeader(endian, version, generator, id_bound,
- schema);
+void InstructionDisassembler::SetGrey() {
+ if (color_) stream_ << spvtools::clr::grey{print_};
}
-
-spv_result_t DisassembleInstruction(
- void* user_data, const spv_parsed_instruction_t* parsed_instruction) {
- assert(user_data);
- auto disassembler = static_cast<Disassembler*>(user_data);
- return disassembler->HandleInstruction(*parsed_instruction);
+void InstructionDisassembler::SetBlue() {
+ if (color_) stream_ << spvtools::clr::blue{print_};
}
+void InstructionDisassembler::SetYellow() {
+ if (color_) stream_ << spvtools::clr::yellow{print_};
+}
+void InstructionDisassembler::SetRed() {
+ if (color_) stream_ << spvtools::clr::red{print_};
+}
+void InstructionDisassembler::SetGreen() {
+ if (color_) stream_ << spvtools::clr::green{print_};
+}
+} // namespace disassemble
+
+std::string spvInstructionBinaryToText(const spv_target_env env,
+ const uint32_t* instCode,
+ const size_t instWordCount,
+ const uint32_t* code,
+ const size_t wordCount,
+ const uint32_t options) {
+ spv_context context = spvContextCreate(env);
+ const AssemblyGrammar grammar(context);
+ if (!grammar.isValid()) {
+ spvContextDestroy(context);
+ return "";
+ }
-// Simple wrapper class to provide extra data necessary for targeted
-// instruction disassembly.
-class WrappedDisassembler {
- public:
- WrappedDisassembler(Disassembler* dis, const uint32_t* binary, size_t wc)
- : disassembler_(dis), inst_binary_(binary), word_count_(wc) {}
-
- Disassembler* disassembler() { return disassembler_; }
- const uint32_t* inst_binary() const { return inst_binary_; }
- size_t word_count() const { return word_count_; }
-
- private:
- Disassembler* disassembler_;
- const uint32_t* inst_binary_;
- const size_t word_count_;
-};
+ // Generate friendly names for Ids if requested.
+ std::unique_ptr<FriendlyNameMapper> friendly_mapper;
+ NameMapper name_mapper = GetTrivialNameMapper();
+ if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) {
+ friendly_mapper = MakeUnique<FriendlyNameMapper>(context, code, wordCount);
+ name_mapper = friendly_mapper->GetNameMapper();
+ }
-spv_result_t DisassembleTargetHeader(void* user_data, spv_endianness_t endian,
- uint32_t /* magic */, uint32_t version,
- uint32_t generator, uint32_t id_bound,
- uint32_t schema) {
- assert(user_data);
- auto wrapped = static_cast<WrappedDisassembler*>(user_data);
- return wrapped->disassembler()->HandleHeader(endian, version, generator,
- id_bound, schema);
-}
+ // Now disassemble!
+ Disassembler disassembler(grammar, options, name_mapper);
+ WrappedDisassembler wrapped(&disassembler, instCode, instWordCount);
+ spvBinaryParse(context, &wrapped, code, wordCount, DisassembleTargetHeader,
+ DisassembleTargetInstruction, nullptr);
-spv_result_t DisassembleTargetInstruction(
- void* user_data, const spv_parsed_instruction_t* parsed_instruction) {
- assert(user_data);
- auto wrapped = static_cast<WrappedDisassembler*>(user_data);
- // Check if this is the instruction we want to disassemble.
- if (wrapped->word_count() == parsed_instruction->num_words &&
- std::equal(wrapped->inst_binary(),
- wrapped->inst_binary() + wrapped->word_count(),
- parsed_instruction->words)) {
- // Found the target instruction. Disassemble it and signal that we should
- // stop searching so we don't output the same instruction again.
- if (auto error =
- wrapped->disassembler()->HandleInstruction(*parsed_instruction))
- return error;
- return SPV_REQUESTED_TERMINATION;
+ spv_text text = nullptr;
+ std::string output;
+ if (disassembler.SaveTextResult(&text) == SPV_SUCCESS) {
+ output.assign(text->str, text->str + text->length);
+ // Drop trailing newline characters.
+ while (!output.empty() && output.back() == '\n') output.pop_back();
}
- return SPV_SUCCESS;
-}
+ spvTextDestroy(text);
+ spvContextDestroy(context);
-} // namespace
+ return output;
+}
+} // namespace spvtools
spv_result_t spvBinaryToText(const spv_const_context context,
const uint32_t* code, const size_t wordCount,
@@ -495,53 +553,13 @@ spv_result_t spvBinaryToText(const spv_const_context context,
}
// Now disassemble!
- Disassembler disassembler(grammar, options, name_mapper);
- if (auto error = spvBinaryParse(&hijack_context, &disassembler, code,
- wordCount, DisassembleHeader,
- DisassembleInstruction, pDiagnostic)) {
+ spvtools::Disassembler disassembler(grammar, options, name_mapper);
+ if (auto error =
+ spvBinaryParse(&hijack_context, &disassembler, code, wordCount,
+ spvtools::DisassembleHeader,
+ spvtools::DisassembleInstruction, pDiagnostic)) {
return error;
}
return disassembler.SaveTextResult(pText);
}
-
-std::string spvtools::spvInstructionBinaryToText(const spv_target_env env,
- const uint32_t* instCode,
- const size_t instWordCount,
- const uint32_t* code,
- const size_t wordCount,
- const uint32_t options) {
- spv_context context = spvContextCreate(env);
- const spvtools::AssemblyGrammar grammar(context);
- if (!grammar.isValid()) {
- spvContextDestroy(context);
- return "";
- }
-
- // Generate friendly names for Ids if requested.
- std::unique_ptr<spvtools::FriendlyNameMapper> friendly_mapper;
- spvtools::NameMapper name_mapper = spvtools::GetTrivialNameMapper();
- if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) {
- friendly_mapper = spvtools::MakeUnique<spvtools::FriendlyNameMapper>(
- context, code, wordCount);
- name_mapper = friendly_mapper->GetNameMapper();
- }
-
- // Now disassemble!
- Disassembler disassembler(grammar, options, name_mapper);
- WrappedDisassembler wrapped(&disassembler, instCode, instWordCount);
- spvBinaryParse(context, &wrapped, code, wordCount, DisassembleTargetHeader,
- DisassembleTargetInstruction, nullptr);
-
- spv_text text = nullptr;
- std::string output;
- if (disassembler.SaveTextResult(&text) == SPV_SUCCESS) {
- output.assign(text->str, text->str + text->length);
- // Drop trailing newline characters.
- while (!output.empty() && output.back() == '\n') output.pop_back();
- }
- spvTextDestroy(text);
- spvContextDestroy(context);
-
- return output;
-}
diff --git a/source/disassemble.h b/source/disassemble.h
index ac357427..8eacb100 100644
--- a/source/disassemble.h
+++ b/source/disassemble.h
@@ -15,8 +15,10 @@
#ifndef SOURCE_DISASSEMBLE_H_
#define SOURCE_DISASSEMBLE_H_
+#include <iosfwd>
#include <string>
+#include "source/name_mapper.h"
#include "spirv-tools/libspirv.h"
namespace spvtools {
@@ -33,6 +35,63 @@ std::string spvInstructionBinaryToText(const spv_target_env env,
const size_t word_count,
const uint32_t options);
+class AssemblyGrammar;
+namespace disassemble {
+
+// Shared code with other tools (than the disassembler) that might need to
+// output disassembly. An InstructionDisassembler instance converts SPIR-V
+// binary for an instruction to its assembly representation.
+class InstructionDisassembler {
+ public:
+ InstructionDisassembler(const AssemblyGrammar& grammar, std::ostream& stream,
+ uint32_t options, NameMapper name_mapper);
+
+ // Emits the assembly header for the module.
+ void EmitHeaderSpirv();
+ void EmitHeaderVersion(uint32_t version);
+ void EmitHeaderGenerator(uint32_t generator);
+ void EmitHeaderIdBound(uint32_t id_bound);
+ void EmitHeaderSchema(uint32_t schema);
+
+ // Emits the assembly text for the given instruction.
+ void EmitInstruction(const spv_parsed_instruction_t& inst,
+ size_t inst_byte_offset);
+
+ // Emits a comment between different sections of the module.
+ void EmitSectionComment(const spv_parsed_instruction_t& inst,
+ bool& inserted_decoration_space,
+ bool& inserted_debug_space,
+ bool& inserted_type_space);
+
+ // Resets the output color, if color is turned on.
+ void ResetColor();
+ // Set the output color, if color is turned on.
+ void SetGrey();
+ void SetBlue();
+ void SetYellow();
+ void SetRed();
+ void SetGreen();
+
+ private:
+ // Emits an operand for the given instruction, where the instruction
+ // is at offset words from the start of the binary.
+ void EmitOperand(const spv_parsed_instruction_t& inst,
+ const uint16_t operand_index);
+
+ // Emits a mask expression for the given mask word of the specified type.
+ void EmitMaskOperand(const spv_operand_type_t type, const uint32_t word);
+
+ const spvtools::AssemblyGrammar& grammar_;
+ std::ostream& stream_;
+ const bool print_; // Should we also print to the standard output stream?
+ const bool color_; // Should we print in colour?
+ const int indent_; // How much to indent. 0 means don't indent
+ const int comment_; // Should we comment the source
+ const bool show_byte_offset_; // Should we print byte offset, in hex?
+ spvtools::NameMapper name_mapper_;
+};
+
+} // namespace disassemble
} // namespace spvtools
#endif // SOURCE_DISASSEMBLE_H_
diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp
index 795cb0f3..4e279545 100644
--- a/source/ext_inst.cpp
+++ b/source/ext_inst.cpp
@@ -29,6 +29,7 @@
#include "debuginfo.insts.inc"
#include "glsl.std.450.insts.inc"
#include "nonsemantic.clspvreflection.insts.inc"
+#include "nonsemantic.shader.debuginfo.100.insts.inc"
#include "opencl.debuginfo.100.insts.inc"
#include "opencl.std.insts.inc"
@@ -55,6 +56,9 @@ static const spv_ext_inst_group_t kGroups_1_0[] = {
debuginfo_entries},
{SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
ARRAY_SIZE(opencl_debuginfo_100_entries), opencl_debuginfo_100_entries},
+ {SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100,
+ ARRAY_SIZE(nonsemantic_shader_debuginfo_100_entries),
+ nonsemantic_shader_debuginfo_100_entries},
{SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION,
ARRAY_SIZE(nonsemantic_clspvreflection_entries),
nonsemantic_clspvreflection_entries},
@@ -92,6 +96,8 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable,
case SPV_ENV_UNIVERSAL_1_4:
case SPV_ENV_UNIVERSAL_1_5:
case SPV_ENV_VULKAN_1_2:
+ case SPV_ENV_UNIVERSAL_1_6:
+ case SPV_ENV_VULKAN_1_3:
*pExtInstTable = &kTable_1_0;
return SPV_SUCCESS;
default:
@@ -126,6 +132,9 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) {
if (!strcmp("OpenCL.DebugInfo.100", name)) {
return SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100;
}
+ if (!strcmp("NonSemantic.Shader.DebugInfo.100", name)) {
+ return SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100;
+ }
if (!strncmp("NonSemantic.ClspvReflection.", name, 28)) {
return SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION;
}
@@ -139,6 +148,7 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) {
bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) {
if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN ||
+ type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100 ||
type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) {
return true;
}
@@ -147,6 +157,7 @@ bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) {
bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type) {
if (type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
+ type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100 ||
type == SPV_EXT_INST_TYPE_DEBUGINFO) {
return true;
}
diff --git a/source/ext_inst.h b/source/ext_inst.h
index aff6e308..4027f4c3 100644
--- a/source/ext_inst.h
+++ b/source/ext_inst.h
@@ -27,7 +27,7 @@ bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type);
// Returns true if the extended instruction set is debug info
bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type);
-// Finds the named extented instruction of the given type in the given extended
+// Finds the named extended instruction of the given type in the given extended
// instruction table. On success, returns SPV_SUCCESS and writes a handle of
// the instruction entry into *entry.
spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table,
@@ -35,7 +35,7 @@ spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table,
const char* name,
spv_ext_inst_desc* entry);
-// Finds the extented instruction of the given type in the given extended
+// Finds the extended instruction of the given type in the given extended
// instruction table by value. On success, returns SPV_SUCCESS and writes a
// handle of the instruction entry into *entry.
spv_result_t spvExtInstTableValueLookup(const spv_ext_inst_table table,
diff --git a/source/extensions.cpp b/source/extensions.cpp
index a94db273..049a3ad1 100644
--- a/source/extensions.cpp
+++ b/source/extensions.cpp
@@ -18,6 +18,7 @@
#include <sstream>
#include <string>
+#include "source/binary.h"
#include "source/enum_string_mapping.h"
namespace spvtools {
@@ -30,8 +31,9 @@ std::string GetExtensionString(const spv_parsed_instruction_t* inst) {
const auto& operand = inst->operands[0];
assert(operand.type == SPV_OPERAND_TYPE_LITERAL_STRING);
assert(inst->num_words > operand.offset);
+ (void)operand; /* No unused variables in release builds. */
- return reinterpret_cast<const char*>(inst->words + operand.offset);
+ return spvDecodeLiteralStringOperand(*inst, 0);
}
std::string ExtensionSetToString(const ExtensionSet& extensions) {
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index a8061662..dd674dd0 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -124,6 +124,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_swap_functions.h
fuzzer_pass_toggle_access_chain_instruction.h
fuzzer_pass_wrap_regions_in_selections.h
+ fuzzer_pass_wrap_vector_synonym.h
fuzzer_util.h
id_use_descriptor.h
instruction_descriptor.h
@@ -230,6 +231,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_vector_shuffle.h
transformation_wrap_early_terminator_in_function.h
transformation_wrap_region_in_selection.h
+ transformation_wrap_vector_synonym.h
uniform_buffer_element_descriptor.h
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
@@ -319,6 +321,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_swap_functions.cpp
fuzzer_pass_toggle_access_chain_instruction.cpp
fuzzer_pass_wrap_regions_in_selections.cpp
+ fuzzer_pass_wrap_vector_synonym.cpp
fuzzer_util.cpp
id_use_descriptor.cpp
instruction_descriptor.cpp
@@ -423,6 +426,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_vector_shuffle.cpp
transformation_wrap_early_terminator_in_function.cpp
transformation_wrap_region_in_selection.cpp
+ transformation_wrap_vector_synonym.cpp
uniform_buffer_element_descriptor.cpp
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
)
diff --git a/source/fuzz/available_instructions.cpp b/source/fuzz/available_instructions.cpp
index e25ed900..0db8b207 100644
--- a/source/fuzz/available_instructions.cpp
+++ b/source/fuzz/available_instructions.cpp
@@ -44,7 +44,7 @@ AvailableInstructions::AvailableInstructions(
// Consider every reachable block in the function.
auto dominator_analysis = ir_context->GetDominatorAnalysis(&function);
for (auto& block : function) {
- if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, &block)) {
+ if (!ir_context->IsReachable(block)) {
// The block is not reachable.
continue;
}
diff --git a/source/fuzz/fact_manager/fact_manager.h b/source/fuzz/fact_manager/fact_manager.h
index 5cf5b18b..ce28ae4c 100644
--- a/source/fuzz/fact_manager/fact_manager.h
+++ b/source/fuzz/fact_manager/fact_manager.h
@@ -163,7 +163,7 @@ class FactManager {
std::vector<const protobufs::DataDescriptor*> GetSynonymsForDataDescriptor(
const protobufs::DataDescriptor& data_descriptor) const;
- // Returns true if and ony if |data_descriptor1| and |data_descriptor2| are
+ // Returns true if and only if |data_descriptor1| and |data_descriptor2| are
// known to be synonymous.
bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1,
const protobufs::DataDescriptor& data_descriptor2) const;
@@ -174,7 +174,7 @@ class FactManager {
//==============================
// Querying facts about dead blocks
- // Returns true if and ony if |block_id| is the id of a block known to be
+ // Returns true if and only if |block_id| is the id of a block known to be
// dynamically unreachable.
bool BlockIsDead(uint32_t block_id) const;
@@ -184,7 +184,7 @@ class FactManager {
//==============================
// Querying facts about livesafe function
- // Returns true if and ony if |function_id| is the id of a function known
+ // Returns true if and only if |function_id| is the id of a function known
// to be livesafe.
bool FunctionIsLivesafe(uint32_t function_id) const;
@@ -194,7 +194,7 @@ class FactManager {
//==============================
// Querying facts about irrelevant values
- // Returns true if and ony if the value of the pointee associated with
+ // Returns true if and only if the value of the pointee associated with
// |pointer_id| is irrelevant.
bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index fe88a55b..9838e64f 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -90,6 +90,7 @@
#include "source/fuzz/fuzzer_pass_swap_functions.h"
#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
#include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h"
+#include "source/fuzz/fuzzer_pass_wrap_vector_synonym.h"
#include "source/fuzz/pass_management/repeated_pass_manager.h"
#include "source/fuzz/pass_management/repeated_pass_recommender_standard.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
@@ -109,7 +110,8 @@ Fuzzer::Fuzzer(std::unique_ptr<opt::IRContext> ir_context,
bool enable_all_passes,
RepeatedPassStrategy repeated_pass_strategy,
bool validate_after_each_fuzzer_pass,
- spv_validator_options validator_options)
+ spv_validator_options validator_options,
+ bool ignore_inapplicable_transformations /* = true */)
: consumer_(std::move(consumer)),
enable_all_passes_(enable_all_passes),
validate_after_each_fuzzer_pass_(validate_after_each_fuzzer_pass),
@@ -123,7 +125,9 @@ Fuzzer::Fuzzer(std::unique_ptr<opt::IRContext> ir_context,
pass_instances_(),
repeated_pass_recommender_(nullptr),
repeated_pass_manager_(nullptr),
- final_passes_() {
+ final_passes_(),
+ ignore_inapplicable_transformations_(
+ ignore_inapplicable_transformations) {
assert(ir_context_ && "IRContext is not initialized");
assert(fuzzer_context_ && "FuzzerContext is not initialized");
assert(transformation_context_ && "TransformationContext is not initialized");
@@ -214,6 +218,7 @@ Fuzzer::Fuzzer(std::unique_ptr<opt::IRContext> ir_context,
MaybeAddRepeatedPass<FuzzerPassSwapBranchConditionalOperands>(
&pass_instances_);
MaybeAddRepeatedPass<FuzzerPassWrapRegionsInSelections>(&pass_instances_);
+ MaybeAddRepeatedPass<FuzzerPassWrapVectorSynonym>(&pass_instances_);
// There is a theoretical possibility that no pass instances were created
// until now; loop again if so.
} while (pass_instances_.GetPasses().empty());
@@ -255,7 +260,8 @@ void Fuzzer::MaybeAddRepeatedPass(uint32_t percentage_chance_of_adding_pass,
fuzzer_context_->ChoosePercentage(percentage_chance_of_adding_pass)) {
pass_instances->SetPass(MakeUnique<FuzzerPassT>(
ir_context_.get(), transformation_context_.get(), fuzzer_context_.get(),
- &transformation_sequence_out_, std::forward<Args>(extra_args)...));
+ &transformation_sequence_out_, ignore_inapplicable_transformations_,
+ std::forward<Args>(extra_args)...));
}
}
@@ -265,7 +271,8 @@ void Fuzzer::MaybeAddFinalPass(std::vector<std::unique_ptr<FuzzerPass>>* passes,
if (enable_all_passes_ || fuzzer_context_->ChooseEven()) {
passes->push_back(MakeUnique<FuzzerPassT>(
ir_context_.get(), transformation_context_.get(), fuzzer_context_.get(),
- &transformation_sequence_out_, std::forward<Args>(extra_args)...));
+ &transformation_sequence_out_, ignore_inapplicable_transformations_,
+ std::forward<Args>(extra_args)...));
}
}
diff --git a/source/fuzz/fuzzer.h b/source/fuzz/fuzzer.h
index 1e7d6860..4c38977f 100644
--- a/source/fuzz/fuzzer.h
+++ b/source/fuzz/fuzzer.h
@@ -62,7 +62,8 @@ class Fuzzer {
const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy,
bool validate_after_each_fuzzer_pass,
- spv_validator_options validator_options);
+ spv_validator_options validator_options,
+ bool ignore_inapplicable_transformations = true);
// Disables copy/move constructor/assignment operations.
Fuzzer(const Fuzzer&) = delete;
@@ -187,6 +188,12 @@ class Fuzzer {
// Some passes that it does not make sense to apply repeatedly, as they do not
// unlock other passes.
std::vector<std::unique_ptr<FuzzerPass>> final_passes_;
+
+ // When set, this flag causes inapplicable transformations that should be
+ // applicable by construction to be ignored. This is useful when the fuzzer
+ // is being deployed at scale to test a SPIR-V processing tool, and where it
+ // is desirable to ignore bugs in the fuzzer itself.
+ const bool ignore_inapplicable_transformations_;
};
} // namespace fuzz
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index 9e9650f6..7e34cc33 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -21,7 +21,7 @@ namespace fuzz {
namespace {
-// An offset between the the module's id bound and the minimum fresh id.
+// An offset between the module's id bound and the minimum fresh id.
//
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541): consider
// the case where the maximum id bound is reached.
@@ -42,6 +42,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherPassToPassLoop = {50,
const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherStructField = {20,
90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingArrayOrStructType = {20, 90};
+const std::pair<uint32_t, uint32_t> KChanceOfAddingAtomicLoad = {30, 90};
+const std::pair<uint32_t, uint32_t> KChanceOfAddingAtomicStore = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingBitInstructionSynonym = {5,
20};
const std::pair<uint32_t, uint32_t>
@@ -162,6 +164,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfTogglingAccessChainInstruction = {
20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfWrappingRegionInSelection = {70,
90};
+const std::pair<uint32_t, uint32_t> kChanceOfWrappingVectorSynonym = {10, 90};
// Default limits for various quantities that are chosen during fuzzing.
// Keep them in alphabetical order.
@@ -215,6 +218,10 @@ FuzzerContext::FuzzerContext(std::unique_ptr<RandomGenerator> random_generator,
ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField);
chance_of_adding_array_or_struct_type_ =
ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType);
+ chance_of_adding_atomic_load_ =
+ ChooseBetweenMinAndMax(KChanceOfAddingAtomicLoad);
+ chance_of_adding_atomic_store_ =
+ ChooseBetweenMinAndMax(KChanceOfAddingAtomicStore);
chance_of_adding_bit_instruction_synonym_ =
ChooseBetweenMinAndMax(kChanceOfAddingBitInstructionSynonym);
chance_of_adding_both_branches_when_replacing_opselect_ =
@@ -369,6 +376,8 @@ FuzzerContext::FuzzerContext(std::unique_ptr<RandomGenerator> random_generator,
ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction);
chance_of_wrapping_region_in_selection_ =
ChooseBetweenMinAndMax(kChanceOfWrappingRegionInSelection);
+ chance_of_wrapping_vector_synonym_ =
+ ChooseBetweenMinAndMax(kChanceOfWrappingVectorSynonym);
}
FuzzerContext::~FuzzerContext() = default;
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index ef2cc2c5..77a5d400 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -142,6 +142,12 @@ class FuzzerContext {
uint32_t GetChanceOfAddingArrayOrStructType() const {
return chance_of_adding_array_or_struct_type_;
}
+ uint32_t GetChanceOfAddingAtomicLoad() const {
+ return chance_of_adding_atomic_load_;
+ }
+ uint32_t GetChanceOfAddingAtomicStore() const {
+ return chance_of_adding_atomic_store_;
+ }
uint32_t GetChanceOfAddingBitInstructionSynonym() const {
return chance_of_adding_bit_instruction_synonym_;
}
@@ -381,6 +387,10 @@ class FuzzerContext {
return chance_of_wrapping_region_in_selection_;
}
+ uint32_t GetChanceOfWrappingVectorSynonym() const {
+ return chance_of_wrapping_vector_synonym_;
+ }
+
// Other functions to control transformations. Keep them in alphabetical
// order.
uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() const {
@@ -425,6 +435,9 @@ class FuzzerContext {
uint32_t GetRandomIndexForCompositeInsert(uint32_t number_of_components) {
return random_generator_->RandomUint32(number_of_components);
}
+ uint32_t GetRandomIndexForWrappingVector(uint32_t vector_width) {
+ return random_generator_->RandomUint32(vector_width);
+ }
int64_t GetRandomValueForStepConstantInLoop() {
return random_generator_->RandomUint64(UINT64_MAX);
}
@@ -462,6 +475,9 @@ class FuzzerContext {
// Ensure that the number of unused components is non-zero.
return random_generator_->RandomUint32(max_unused_component_count) + 1;
}
+ uint32_t GetWidthOfWrappingVector() {
+ return 2 + random_generator_->RandomUint32(3);
+ }
bool GoDeeperInConstantObfuscation(uint32_t depth) {
return go_deeper_in_constant_obfuscation_(depth, random_generator_.get());
}
@@ -482,6 +498,8 @@ class FuzzerContext {
uint32_t chance_of_adding_another_pass_to_pass_loop_;
uint32_t chance_of_adding_another_struct_field_;
uint32_t chance_of_adding_array_or_struct_type_;
+ uint32_t chance_of_adding_atomic_load_;
+ uint32_t chance_of_adding_atomic_store_;
uint32_t chance_of_adding_bit_instruction_synonym_;
uint32_t chance_of_adding_both_branches_when_replacing_opselect_;
uint32_t chance_of_adding_composite_extract_;
@@ -562,6 +580,7 @@ class FuzzerContext {
uint32_t chance_of_swapping_functions_;
uint32_t chance_of_toggling_access_chain_instruction_;
uint32_t chance_of_wrapping_region_in_selection_;
+ uint32_t chance_of_wrapping_vector_synonym_;
// Limits associated with various quantities for which random values are
// chosen during fuzzing.
diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp
index f67efc66..6a879851 100644
--- a/source/fuzz/fuzzer_pass.cpp
+++ b/source/fuzz/fuzzer_pass.cpp
@@ -43,11 +43,14 @@ namespace fuzz {
FuzzerPass::FuzzerPass(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: ir_context_(ir_context),
transformation_context_(transformation_context),
fuzzer_context_(fuzzer_context),
- transformations_(transformations) {}
+ transformations_(transformations),
+ ignore_inapplicable_transformations_(
+ ignore_inapplicable_transformations) {}
FuzzerPass::~FuzzerPass() = default;
@@ -111,10 +114,8 @@ void FuzzerPass::ForEachInstructionWithInstructionDescriptor(
// module.
std::vector<opt::BasicBlock*> reachable_blocks;
- const auto* dominator_analysis =
- GetIRContext()->GetDominatorAnalysis(function);
for (auto& block : *function) {
- if (dominator_analysis->IsReachable(&block)) {
+ if (GetIRContext()->IsReachable(block)) {
reachable_blocks.push_back(&block);
}
}
@@ -185,9 +186,23 @@ void FuzzerPass::ForEachInstructionWithInstructionDescriptor(
}
void FuzzerPass::ApplyTransformation(const Transformation& transformation) {
- assert(transformation.IsApplicable(GetIRContext(),
- *GetTransformationContext()) &&
- "Transformation should be applicable by construction.");
+ if (ignore_inapplicable_transformations_) {
+ // If an applicable-by-construction transformation turns out to be
+ // inapplicable, this is a bug in the fuzzer. However, when deploying the
+ // fuzzer at scale for finding bugs in SPIR-V processing tools it is
+ // desirable to silently ignore such bugs. This code path caters for that
+ // scenario.
+ if (!transformation.IsApplicable(GetIRContext(),
+ *GetTransformationContext())) {
+ return;
+ }
+ } else {
+ // This code path caters for debugging bugs in the fuzzer, where an
+ // applicable-by-construction transformation turns out to be inapplicable.
+ assert(transformation.IsApplicable(GetIRContext(),
+ *GetTransformationContext()) &&
+ "Transformation should be applicable by construction.");
+ }
transformation.Apply(GetIRContext(), GetTransformationContext());
auto transformation_message = transformation.ToMessage();
assert(transformation_message.transformation_case() !=
@@ -246,7 +261,7 @@ uint32_t FuzzerPass::FindOrCreateFloatType(uint32_t width) {
uint32_t FuzzerPass::FindOrCreateFunctionType(
uint32_t return_type_id, const std::vector<uint32_t>& argument_id) {
- // FindFunctionType has a sigle argument for OpTypeFunction operands
+ // FindFunctionType has a single argument for OpTypeFunction operands
// so we will have to copy them all in this vector
std::vector<uint32_t> type_ids(argument_id.size() + 1);
type_ids[0] = return_type_id;
diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h
index ec254855..2655b540 100644
--- a/source/fuzz/fuzzer_pass.h
+++ b/source/fuzz/fuzzer_pass.h
@@ -33,7 +33,8 @@ class FuzzerPass {
FuzzerPass(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
virtual ~FuzzerPass();
@@ -321,6 +322,10 @@ class FuzzerPass {
TransformationContext* transformation_context_;
FuzzerContext* fuzzer_context_;
protobufs::TransformationSequence* transformations_;
+ // If set, then transformations that should be applicable by construction are
+ // still tested for applicability, and ignored if they turn out to be
+ // inapplicable. Otherwise, applicability by construction is asserted.
+ const bool ignore_inapplicable_transformations_;
};
} // namespace fuzz
diff --git a/source/fuzz/fuzzer_pass_add_access_chains.cpp b/source/fuzz/fuzzer_pass_add_access_chains.cpp
index c498642c..39f193d9 100644
--- a/source/fuzz/fuzzer_pass_add_access_chains.cpp
+++ b/source/fuzz/fuzzer_pass_add_access_chains.cpp
@@ -23,9 +23,10 @@ namespace fuzz {
FuzzerPassAddAccessChains::FuzzerPassAddAccessChains(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddAccessChains::Apply() {
ForEachInstructionWithInstructionDescriptor(
diff --git a/source/fuzz/fuzzer_pass_add_access_chains.h b/source/fuzz/fuzzer_pass_add_access_chains.h
index e80c2c6c..5e209cd2 100644
--- a/source/fuzz/fuzzer_pass_add_access_chains.h
+++ b/source/fuzz/fuzzer_pass_add_access_chains.h
@@ -28,7 +28,8 @@ class FuzzerPassAddAccessChains : public FuzzerPass {
FuzzerPassAddAccessChains(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp
index 78abf5be..1b0d7b10 100644
--- a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp
@@ -24,9 +24,10 @@ namespace fuzz {
FuzzerPassAddBitInstructionSynonyms::FuzzerPassAddBitInstructionSynonyms(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddBitInstructionSynonyms::Apply() {
for (auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h
index 28f95779..38d81aa7 100644
--- a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h
+++ b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h
@@ -28,7 +28,8 @@ class FuzzerPassAddBitInstructionSynonyms : public FuzzerPass {
FuzzerPassAddBitInstructionSynonyms(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_add_composite_extract.cpp b/source/fuzz/fuzzer_pass_add_composite_extract.cpp
index 19f99024..dbbec0ca 100644
--- a/source/fuzz/fuzzer_pass_add_composite_extract.cpp
+++ b/source/fuzz/fuzzer_pass_add_composite_extract.cpp
@@ -26,9 +26,10 @@ namespace fuzz {
FuzzerPassAddCompositeExtract::FuzzerPassAddCompositeExtract(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddCompositeExtract::Apply() {
std::vector<const protobufs::DataDescriptor*> composite_synonyms;
diff --git a/source/fuzz/fuzzer_pass_add_composite_extract.h b/source/fuzz/fuzzer_pass_add_composite_extract.h
index 32ac190a..e7ed18af 100644
--- a/source/fuzz/fuzzer_pass_add_composite_extract.h
+++ b/source/fuzz/fuzzer_pass_add_composite_extract.h
@@ -27,7 +27,8 @@ class FuzzerPassAddCompositeExtract : public FuzzerPass {
FuzzerPassAddCompositeExtract(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_add_composite_inserts.cpp b/source/fuzz/fuzzer_pass_add_composite_inserts.cpp
index cf314d30..2ac12de4 100644
--- a/source/fuzz/fuzzer_pass_add_composite_inserts.cpp
+++ b/source/fuzz/fuzzer_pass_add_composite_inserts.cpp
@@ -25,9 +25,10 @@ namespace fuzz {
FuzzerPassAddCompositeInserts::FuzzerPassAddCompositeInserts(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddCompositeInserts::Apply() {
ForEachInstructionWithInstructionDescriptor(
diff --git a/source/fuzz/fuzzer_pass_add_composite_inserts.h b/source/fuzz/fuzzer_pass_add_composite_inserts.h
index 4d511f6c..d9f42d58 100644
--- a/source/fuzz/fuzzer_pass_add_composite_inserts.h
+++ b/source/fuzz/fuzzer_pass_add_composite_inserts.h
@@ -27,7 +27,8 @@ class FuzzerPassAddCompositeInserts : public FuzzerPass {
FuzzerPassAddCompositeInserts(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_add_composite_types.cpp b/source/fuzz/fuzzer_pass_add_composite_types.cpp
index 3dfbd690..af36ad06 100644
--- a/source/fuzz/fuzzer_pass_add_composite_types.cpp
+++ b/source/fuzz/fuzzer_pass_add_composite_types.cpp
@@ -24,9 +24,10 @@ namespace fuzz {
FuzzerPassAddCompositeTypes::FuzzerPassAddCompositeTypes(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddCompositeTypes::Apply() {
MaybeAddMissingVectorTypes();
diff --git a/source/fuzz/fuzzer_pass_add_composite_types.h b/source/fuzz/fuzzer_pass_add_composite_types.h
index 89d48f8a..f16c79e3 100644
--- a/source/fuzz/fuzzer_pass_add_composite_types.h
+++ b/source/fuzz/fuzzer_pass_add_composite_types.h
@@ -27,7 +27,8 @@ class FuzzerPassAddCompositeTypes : public FuzzerPass {
FuzzerPassAddCompositeTypes(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_add_copy_memory.cpp b/source/fuzz/fuzzer_pass_add_copy_memory.cpp
index b654927f..6551f49f 100644
--- a/source/fuzz/fuzzer_pass_add_copy_memory.cpp
+++ b/source/fuzz/fuzzer_pass_add_copy_memory.cpp
@@ -25,9 +25,10 @@ namespace fuzz {
FuzzerPassAddCopyMemory::FuzzerPassAddCopyMemory(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddCopyMemory::Apply() {
ForEachInstructionWithInstructionDescriptor(
diff --git a/source/fuzz/fuzzer_pass_add_copy_memory.h b/source/fuzz/fuzzer_pass_add_copy_memory.h
index 0f7db0c7..e258752c 100644
--- a/source/fuzz/fuzzer_pass_add_copy_memory.h
+++ b/source/fuzz/fuzzer_pass_add_copy_memory.h
@@ -27,7 +27,8 @@ class FuzzerPassAddCopyMemory : public FuzzerPass {
FuzzerPassAddCopyMemory(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_add_dead_blocks.cpp b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
index 8c166a20..82d53ebb 100644
--- a/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
+++ b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
@@ -31,9 +31,10 @@ const size_t kMaxTransformationsInOnePass = 100U;
FuzzerPassAddDeadBlocks::FuzzerPassAddDeadBlocks(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddDeadBlocks::Apply() {
// We iterate over all blocks in the module collecting up those at which we
diff --git a/source/fuzz/fuzzer_pass_add_dead_blocks.h b/source/fuzz/fuzzer_pass_add_dead_blocks.h
index a87c05c3..4567e87d 100644
--- a/source/fuzz/fuzzer_pass_add_dead_blocks.h
+++ b/source/fuzz/fuzzer_pass_add_dead_blocks.h
@@ -27,7 +27,8 @@ class FuzzerPassAddDeadBlocks : public FuzzerPass {
FuzzerPassAddDeadBlocks(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
index 0c18da90..c3664c87 100644
--- a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
+++ b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
@@ -24,9 +24,10 @@ namespace fuzz {
FuzzerPassAddDeadBreaks::FuzzerPassAddDeadBreaks(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddDeadBreaks::Apply() {
// We first collect up lots of possibly-applicable transformations.
diff --git a/source/fuzz/fuzzer_pass_add_dead_breaks.h b/source/fuzz/fuzzer_pass_add_dead_breaks.h
index d1086fc4..361c3464 100644
--- a/source/fuzz/fuzzer_pass_add_dead_breaks.h
+++ b/source/fuzz/fuzzer_pass_add_dead_breaks.h
@@ -26,7 +26,8 @@ class FuzzerPassAddDeadBreaks : public FuzzerPass {
FuzzerPassAddDeadBreaks(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/source/fuzz/fuzzer_pass_add_dead_continues.cpp
index 1ab40b73..38814811 100644
--- a/source/fuzz/fuzzer_pass_add_dead_continues.cpp
+++ b/source/fuzz/fuzzer_pass_add_dead_continues.cpp
@@ -24,9 +24,10 @@ namespace fuzz {
FuzzerPassAddDeadContinues::FuzzerPassAddDeadContinues(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddDeadContinues::Apply() {
// Consider every block in every function.
diff --git a/source/fuzz/fuzzer_pass_add_dead_continues.h b/source/fuzz/fuzzer_pass_add_dead_continues.h
index bf0009e4..4f1bd601 100644
--- a/source/fuzz/fuzzer_pass_add_dead_continues.h
+++ b/source/fuzz/fuzzer_pass_add_dead_continues.h
@@ -23,10 +23,11 @@ namespace fuzz {
// A fuzzer pass for adding dead continue edges to the module.
class FuzzerPassAddDeadContinues : public FuzzerPass {
public:
- FuzzerPassAddDeadContinues(
- opt::IRContext* ir_context, TransformationContext* transformation_context,
- FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ FuzzerPassAddDeadContinues(opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
index 15554b7a..4bbded8e 100644
--- a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
+++ b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
@@ -45,9 +45,10 @@ bool IsBitWidthSupported(opt::IRContext* ir_context, uint32_t bit_width) {
FuzzerPassAddEquationInstructions::FuzzerPassAddEquationInstructions(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddEquationInstructions::Apply() {
ForEachInstructionWithInstructionDescriptor(
diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.h b/source/fuzz/fuzzer_pass_add_equation_instructions.h
index dbec5bac..dc9a27b3 100644
--- a/source/fuzz/fuzzer_pass_add_equation_instructions.h
+++ b/source/fuzz/fuzzer_pass_add_equation_instructions.h
@@ -29,7 +29,8 @@ class FuzzerPassAddEquationInstructions : public FuzzerPass {
FuzzerPassAddEquationInstructions(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_add_function_calls.cpp b/source/fuzz/fuzzer_pass_add_function_calls.cpp
index 2240696b..033f4a27 100644
--- a/source/fuzz/fuzzer_pass_add_function_calls.cpp
+++ b/source/fuzz/fuzzer_pass_add_function_calls.cpp
@@ -26,9 +26,10 @@ namespace fuzz {
FuzzerPassAddFunctionCalls::FuzzerPassAddFunctionCalls(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddFunctionCalls::Apply() {
ForEachInstructionWithInstructionDescriptor(
diff --git a/source/fuzz/fuzzer_pass_add_function_calls.h b/source/fuzz/fuzzer_pass_add_function_calls.h
index 081510c1..80b03d7b 100644
--- a/source/fuzz/fuzzer_pass_add_function_calls.h
+++ b/source/fuzz/fuzzer_pass_add_function_calls.h
@@ -24,10 +24,11 @@ namespace fuzz {
// anywhere, and (b) any functions, from dead blocks.
class FuzzerPassAddFunctionCalls : public FuzzerPass {
public:
- FuzzerPassAddFunctionCalls(
- opt::IRContext* ir_context, TransformationContext* transformation_context,
- FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ FuzzerPassAddFunctionCalls(opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_add_global_variables.cpp b/source/fuzz/fuzzer_pass_add_global_variables.cpp
index 06797413..061f44d0 100644
--- a/source/fuzz/fuzzer_pass_add_global_variables.cpp
+++ b/source/fuzz/fuzzer_pass_add_global_variables.cpp
@@ -23,9 +23,10 @@ namespace fuzz {
FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddGlobalVariables::Apply() {
SpvStorageClass variable_storage_class = SpvStorageClassPrivate;
@@ -47,6 +48,10 @@ void FuzzerPassAddGlobalVariables::Apply() {
// These are the basic types that are available to this fuzzer pass.
auto& basic_types = basic_type_ids_and_pointers.first;
+ if (basic_types.empty()) {
+ // There are no basic types, so there is nothing this fuzzer pass can do.
+ return;
+ }
// These are the pointers to those basic types that are *initially* available
// to the fuzzer pass. The fuzzer pass might add pointer types in cases where
diff --git a/source/fuzz/fuzzer_pass_add_global_variables.h b/source/fuzz/fuzzer_pass_add_global_variables.h
index 3745c5c3..1496646e 100644
--- a/source/fuzz/fuzzer_pass_add_global_variables.h
+++ b/source/fuzz/fuzzer_pass_add_global_variables.h
@@ -27,7 +27,8 @@ class FuzzerPassAddGlobalVariables : public FuzzerPass {
FuzzerPassAddGlobalVariables(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp
index c10db720..19661d10 100644
--- a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp
+++ b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp
@@ -27,9 +27,10 @@ FuzzerPassAddImageSampleUnusedComponents::
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddImageSampleUnusedComponents::Apply() {
// SPIR-V module to help understand the transformation.
diff --git a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h
index f5dea8bf..1a278931 100644
--- a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h
+++ b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h
@@ -28,7 +28,8 @@ class FuzzerPassAddImageSampleUnusedComponents : public FuzzerPass {
FuzzerPassAddImageSampleUnusedComponents(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_add_loads.cpp b/source/fuzz/fuzzer_pass_add_loads.cpp
index 2e50da26..ab91543b 100644
--- a/source/fuzz/fuzzer_pass_add_loads.cpp
+++ b/source/fuzz/fuzzer_pass_add_loads.cpp
@@ -23,9 +23,10 @@ namespace fuzz {
FuzzerPassAddLoads::FuzzerPassAddLoads(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddLoads::Apply() {
ForEachInstructionWithInstructionDescriptor(
@@ -38,18 +39,22 @@ void FuzzerPassAddLoads::Apply() {
"The opcode of the instruction we might insert before must be "
"the same as the opcode in the descriptor for the instruction");
- // Check whether it is legitimate to insert a load before this
- // instruction.
- if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, inst_it)) {
- return;
- }
-
// Randomly decide whether to try inserting a load here.
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfAddingLoad())) {
return;
}
+ // Check whether it is legitimate to insert a load or atomic load before
+ // this instruction.
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, inst_it)) {
+ return;
+ }
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAtomicLoad,
+ inst_it)) {
+ return;
+ }
+
std::vector<opt::Instruction*> relevant_instructions =
FindAvailableInstructions(
function, block, inst_it,
@@ -79,13 +84,53 @@ void FuzzerPassAddLoads::Apply() {
return;
}
- // Choose a pointer at random, and create and apply a loading
- // transformation based on it.
- ApplyTransformation(TransformationLoad(
- GetFuzzerContext()->GetFreshId(),
+ auto chosen_instruction =
relevant_instructions[GetFuzzerContext()->RandomIndex(
- relevant_instructions)]
- ->result_id(),
+ relevant_instructions)];
+
+ bool is_atomic_load = false;
+ uint32_t memory_scope_id = 0;
+ uint32_t memory_semantics_id = 0;
+
+ auto storage_class = static_cast<SpvStorageClass>(
+ GetIRContext()
+ ->get_def_use_mgr()
+ ->GetDef(chosen_instruction->type_id())
+ ->GetSingleWordInOperand(0));
+
+ switch (storage_class) {
+ case SpvStorageClassStorageBuffer:
+ case SpvStorageClassPhysicalStorageBuffer:
+ case SpvStorageClassWorkgroup:
+ case SpvStorageClassCrossWorkgroup:
+ case SpvStorageClassAtomicCounter:
+ case SpvStorageClassImage:
+ if (GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfAddingAtomicLoad())) {
+ is_atomic_load = true;
+
+ memory_scope_id = FindOrCreateConstant(
+ {SpvScopeInvocation},
+ FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()),
+ false);
+
+ memory_semantics_id = FindOrCreateConstant(
+ {static_cast<uint32_t>(
+ fuzzerutil::GetMemorySemanticsForStorageClass(
+ storage_class))},
+ FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()),
+ false);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Create and apply the transformation.
+ ApplyTransformation(TransformationLoad(
+ GetFuzzerContext()->GetFreshId(), chosen_instruction->result_id(),
+ is_atomic_load, memory_scope_id, memory_semantics_id,
instruction_descriptor));
});
}
diff --git a/source/fuzz/fuzzer_pass_add_loads.h b/source/fuzz/fuzzer_pass_add_loads.h
index 3913c624..14460e7f 100644
--- a/source/fuzz/fuzzer_pass_add_loads.h
+++ b/source/fuzz/fuzzer_pass_add_loads.h
@@ -26,7 +26,8 @@ class FuzzerPassAddLoads : public FuzzerPass {
FuzzerPassAddLoads(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_add_local_variables.cpp b/source/fuzz/fuzzer_pass_add_local_variables.cpp
index a50b0b07..a4e739f8 100644
--- a/source/fuzz/fuzzer_pass_add_local_variables.cpp
+++ b/source/fuzz/fuzzer_pass_add_local_variables.cpp
@@ -24,9 +24,10 @@ namespace fuzz {
FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddLocalVariables::Apply() {
auto basic_type_ids_and_pointers =
@@ -34,6 +35,10 @@ void FuzzerPassAddLocalVariables::Apply() {
// These are the basic types that are available to this fuzzer pass.
auto& basic_types = basic_type_ids_and_pointers.first;
+ if (basic_types.empty()) {
+ // The pass cannot do anything if there are no basic types.
+ return;
+ }
// These are the pointers to those basic types that are *initially* available
// to the fuzzer pass. The fuzzer pass might add pointer types in cases where
diff --git a/source/fuzz/fuzzer_pass_add_local_variables.h b/source/fuzz/fuzzer_pass_add_local_variables.h
index d73dae29..c969ba06 100644
--- a/source/fuzz/fuzzer_pass_add_local_variables.h
+++ b/source/fuzz/fuzzer_pass_add_local_variables.h
@@ -27,7 +27,8 @@ class FuzzerPassAddLocalVariables : public FuzzerPass {
FuzzerPassAddLocalVariables(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp b/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp
index 1cfed866..e8c9e964 100644
--- a/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp
+++ b/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp
@@ -23,9 +23,10 @@ namespace fuzz {
FuzzerPassAddLoopPreheaders::FuzzerPassAddLoopPreheaders(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddLoopPreheaders::Apply() {
for (auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_add_loop_preheaders.h b/source/fuzz/fuzzer_pass_add_loop_preheaders.h
index 8ac2dac8..2a13dbac 100644
--- a/source/fuzz/fuzzer_pass_add_loop_preheaders.h
+++ b/source/fuzz/fuzzer_pass_add_loop_preheaders.h
@@ -30,7 +30,8 @@ class FuzzerPassAddLoopPreheaders : public FuzzerPass {
FuzzerPassAddLoopPreheaders(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp
index 69e0697f..1a65ef61 100644
--- a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp
@@ -29,9 +29,10 @@ FuzzerPassAddLoopsToCreateIntConstantSynonyms::
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddLoopsToCreateIntConstantSynonyms::Apply() {
std::vector<uint32_t> constants;
diff --git a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h
index 2eacef5f..14e17542 100644
--- a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h
+++ b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h
@@ -28,7 +28,8 @@ class FuzzerPassAddLoopsToCreateIntConstantSynonyms : public FuzzerPass {
FuzzerPassAddLoopsToCreateIntConstantSynonyms(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
index d0753100..8ae6e90a 100644
--- a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
+++ b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
@@ -22,9 +22,10 @@ namespace fuzz {
FuzzerPassAddNoContractionDecorations::FuzzerPassAddNoContractionDecorations(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddNoContractionDecorations::Apply() {
// Consider every instruction in every block in every function.
diff --git a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h
index 74212d87..7aeb26de 100644
--- a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h
+++ b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h
@@ -26,7 +26,8 @@ class FuzzerPassAddNoContractionDecorations : public FuzzerPass {
FuzzerPassAddNoContractionDecorations(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp b/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp
index be6e7ea1..73b6b0ac 100644
--- a/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp
@@ -23,9 +23,10 @@ namespace fuzz {
FuzzerPassAddOpPhiSynonyms::FuzzerPassAddOpPhiSynonyms(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddOpPhiSynonyms::Apply() {
// Get a list of synonymous ids with the same type that can be used in the
diff --git a/source/fuzz/fuzzer_pass_add_opphi_synonyms.h b/source/fuzz/fuzzer_pass_add_opphi_synonyms.h
index 9077118e..c45c9a8a 100644
--- a/source/fuzz/fuzzer_pass_add_opphi_synonyms.h
+++ b/source/fuzz/fuzzer_pass_add_opphi_synonyms.h
@@ -25,10 +25,11 @@ namespace fuzz {
// synonymous with the others.
class FuzzerPassAddOpPhiSynonyms : public FuzzerPass {
public:
- FuzzerPassAddOpPhiSynonyms(
- opt::IRContext* ir_context, TransformationContext* transformation_context,
- FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ FuzzerPassAddOpPhiSynonyms(opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_add_parameters.cpp b/source/fuzz/fuzzer_pass_add_parameters.cpp
index 784653d8..1cb6a79d 100644
--- a/source/fuzz/fuzzer_pass_add_parameters.cpp
+++ b/source/fuzz/fuzzer_pass_add_parameters.cpp
@@ -25,9 +25,10 @@ namespace fuzz {
FuzzerPassAddParameters::FuzzerPassAddParameters(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddParameters::Apply() {
// Compute type candidates for the new parameter.
diff --git a/source/fuzz/fuzzer_pass_add_parameters.h b/source/fuzz/fuzzer_pass_add_parameters.h
index 47dde390..c79f0e09 100644
--- a/source/fuzz/fuzzer_pass_add_parameters.h
+++ b/source/fuzz/fuzzer_pass_add_parameters.h
@@ -30,7 +30,8 @@ class FuzzerPassAddParameters : public FuzzerPass {
FuzzerPassAddParameters(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp b/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp
index 58c6d1b2..b8c2b559 100644
--- a/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp
+++ b/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp
@@ -22,9 +22,10 @@ namespace fuzz {
FuzzerPassAddRelaxedDecorations::FuzzerPassAddRelaxedDecorations(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddRelaxedDecorations::Apply() {
// Consider every instruction in every block in every function.
diff --git a/source/fuzz/fuzzer_pass_add_relaxed_decorations.h b/source/fuzz/fuzzer_pass_add_relaxed_decorations.h
index 723c4a0a..51eb5942 100644
--- a/source/fuzz/fuzzer_pass_add_relaxed_decorations.h
+++ b/source/fuzz/fuzzer_pass_add_relaxed_decorations.h
@@ -26,7 +26,8 @@ class FuzzerPassAddRelaxedDecorations : public FuzzerPass {
FuzzerPassAddRelaxedDecorations(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_add_stores.cpp b/source/fuzz/fuzzer_pass_add_stores.cpp
index f89428d3..606e4a63 100644
--- a/source/fuzz/fuzzer_pass_add_stores.cpp
+++ b/source/fuzz/fuzzer_pass_add_stores.cpp
@@ -23,9 +23,10 @@ namespace fuzz {
FuzzerPassAddStores::FuzzerPassAddStores(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddStores::Apply() {
ForEachInstructionWithInstructionDescriptor(
@@ -38,16 +39,20 @@ void FuzzerPassAddStores::Apply() {
"The opcode of the instruction we might insert before must be "
"the same as the opcode in the descriptor for the instruction");
+ // Randomly decide whether to try inserting a store here.
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfAddingStore())) {
+ return;
+ }
+
// Check whether it is legitimate to insert a store before this
// instruction.
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
inst_it)) {
return;
}
-
- // Randomly decide whether to try inserting a store here.
- if (!GetFuzzerContext()->ChoosePercentage(
- GetFuzzerContext()->GetChanceOfAddingStore())) {
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAtomicStore,
+ inst_it)) {
return;
}
@@ -116,10 +121,49 @@ void FuzzerPassAddStores::Apply() {
return;
}
- // Choose a value at random, and create and apply a storing
- // transformation based on it and the pointer.
+ bool is_atomic_store = false;
+ uint32_t memory_scope_id = 0;
+ uint32_t memory_semantics_id = 0;
+
+ auto storage_class =
+ static_cast<SpvStorageClass>(GetIRContext()
+ ->get_def_use_mgr()
+ ->GetDef(pointer->type_id())
+ ->GetSingleWordInOperand(0));
+
+ switch (storage_class) {
+ case SpvStorageClassStorageBuffer:
+ case SpvStorageClassPhysicalStorageBuffer:
+ case SpvStorageClassWorkgroup:
+ case SpvStorageClassCrossWorkgroup:
+ case SpvStorageClassAtomicCounter:
+ case SpvStorageClassImage:
+ if (GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfAddingAtomicStore())) {
+ is_atomic_store = true;
+
+ memory_scope_id = FindOrCreateConstant(
+ {SpvScopeInvocation},
+ FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()),
+ false);
+
+ memory_semantics_id = FindOrCreateConstant(
+ {static_cast<uint32_t>(
+ fuzzerutil::GetMemorySemanticsForStorageClass(
+ storage_class))},
+ FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()),
+ false);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Create and apply the transformation.
ApplyTransformation(TransformationStore(
- pointer->result_id(),
+ pointer->result_id(), is_atomic_store, memory_scope_id,
+ memory_semantics_id,
relevant_values[GetFuzzerContext()->RandomIndex(relevant_values)]
->result_id(),
instruction_descriptor));
diff --git a/source/fuzz/fuzzer_pass_add_stores.h b/source/fuzz/fuzzer_pass_add_stores.h
index 9519c385..b18dde1f 100644
--- a/source/fuzz/fuzzer_pass_add_stores.h
+++ b/source/fuzz/fuzzer_pass_add_stores.h
@@ -28,7 +28,8 @@ class FuzzerPassAddStores : public FuzzerPass {
FuzzerPassAddStores(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_add_synonyms.cpp b/source/fuzz/fuzzer_pass_add_synonyms.cpp
index fd866f98..1d188deb 100644
--- a/source/fuzz/fuzzer_pass_add_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_add_synonyms.cpp
@@ -25,9 +25,10 @@ namespace fuzz {
FuzzerPassAddSynonyms::FuzzerPassAddSynonyms(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddSynonyms::Apply() {
ForEachInstructionWithInstructionDescriptor(
diff --git a/source/fuzz/fuzzer_pass_add_synonyms.h b/source/fuzz/fuzzer_pass_add_synonyms.h
index ccf4a886..b0c174bc 100644
--- a/source/fuzz/fuzzer_pass_add_synonyms.h
+++ b/source/fuzz/fuzzer_pass_add_synonyms.h
@@ -27,7 +27,8 @@ class FuzzerPassAddSynonyms : public FuzzerPass {
FuzzerPassAddSynonyms(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
index e2eaaa0a..a29d1d34 100644
--- a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
+++ b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
@@ -24,9 +24,10 @@ namespace fuzz {
FuzzerPassAddVectorShuffleInstructions::FuzzerPassAddVectorShuffleInstructions(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAddVectorShuffleInstructions::Apply() {
ForEachInstructionWithInstructionDescriptor(
@@ -75,7 +76,7 @@ void FuzzerPassAddVectorShuffleInstructions::Apply() {
->IdIsIrrelevant(instruction->result_id()) &&
!fuzzerutil::CanMakeSynonymOf(ir_context,
*GetTransformationContext(),
- instruction)) {
+ *instruction)) {
// If the id is irrelevant, we can use it since it will not
// participate in DataSynonym fact. Otherwise, we should be
// able to produce a synonym out of the id.
diff --git a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h
index c5af374c..64d6608a 100644
--- a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h
+++ b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h
@@ -26,7 +26,8 @@ class FuzzerPassAddVectorShuffleInstructions : public FuzzerPass {
FuzzerPassAddVectorShuffleInstructions(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp b/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp
index 3c4f3803..94428f72 100644
--- a/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp
+++ b/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp
@@ -24,9 +24,10 @@ namespace fuzz {
FuzzerPassAdjustBranchWeights::FuzzerPassAdjustBranchWeights(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAdjustBranchWeights::Apply() {
// For all OpBranchConditional instructions,
diff --git a/source/fuzz/fuzzer_pass_adjust_branch_weights.h b/source/fuzz/fuzzer_pass_adjust_branch_weights.h
index de2f33d3..ae1ea345 100644
--- a/source/fuzz/fuzzer_pass_adjust_branch_weights.h
+++ b/source/fuzz/fuzzer_pass_adjust_branch_weights.h
@@ -28,7 +28,8 @@ class FuzzerPassAdjustBranchWeights : public FuzzerPass {
FuzzerPassAdjustBranchWeights(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
index b38bd212..1c2bc8cf 100644
--- a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
+++ b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
@@ -22,9 +22,10 @@ namespace fuzz {
FuzzerPassAdjustFunctionControls::FuzzerPassAdjustFunctionControls(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAdjustFunctionControls::Apply() {
// Consider every function in the module.
diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.h b/source/fuzz/fuzzer_pass_adjust_function_controls.h
index 5ef32a18..7a8c4927 100644
--- a/source/fuzz/fuzzer_pass_adjust_function_controls.h
+++ b/source/fuzz/fuzzer_pass_adjust_function_controls.h
@@ -26,7 +26,8 @@ class FuzzerPassAdjustFunctionControls : public FuzzerPass {
FuzzerPassAdjustFunctionControls(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
index 0f7cf037..fe855cad 100644
--- a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
+++ b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
@@ -22,9 +22,10 @@ namespace fuzz {
FuzzerPassAdjustLoopControls::FuzzerPassAdjustLoopControls(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAdjustLoopControls::Apply() {
// Consider every merge instruction in the module (via looking through all
diff --git a/source/fuzz/fuzzer_pass_adjust_loop_controls.h b/source/fuzz/fuzzer_pass_adjust_loop_controls.h
index 4ca670b7..25e83047 100644
--- a/source/fuzz/fuzzer_pass_adjust_loop_controls.h
+++ b/source/fuzz/fuzzer_pass_adjust_loop_controls.h
@@ -26,7 +26,8 @@ class FuzzerPassAdjustLoopControls : public FuzzerPass {
FuzzerPassAdjustLoopControls(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
index 778d43ff..d2ff40e6 100644
--- a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
+++ b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
@@ -23,9 +23,10 @@ namespace fuzz {
FuzzerPassAdjustMemoryOperandsMasks::FuzzerPassAdjustMemoryOperandsMasks(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAdjustMemoryOperandsMasks::Apply() {
// Consider every block in every function.
diff --git a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h
index a068b8dd..3197f91b 100644
--- a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h
+++ b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h
@@ -27,7 +27,8 @@ class FuzzerPassAdjustMemoryOperandsMasks : public FuzzerPass {
FuzzerPassAdjustMemoryOperandsMasks(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
index d9b4e293..7d8e6b57 100644
--- a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
+++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
@@ -22,9 +22,10 @@ namespace fuzz {
FuzzerPassAdjustSelectionControls::FuzzerPassAdjustSelectionControls(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassAdjustSelectionControls::Apply() {
// Consider every merge instruction in the module (via looking through all
diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.h b/source/fuzz/fuzzer_pass_adjust_selection_controls.h
index 6931f942..ac55de79 100644
--- a/source/fuzz/fuzzer_pass_adjust_selection_controls.h
+++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.h
@@ -26,7 +26,8 @@ class FuzzerPassAdjustSelectionControls : public FuzzerPass {
FuzzerPassAdjustSelectionControls(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
index 71208313..5c3b86b9 100644
--- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
@@ -28,9 +28,10 @@ namespace fuzz {
FuzzerPassApplyIdSynonyms::FuzzerPassApplyIdSynonyms(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassApplyIdSynonyms::Apply() {
// Compute a closure of data synonym facts, to enrich the pool of synonyms
@@ -197,7 +198,7 @@ bool FuzzerPassApplyIdSynonyms::DataDescriptorsHaveCompatibleTypes(
GetIRContext(), base_object_type_id_2, dd2.index());
assert(type_id_1 && type_id_2 && "Data descriptors have invalid types");
- return TransformationReplaceIdWithSynonym::TypesAreCompatible(
+ return fuzzerutil::TypesAreCompatible(
GetIRContext(), opcode, use_in_operand_index, type_id_1, type_id_2);
}
diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.h b/source/fuzz/fuzzer_pass_apply_id_synonyms.h
index b402b509..3da9c5d6 100644
--- a/source/fuzz/fuzzer_pass_apply_id_synonyms.h
+++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.h
@@ -28,7 +28,8 @@ class FuzzerPassApplyIdSynonyms : public FuzzerPass {
FuzzerPassApplyIdSynonyms(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_construct_composites.cpp b/source/fuzz/fuzzer_pass_construct_composites.cpp
index 1a174cf1..ff022fcc 100644
--- a/source/fuzz/fuzzer_pass_construct_composites.cpp
+++ b/source/fuzz/fuzzer_pass_construct_composites.cpp
@@ -26,9 +26,10 @@ namespace fuzz {
FuzzerPassConstructComposites::FuzzerPassConstructComposites(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassConstructComposites::Apply() {
// Gather up the ids of all composite types, but skip block-/buffer
@@ -61,7 +62,7 @@ void FuzzerPassConstructComposites::Apply() {
return GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
inst->result_id()) ||
fuzzerutil::CanMakeSynonymOf(ir_context,
- *GetTransformationContext(), inst);
+ *GetTransformationContext(), *inst);
});
ForEachInstructionWithInstructionDescriptor(
diff --git a/source/fuzz/fuzzer_pass_construct_composites.h b/source/fuzz/fuzzer_pass_construct_composites.h
index 333ac931..81413980 100644
--- a/source/fuzz/fuzzer_pass_construct_composites.h
+++ b/source/fuzz/fuzzer_pass_construct_composites.h
@@ -29,7 +29,8 @@ class FuzzerPassConstructComposites : public FuzzerPass {
FuzzerPassConstructComposites(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_copy_objects.cpp b/source/fuzz/fuzzer_pass_copy_objects.cpp
index 09b5368a..80cc2a57 100644
--- a/source/fuzz/fuzzer_pass_copy_objects.cpp
+++ b/source/fuzz/fuzzer_pass_copy_objects.cpp
@@ -24,9 +24,10 @@ namespace fuzz {
FuzzerPassCopyObjects::FuzzerPassCopyObjects(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassCopyObjects::Apply() {
ForEachInstructionWithInstructionDescriptor(
diff --git a/source/fuzz/fuzzer_pass_copy_objects.h b/source/fuzz/fuzzer_pass_copy_objects.h
index 461cd979..2fb5a53f 100644
--- a/source/fuzz/fuzzer_pass_copy_objects.h
+++ b/source/fuzz/fuzzer_pass_copy_objects.h
@@ -26,7 +26,8 @@ class FuzzerPassCopyObjects : public FuzzerPass {
FuzzerPassCopyObjects(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp
index 2f2ed50a..29ede58b 100644
--- a/source/fuzz/fuzzer_pass_donate_modules.cpp
+++ b/source/fuzz/fuzzer_pass_donate_modules.cpp
@@ -45,10 +45,11 @@ FuzzerPassDonateModules::FuzzerPassDonateModules(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations,
- const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers)
+ bool ignore_inapplicable_transformations,
+ std::vector<fuzzerutil::ModuleSupplier> donor_suppliers)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations),
- donor_suppliers_(donor_suppliers) {}
+ transformations, ignore_inapplicable_transformations),
+ donor_suppliers_(std::move(donor_suppliers)) {}
void FuzzerPassDonateModules::Apply() {
// If there are no donor suppliers, this fuzzer pass is a no-op.
@@ -478,7 +479,7 @@ void FuzzerPassDonateModules::HandleTypeOrValue(
"should have been donated.");
// It is OK to have duplicate constant composite definitions, so add
- // this to the module using remapped versions of all consituent ids and
+ // this to the module using remapped versions of all constituent ids and
// the result type.
new_result_id = GetFuzzerContext()->GetFreshId();
std::vector<uint32_t> constituent_ids;
diff --git a/source/fuzz/fuzzer_pass_donate_modules.h b/source/fuzz/fuzzer_pass_donate_modules.h
index 1581a8a4..924dd358 100644
--- a/source/fuzz/fuzzer_pass_donate_modules.h
+++ b/source/fuzz/fuzzer_pass_donate_modules.h
@@ -31,7 +31,8 @@ class FuzzerPassDonateModules : public FuzzerPass {
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations,
- const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers);
+ bool ignore_inapplicable_transformations,
+ std::vector<fuzzerutil::ModuleSupplier> donor_suppliers);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp
index e08d65b1..3a9a7e66 100644
--- a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp
+++ b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp
@@ -25,9 +25,10 @@ FuzzerPassDuplicateRegionsWithSelections::
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassDuplicateRegionsWithSelections::Apply() {
// Iterate over all of the functions in the module.
diff --git a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h
index 7cb1197d..74343a66 100644
--- a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h
+++ b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h
@@ -29,7 +29,8 @@ class FuzzerPassDuplicateRegionsWithSelections : public FuzzerPass {
FuzzerPassDuplicateRegionsWithSelections(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp b/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp
index e25dcbc3..5bf0461d 100644
--- a/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp
+++ b/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp
@@ -24,9 +24,10 @@ namespace fuzz {
FuzzerPassExpandVectorReductions::FuzzerPassExpandVectorReductions(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassExpandVectorReductions::Apply() {
for (auto& function : *GetIRContext()->module()) {
@@ -46,7 +47,7 @@ void FuzzerPassExpandVectorReductions::Apply() {
// It must be able to make a synonym of |instruction|.
if (!fuzzerutil::CanMakeSynonymOf(
- GetIRContext(), *GetTransformationContext(), &instruction)) {
+ GetIRContext(), *GetTransformationContext(), instruction)) {
continue;
}
diff --git a/source/fuzz/fuzzer_pass_expand_vector_reductions.h b/source/fuzz/fuzzer_pass_expand_vector_reductions.h
index ed0225df..c0673e12 100644
--- a/source/fuzz/fuzzer_pass_expand_vector_reductions.h
+++ b/source/fuzz/fuzzer_pass_expand_vector_reductions.h
@@ -28,7 +28,8 @@ class FuzzerPassExpandVectorReductions : public FuzzerPass {
FuzzerPassExpandVectorReductions(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp b/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp
index 84da7a74..70fa6a12 100644
--- a/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp
+++ b/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp
@@ -26,9 +26,10 @@ namespace fuzz {
FuzzerPassFlattenConditionalBranches::FuzzerPassFlattenConditionalBranches(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassFlattenConditionalBranches::Apply() {
for (auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_flatten_conditional_branches.h b/source/fuzz/fuzzer_pass_flatten_conditional_branches.h
index e7d7dc36..abfbc3c1 100644
--- a/source/fuzz/fuzzer_pass_flatten_conditional_branches.h
+++ b/source/fuzz/fuzzer_pass_flatten_conditional_branches.h
@@ -25,7 +25,8 @@ class FuzzerPassFlattenConditionalBranches : public FuzzerPass {
FuzzerPassFlattenConditionalBranches(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_inline_functions.cpp b/source/fuzz/fuzzer_pass_inline_functions.cpp
index 405afd8b..4024096f 100644
--- a/source/fuzz/fuzzer_pass_inline_functions.cpp
+++ b/source/fuzz/fuzzer_pass_inline_functions.cpp
@@ -25,9 +25,10 @@ namespace fuzz {
FuzzerPassInlineFunctions::FuzzerPassInlineFunctions(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassInlineFunctions::Apply() {
// |function_call_instructions| are the instructions that will be inlined.
diff --git a/source/fuzz/fuzzer_pass_inline_functions.h b/source/fuzz/fuzzer_pass_inline_functions.h
index a6ed8cad..c4e0b831 100644
--- a/source/fuzz/fuzzer_pass_inline_functions.h
+++ b/source/fuzz/fuzzer_pass_inline_functions.h
@@ -28,7 +28,8 @@ class FuzzerPassInlineFunctions : public FuzzerPass {
FuzzerPassInlineFunctions(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp
index 675cae9a..d8780ff7 100644
--- a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp
+++ b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp
@@ -27,9 +27,10 @@ FuzzerPassInterchangeSignednessOfIntegerOperands::
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassInterchangeSignednessOfIntegerOperands::Apply() {
assert(!GetFuzzerContext()->IsWgslCompatible() &&
diff --git a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h
index 11b8fb6e..6a10e897 100644
--- a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h
+++ b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h
@@ -32,7 +32,8 @@ class FuzzerPassInterchangeSignednessOfIntegerOperands : public FuzzerPass {
FuzzerPassInterchangeSignednessOfIntegerOperands(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
index 5d0a2223..2c16cd5b 100644
--- a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
+++ b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
@@ -25,9 +25,10 @@ namespace fuzz {
FuzzerPassInterchangeZeroLikeConstants::FuzzerPassInterchangeZeroLikeConstants(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant(
opt::Instruction* declaration) {
diff --git a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
index 012f03d3..bb4a4dc7 100644
--- a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
+++ b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
@@ -33,7 +33,8 @@ class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass {
FuzzerPassInterchangeZeroLikeConstants(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp b/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp
index 542748e9..130f7502 100644
--- a/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp
+++ b/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp
@@ -24,9 +24,10 @@ namespace fuzz {
FuzzerPassInvertComparisonOperators::FuzzerPassInvertComparisonOperators(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassInvertComparisonOperators::Apply() {
GetIRContext()->module()->ForEachInst([this](const opt::Instruction* inst) {
diff --git a/source/fuzz/fuzzer_pass_invert_comparison_operators.h b/source/fuzz/fuzzer_pass_invert_comparison_operators.h
index 5f015c28..d0d09edb 100644
--- a/source/fuzz/fuzzer_pass_invert_comparison_operators.h
+++ b/source/fuzz/fuzzer_pass_invert_comparison_operators.h
@@ -27,7 +27,8 @@ class FuzzerPassInvertComparisonOperators : public FuzzerPass {
FuzzerPassInvertComparisonOperators(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp
index 7bf07a49..b755d235 100644
--- a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp
+++ b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp
@@ -24,9 +24,10 @@ namespace fuzz {
FuzzerPassMakeVectorOperationsDynamic::FuzzerPassMakeVectorOperationsDynamic(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassMakeVectorOperationsDynamic::Apply() {
for (auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h
index da27825a..b6a46c78 100644
--- a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h
+++ b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h
@@ -27,7 +27,8 @@ class FuzzerPassMakeVectorOperationsDynamic : public FuzzerPass {
FuzzerPassMakeVectorOperationsDynamic(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_merge_blocks.cpp b/source/fuzz/fuzzer_pass_merge_blocks.cpp
index a8ec784c..70200629 100644
--- a/source/fuzz/fuzzer_pass_merge_blocks.cpp
+++ b/source/fuzz/fuzzer_pass_merge_blocks.cpp
@@ -24,9 +24,10 @@ namespace fuzz {
FuzzerPassMergeBlocks::FuzzerPassMergeBlocks(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassMergeBlocks::Apply() {
// First we populate a sequence of transformations that we might consider
diff --git a/source/fuzz/fuzzer_pass_merge_blocks.h b/source/fuzz/fuzzer_pass_merge_blocks.h
index 66cf4c64..46e257f1 100644
--- a/source/fuzz/fuzzer_pass_merge_blocks.h
+++ b/source/fuzz/fuzzer_pass_merge_blocks.h
@@ -26,7 +26,8 @@ class FuzzerPassMergeBlocks : public FuzzerPass {
FuzzerPassMergeBlocks(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_merge_function_returns.cpp b/source/fuzz/fuzzer_pass_merge_function_returns.cpp
index ee82eca5..220f707b 100644
--- a/source/fuzz/fuzzer_pass_merge_function_returns.cpp
+++ b/source/fuzz/fuzzer_pass_merge_function_returns.cpp
@@ -26,9 +26,10 @@ namespace fuzz {
FuzzerPassMergeFunctionReturns::FuzzerPassMergeFunctionReturns(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassMergeFunctionReturns::Apply() {
// The pass might add new functions to the module (due to wrapping early
diff --git a/source/fuzz/fuzzer_pass_merge_function_returns.h b/source/fuzz/fuzzer_pass_merge_function_returns.h
index ddd2c9dc..a799b8be 100644
--- a/source/fuzz/fuzzer_pass_merge_function_returns.h
+++ b/source/fuzz/fuzzer_pass_merge_function_returns.h
@@ -31,7 +31,8 @@ class FuzzerPassMergeFunctionReturns : public FuzzerPass {
FuzzerPassMergeFunctionReturns(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_mutate_pointers.cpp b/source/fuzz/fuzzer_pass_mutate_pointers.cpp
index f021a978..bbe05409 100644
--- a/source/fuzz/fuzzer_pass_mutate_pointers.cpp
+++ b/source/fuzz/fuzzer_pass_mutate_pointers.cpp
@@ -24,9 +24,10 @@ namespace fuzz {
FuzzerPassMutatePointers::FuzzerPassMutatePointers(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassMutatePointers::Apply() {
ForEachInstructionWithInstructionDescriptor(
diff --git a/source/fuzz/fuzzer_pass_mutate_pointers.h b/source/fuzz/fuzzer_pass_mutate_pointers.h
index 5ef6a2ab..45c6a7cc 100644
--- a/source/fuzz/fuzzer_pass_mutate_pointers.h
+++ b/source/fuzz/fuzzer_pass_mutate_pointers.h
@@ -26,7 +26,8 @@ class FuzzerPassMutatePointers : public FuzzerPass {
FuzzerPassMutatePointers(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
index 32318e89..f60c1b4c 100644
--- a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
+++ b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
@@ -30,9 +30,10 @@ namespace fuzz {
FuzzerPassObfuscateConstants::FuzzerPassObfuscateConstants(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair(
uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.h b/source/fuzz/fuzzer_pass_obfuscate_constants.h
index 82b1092c..30e64d28 100644
--- a/source/fuzz/fuzzer_pass_obfuscate_constants.h
+++ b/source/fuzz/fuzzer_pass_obfuscate_constants.h
@@ -30,7 +30,8 @@ class FuzzerPassObfuscateConstants : public FuzzerPass {
FuzzerPassObfuscateConstants(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_outline_functions.cpp b/source/fuzz/fuzzer_pass_outline_functions.cpp
index bfde61f7..b90c12dc 100644
--- a/source/fuzz/fuzzer_pass_outline_functions.cpp
+++ b/source/fuzz/fuzzer_pass_outline_functions.cpp
@@ -27,9 +27,10 @@ namespace fuzz {
FuzzerPassOutlineFunctions::FuzzerPassOutlineFunctions(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassOutlineFunctions::Apply() {
std::vector<opt::Function*> original_functions;
diff --git a/source/fuzz/fuzzer_pass_outline_functions.h b/source/fuzz/fuzzer_pass_outline_functions.h
index 45e52ff4..d80dc4a7 100644
--- a/source/fuzz/fuzzer_pass_outline_functions.h
+++ b/source/fuzz/fuzzer_pass_outline_functions.h
@@ -24,10 +24,11 @@ namespace fuzz {
// flow graph into their own functions.
class FuzzerPassOutlineFunctions : public FuzzerPass {
public:
- FuzzerPassOutlineFunctions(
- opt::IRContext* ir_context, TransformationContext* transformation_context,
- FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ FuzzerPassOutlineFunctions(opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_permute_blocks.cpp b/source/fuzz/fuzzer_pass_permute_blocks.cpp
index 769c49f0..e55fae32 100644
--- a/source/fuzz/fuzzer_pass_permute_blocks.cpp
+++ b/source/fuzz/fuzzer_pass_permute_blocks.cpp
@@ -22,9 +22,10 @@ namespace fuzz {
FuzzerPassPermuteBlocks::FuzzerPassPermuteBlocks(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassPermuteBlocks::Apply() {
// For now we do something very simple: we randomly decide whether to move a
diff --git a/source/fuzz/fuzzer_pass_permute_blocks.h b/source/fuzz/fuzzer_pass_permute_blocks.h
index e40178e6..39326c2b 100644
--- a/source/fuzz/fuzzer_pass_permute_blocks.h
+++ b/source/fuzz/fuzzer_pass_permute_blocks.h
@@ -27,7 +27,8 @@ class FuzzerPassPermuteBlocks : public FuzzerPass {
FuzzerPassPermuteBlocks(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
index 9a61bea0..a8035b90 100644
--- a/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
+++ b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
@@ -28,9 +28,10 @@ namespace fuzz {
FuzzerPassPermuteFunctionParameters::FuzzerPassPermuteFunctionParameters(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassPermuteFunctionParameters::Apply() {
for (const auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_permute_function_parameters.h b/source/fuzz/fuzzer_pass_permute_function_parameters.h
index a4bf2ca5..c5b6ad43 100644
--- a/source/fuzz/fuzzer_pass_permute_function_parameters.h
+++ b/source/fuzz/fuzzer_pass_permute_function_parameters.h
@@ -32,7 +32,8 @@ class FuzzerPassPermuteFunctionParameters : public FuzzerPass {
FuzzerPassPermuteFunctionParameters(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_permute_function_variables.cpp b/source/fuzz/fuzzer_pass_permute_function_variables.cpp
index a4e19e3b..f8b9b450 100644
--- a/source/fuzz/fuzzer_pass_permute_function_variables.cpp
+++ b/source/fuzz/fuzzer_pass_permute_function_variables.cpp
@@ -29,9 +29,11 @@ namespace fuzz {
FuzzerPassPermuteFunctionVariables::FuzzerPassPermuteFunctionVariables(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {} // Here we call parent constructor.
+ transformations, ignore_inapplicable_transformations) {
+} // Here we call parent constructor.
void FuzzerPassPermuteFunctionVariables::Apply() {
// Permuting OpVariable instructions in each function.
diff --git a/source/fuzz/fuzzer_pass_permute_function_variables.h b/source/fuzz/fuzzer_pass_permute_function_variables.h
index 47f1de28..2ebc15f2 100644
--- a/source/fuzz/fuzzer_pass_permute_function_variables.h
+++ b/source/fuzz/fuzzer_pass_permute_function_variables.h
@@ -26,7 +26,8 @@ class FuzzerPassPermuteFunctionVariables : public FuzzerPass {
FuzzerPassPermuteFunctionVariables(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_permute_instructions.cpp b/source/fuzz/fuzzer_pass_permute_instructions.cpp
index f17e0187..3ef76be4 100644
--- a/source/fuzz/fuzzer_pass_permute_instructions.cpp
+++ b/source/fuzz/fuzzer_pass_permute_instructions.cpp
@@ -25,9 +25,10 @@ namespace fuzz {
FuzzerPassPermuteInstructions::FuzzerPassPermuteInstructions(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassPermuteInstructions::Apply() {
// We are iterating over all instructions in all basic blocks.
diff --git a/source/fuzz/fuzzer_pass_permute_instructions.h b/source/fuzz/fuzzer_pass_permute_instructions.h
index 027101dd..b7ccbccb 100644
--- a/source/fuzz/fuzzer_pass_permute_instructions.h
+++ b/source/fuzz/fuzzer_pass_permute_instructions.h
@@ -27,7 +27,8 @@ class FuzzerPassPermuteInstructions : public FuzzerPass {
FuzzerPassPermuteInstructions(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_permute_phi_operands.cpp b/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
index f2cc5231..5fac9816 100644
--- a/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
+++ b/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
@@ -28,9 +28,10 @@ namespace fuzz {
FuzzerPassPermutePhiOperands::FuzzerPassPermutePhiOperands(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassPermutePhiOperands::Apply() {
ForEachInstructionWithInstructionDescriptor(
diff --git a/source/fuzz/fuzzer_pass_permute_phi_operands.h b/source/fuzz/fuzzer_pass_permute_phi_operands.h
index 79999562..30a9d4f5 100644
--- a/source/fuzz/fuzzer_pass_permute_phi_operands.h
+++ b/source/fuzz/fuzzer_pass_permute_phi_operands.h
@@ -27,7 +27,8 @@ class FuzzerPassPermutePhiOperands : public FuzzerPass {
FuzzerPassPermutePhiOperands(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp b/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp
index af27a5da..4c46dcd3 100644
--- a/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp
+++ b/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp
@@ -23,16 +23,16 @@ namespace fuzz {
FuzzerPassPropagateInstructionsDown::FuzzerPassPropagateInstructionsDown(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassPropagateInstructionsDown::Apply() {
for (const auto& function : *GetIRContext()->module()) {
std::vector<const opt::BasicBlock*> reachable_blocks;
for (const auto& block : function) {
- if (GetIRContext()->GetDominatorAnalysis(&function)->IsReachable(
- &block)) {
+ if (GetIRContext()->IsReachable(block)) {
reachable_blocks.push_back(&block);
}
}
diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_down.h b/source/fuzz/fuzzer_pass_propagate_instructions_down.h
index a2a0aac8..18f01654 100644
--- a/source/fuzz/fuzzer_pass_propagate_instructions_down.h
+++ b/source/fuzz/fuzzer_pass_propagate_instructions_down.h
@@ -26,7 +26,8 @@ class FuzzerPassPropagateInstructionsDown : public FuzzerPass {
FuzzerPassPropagateInstructionsDown(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp b/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp
index 8cd7437b..5e45da83 100644
--- a/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp
+++ b/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp
@@ -25,9 +25,10 @@ namespace fuzz {
FuzzerPassPropagateInstructionsUp::FuzzerPassPropagateInstructionsUp(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassPropagateInstructionsUp::Apply() {
for (const auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_up.h b/source/fuzz/fuzzer_pass_propagate_instructions_up.h
index b89be48d..0cb8396b 100644
--- a/source/fuzz/fuzzer_pass_propagate_instructions_up.h
+++ b/source/fuzz/fuzzer_pass_propagate_instructions_up.h
@@ -27,7 +27,8 @@ class FuzzerPassPropagateInstructionsUp : public FuzzerPass {
FuzzerPassPropagateInstructionsUp(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
index 54e589c9..a6c07b4b 100644
--- a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
+++ b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
@@ -24,9 +24,10 @@ namespace fuzz {
FuzzerPassPushIdsThroughVariables::FuzzerPassPushIdsThroughVariables(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassPushIdsThroughVariables::Apply() {
ForEachInstructionWithInstructionDescriptor(
@@ -47,7 +48,7 @@ void FuzzerPassPushIdsThroughVariables::Apply() {
// The block containing the instruction we are going to insert before
// must be reachable.
- if (!fuzzerutil::BlockIsReachableInItsFunction(GetIRContext(), block)) {
+ if (!GetIRContext()->IsReachable(*block)) {
return;
}
@@ -69,6 +70,12 @@ void FuzzerPassPushIdsThroughVariables::Apply() {
auto basic_type_ids_and_pointers =
GetAvailableBasicTypesAndPointers(variable_storage_class);
auto& basic_types = basic_type_ids_and_pointers.first;
+
+ // There must be at least some basic types.
+ if (basic_types.empty()) {
+ return;
+ }
+
uint32_t basic_type_id =
basic_types[GetFuzzerContext()->RandomIndex(basic_types)];
@@ -96,7 +103,7 @@ void FuzzerPassPushIdsThroughVariables::Apply() {
->IdIsIrrelevant(instruction->result_id()) &&
!fuzzerutil::CanMakeSynonymOf(ir_context,
*GetTransformationContext(),
- instruction)) {
+ *instruction)) {
return false;
}
diff --git a/source/fuzz/fuzzer_pass_push_ids_through_variables.h b/source/fuzz/fuzzer_pass_push_ids_through_variables.h
index 53008ee2..71b69492 100644
--- a/source/fuzz/fuzzer_pass_push_ids_through_variables.h
+++ b/source/fuzz/fuzzer_pass_push_ids_through_variables.h
@@ -28,7 +28,8 @@ class FuzzerPassPushIdsThroughVariables : public FuzzerPass {
FuzzerPassPushIdsThroughVariables(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp
index 8a83d3bc..467e6134 100644
--- a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp
+++ b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp
@@ -29,9 +29,10 @@ FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::Apply() {
std::vector<opt::Instruction> instructions_for_transformation;
diff --git a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h
index 0e29a6c6..268655f9 100644
--- a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h
+++ b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h
@@ -29,7 +29,8 @@ class FuzzerPassReplaceAddsSubsMulsWithCarryingExtended : public FuzzerPass {
FuzzerPassReplaceAddsSubsMulsWithCarryingExtended(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp
index a516f3d4..995657cc 100644
--- a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp
+++ b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp
@@ -28,9 +28,10 @@ FuzzerPassReplaceBranchesFromDeadBlocksWithExits::
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassReplaceBranchesFromDeadBlocksWithExits::Apply() {
// OpKill can only be used as a terminator in a function that is guaranteed
diff --git a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h
index ab7e00e5..cdbb66f2 100644
--- a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h
+++ b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h
@@ -28,7 +28,8 @@ class FuzzerPassReplaceBranchesFromDeadBlocksWithExits : public FuzzerPass {
FuzzerPassReplaceBranchesFromDeadBlocksWithExits(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
index f17339a9..af1aacee 100644
--- a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
+++ b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
@@ -26,9 +26,10 @@ FuzzerPassReplaceCopyMemoriesWithLoadsStores::
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassReplaceCopyMemoriesWithLoadsStores::Apply() {
GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
diff --git a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h
index cffe1cb9..7d954abe 100644
--- a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h
+++ b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h
@@ -28,7 +28,8 @@ class FuzzerPassReplaceCopyMemoriesWithLoadsStores : public FuzzerPass {
FuzzerPassReplaceCopyMemoriesWithLoadsStores(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
index 24f2255b..d0992a3f 100644
--- a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
+++ b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
@@ -26,9 +26,10 @@ FuzzerPassReplaceCopyObjectsWithStoresLoads::
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassReplaceCopyObjectsWithStoresLoads::Apply() {
GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
diff --git a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h
index e7b11ce1..2ffc00b6 100644
--- a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h
+++ b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h
@@ -28,7 +28,8 @@ class FuzzerPassReplaceCopyObjectsWithStoresLoads : public FuzzerPass {
FuzzerPassReplaceCopyObjectsWithStoresLoads(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp
index 7e9d7baa..4d55ae84 100644
--- a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp
+++ b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp
@@ -27,9 +27,10 @@ namespace fuzz {
FuzzerPassReplaceIrrelevantIds::FuzzerPassReplaceIrrelevantIds(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassReplaceIrrelevantIds::Apply() {
// Keep track of the irrelevant ids. This includes all the ids that are
diff --git a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h
index 1dc6b5d3..80f8eb86 100644
--- a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h
+++ b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h
@@ -28,7 +28,8 @@ class FuzzerPassReplaceIrrelevantIds : public FuzzerPass {
FuzzerPassReplaceIrrelevantIds(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp
index 0890c2fe..445dbfe1 100644
--- a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp
+++ b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp
@@ -26,9 +26,10 @@ FuzzerPassReplaceLinearAlgebraInstructions::
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassReplaceLinearAlgebraInstructions::Apply() {
// For each instruction, checks whether it is a linear algebra instruction. In
diff --git a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h
index 5d2f2042..5734bf1f 100644
--- a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h
+++ b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h
@@ -27,7 +27,8 @@ class FuzzerPassReplaceLinearAlgebraInstructions : public FuzzerPass {
FuzzerPassReplaceLinearAlgebraInstructions(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
index f2cf80fa..38ac048b 100644
--- a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
+++ b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
@@ -27,9 +27,10 @@ FuzzerPassReplaceLoadsStoresWithCopyMemories::
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassReplaceLoadsStoresWithCopyMemories::Apply() {
// We look for matching pairs of instructions OpLoad and
diff --git a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h
index f30fc2b7..f6209fc0 100644
--- a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h
+++ b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h
@@ -28,7 +28,8 @@ class FuzzerPassReplaceLoadsStoresWithCopyMemories : public FuzzerPass {
FuzzerPassReplaceLoadsStoresWithCopyMemories(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp
index b0a3d57c..ea90a7ac 100644
--- a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp
+++ b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp
@@ -24,9 +24,10 @@ FuzzerPassReplaceOpPhiIdsFromDeadPredecessors::
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassReplaceOpPhiIdsFromDeadPredecessors::Apply() {
// Keep a vector of the transformations to apply.
diff --git a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h
index a2bc1886..b01e242f 100644
--- a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h
+++ b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h
@@ -27,7 +27,8 @@ class FuzzerPassReplaceOpPhiIdsFromDeadPredecessors : public FuzzerPass {
FuzzerPassReplaceOpPhiIdsFromDeadPredecessors(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp
index 10bb90ad..72ed0936 100644
--- a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp
+++ b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp
@@ -27,9 +27,10 @@ FuzzerPassReplaceOpSelectsWithConditionalBranches::
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassReplaceOpSelectsWithConditionalBranches::Apply() {
// Keep track of the instructions that we want to replace. We need to collect
diff --git a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h
index ec743890..174962ee 100644
--- a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h
+++ b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h
@@ -27,7 +27,8 @@ class FuzzerPassReplaceOpSelectsWithConditionalBranches : public FuzzerPass {
FuzzerPassReplaceOpSelectsWithConditionalBranches(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp b/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
index 5c256bb0..7fb7b0d2 100644
--- a/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
+++ b/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
@@ -27,9 +27,10 @@ namespace fuzz {
FuzzerPassReplaceParameterWithGlobal::FuzzerPassReplaceParameterWithGlobal(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassReplaceParameterWithGlobal::Apply() {
for (const auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_replace_parameter_with_global.h b/source/fuzz/fuzzer_pass_replace_parameter_with_global.h
index 2ae49469..4eb50866 100644
--- a/source/fuzz/fuzzer_pass_replace_parameter_with_global.h
+++ b/source/fuzz/fuzzer_pass_replace_parameter_with_global.h
@@ -27,7 +27,8 @@ class FuzzerPassReplaceParameterWithGlobal : public FuzzerPass {
FuzzerPassReplaceParameterWithGlobal(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp b/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp
index c045e19f..f029316e 100644
--- a/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp
+++ b/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp
@@ -27,9 +27,10 @@ namespace fuzz {
FuzzerPassReplaceParamsWithStruct::FuzzerPassReplaceParamsWithStruct(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassReplaceParamsWithStruct::Apply() {
for (const auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_replace_params_with_struct.h b/source/fuzz/fuzzer_pass_replace_params_with_struct.h
index f17f5207..3af7367a 100644
--- a/source/fuzz/fuzzer_pass_replace_params_with_struct.h
+++ b/source/fuzz/fuzzer_pass_replace_params_with_struct.h
@@ -27,7 +27,8 @@ class FuzzerPassReplaceParamsWithStruct : public FuzzerPass {
FuzzerPassReplaceParamsWithStruct(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_split_blocks.cpp b/source/fuzz/fuzzer_pass_split_blocks.cpp
index 7b493559..40a4151d 100644
--- a/source/fuzz/fuzzer_pass_split_blocks.cpp
+++ b/source/fuzz/fuzzer_pass_split_blocks.cpp
@@ -25,9 +25,10 @@ namespace fuzz {
FuzzerPassSplitBlocks::FuzzerPassSplitBlocks(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassSplitBlocks::Apply() {
// Gather up pointers to all the blocks in the module. We are then able to
diff --git a/source/fuzz/fuzzer_pass_split_blocks.h b/source/fuzz/fuzzer_pass_split_blocks.h
index 58f10ddb..b1b94240 100644
--- a/source/fuzz/fuzzer_pass_split_blocks.h
+++ b/source/fuzz/fuzzer_pass_split_blocks.h
@@ -27,7 +27,8 @@ class FuzzerPassSplitBlocks : public FuzzerPass {
FuzzerPassSplitBlocks(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp b/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp
index 27fadd17..dce65f08 100644
--- a/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp
+++ b/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp
@@ -24,9 +24,10 @@ namespace fuzz {
FuzzerPassSwapCommutableOperands::FuzzerPassSwapCommutableOperands(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassSwapCommutableOperands::Apply() {
auto context = GetIRContext();
diff --git a/source/fuzz/fuzzer_pass_swap_commutable_operands.h b/source/fuzz/fuzzer_pass_swap_commutable_operands.h
index 93de1728..13d8fb6e 100644
--- a/source/fuzz/fuzzer_pass_swap_commutable_operands.h
+++ b/source/fuzz/fuzzer_pass_swap_commutable_operands.h
@@ -28,7 +28,8 @@ class FuzzerPassSwapCommutableOperands : public FuzzerPass {
FuzzerPassSwapCommutableOperands(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
index b145b3bc..f8bf111d 100644
--- a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
+++ b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
@@ -27,9 +27,10 @@ FuzzerPassSwapBranchConditionalOperands::
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassSwapBranchConditionalOperands::Apply() {
ForEachInstructionWithInstructionDescriptor(
diff --git a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h
index 0137f38b..7f71f9b1 100644
--- a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h
+++ b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h
@@ -27,7 +27,8 @@ class FuzzerPassSwapBranchConditionalOperands : public FuzzerPass {
FuzzerPassSwapBranchConditionalOperands(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_swap_functions.cpp b/source/fuzz/fuzzer_pass_swap_functions.cpp
index 171f6cb8..8eeec850 100644
--- a/source/fuzz/fuzzer_pass_swap_functions.cpp
+++ b/source/fuzz/fuzzer_pass_swap_functions.cpp
@@ -23,9 +23,10 @@ namespace fuzz {
FuzzerPassSwapFunctions::FuzzerPassSwapFunctions(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassSwapFunctions::Apply() {
// Collect all function ids in a module.
diff --git a/source/fuzz/fuzzer_pass_swap_functions.h b/source/fuzz/fuzzer_pass_swap_functions.h
index ac551f69..7af527f0 100644
--- a/source/fuzz/fuzzer_pass_swap_functions.h
+++ b/source/fuzz/fuzzer_pass_swap_functions.h
@@ -26,7 +26,8 @@ class FuzzerPassSwapFunctions : public FuzzerPass {
FuzzerPassSwapFunctions(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
index e5afd9ee..ac2b1565 100644
--- a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
+++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
@@ -24,9 +24,10 @@ namespace fuzz {
FuzzerPassToggleAccessChainInstruction::FuzzerPassToggleAccessChainInstruction(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassToggleAccessChainInstruction::Apply() {
auto context = GetIRContext();
diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
index ff2f5d45..f0b6166b 100644
--- a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
+++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
@@ -27,7 +27,8 @@ class FuzzerPassToggleAccessChainInstruction : public FuzzerPass {
FuzzerPassToggleAccessChainInstruction(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
};
diff --git a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp
index 66bbcd81..3a3b12c1 100644
--- a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp
+++ b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp
@@ -25,9 +25,10 @@ namespace fuzz {
FuzzerPassWrapRegionsInSelections::FuzzerPassWrapRegionsInSelections(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations)
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, ignore_inapplicable_transformations) {}
void FuzzerPassWrapRegionsInSelections::Apply() {
for (auto& function : *GetIRContext()->module()) {
diff --git a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h
index 822c308f..fc3d7df9 100644
--- a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h
+++ b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h
@@ -27,7 +27,8 @@ class FuzzerPassWrapRegionsInSelections : public FuzzerPass {
FuzzerPassWrapRegionsInSelections(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
- protobufs::TransformationSequence* transformations);
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
void Apply() override;
diff --git a/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp
new file mode 100644
index 00000000..35adcfec
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp
@@ -0,0 +1,144 @@
+// Copyright (c) 2021 Shiyu Liu
+//
+// 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/fuzz/fuzzer_pass_wrap_vector_synonym.h"
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_composite_construct.h"
+#include "source/fuzz/transformation_wrap_vector_synonym.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassWrapVectorSynonym::FuzzerPassWrapVectorSynonym(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations, ignore_inapplicable_transformations) {}
+
+void FuzzerPassWrapVectorSynonym::Apply() {
+ ForEachInstructionWithInstructionDescriptor(
+ [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
+ opt::BasicBlock::iterator instruction_iterator,
+ const protobufs::InstructionDescriptor& instruction_descriptor)
+ -> void {
+
+ // Randomly decide whether to wrap it to a vector operation.
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfWrappingVectorSynonym())) {
+ return;
+ }
+
+ // The transformation is not applicable if the instruction has missing
+ // result id, type id, or is not supported type.
+ if (!TransformationWrapVectorSynonym::IsInstructionSupported(
+ GetIRContext(), *instruction_iterator)) {
+ return;
+ }
+
+ // It must be valid to insert an OpCompositeConstruct instruction
+ // before |instruction_iterator|.
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+ SpvOpCompositeConstruct, instruction_iterator)) {
+ return;
+ }
+
+ // Get the scalar operands from the original instruction.
+ opt::Instruction* operand1 = GetIRContext()->get_def_use_mgr()->GetDef(
+ instruction_iterator->GetSingleWordInOperand(0));
+ opt::Instruction* operand2 = GetIRContext()->get_def_use_mgr()->GetDef(
+ instruction_iterator->GetSingleWordInOperand(1));
+
+ // We need to be able to make a synonym of the scalar operation's result
+ // id, as well as the operand ids (for example, they cannot be
+ // irrelevant).
+ if (!fuzzerutil::CanMakeSynonymOf(GetIRContext(),
+ *GetTransformationContext(),
+ *instruction_iterator)) {
+ return;
+ }
+ if (!fuzzerutil::CanMakeSynonymOf(
+ GetIRContext(), *GetTransformationContext(), *operand1)) {
+ return;
+ }
+ if (!fuzzerutil::CanMakeSynonymOf(
+ GetIRContext(), *GetTransformationContext(), *operand2)) {
+ return;
+ }
+
+ // Get a random vector size from 2 to 4.
+ uint32_t vector_size = GetFuzzerContext()->GetWidthOfWrappingVector();
+
+ // Randomly choose a position that target ids should be placed at.
+ // The position is in range [0, n - 1], where n is the size of the
+ // vector.
+ uint32_t position =
+ GetFuzzerContext()->GetRandomIndexForWrappingVector(vector_size);
+
+ // Stores the ids of scalar constants.
+ std::vector<uint32_t> vec1_components;
+ std::vector<uint32_t> vec2_components;
+
+ // Populate components based on vector type and size.
+ for (uint32_t i = 0; i < vector_size; ++i) {
+ if (i == position) {
+ vec1_components.emplace_back(operand1->result_id());
+ vec2_components.emplace_back(operand2->result_id());
+ } else {
+ vec1_components.emplace_back(
+ FindOrCreateZeroConstant(operand1->type_id(), true));
+ vec2_components.emplace_back(
+ FindOrCreateZeroConstant(operand2->type_id(), true));
+ }
+ }
+
+ // Add two OpCompositeConstruct to the module with result id returned.
+ // The added vectors may have different types, for instance if the
+ // scalar instruction operates on integers with differing sign.
+
+ // Add the first OpCompositeConstruct that wraps the id of the first
+ // operand.
+ uint32_t result_id1 = GetFuzzerContext()->GetFreshId();
+ ApplyTransformation(TransformationCompositeConstruct(
+ FindOrCreateVectorType(operand1->type_id(), vector_size),
+ vec1_components, instruction_descriptor, result_id1));
+
+ // Add the second OpCompositeConstruct that wraps the id of the second
+ // operand.
+ uint32_t result_id2 = GetFuzzerContext()->GetFreshId();
+ ApplyTransformation(TransformationCompositeConstruct(
+ FindOrCreateVectorType(operand2->type_id(), vector_size),
+ vec2_components, instruction_descriptor, result_id2));
+
+ // The result of the vector instruction that
+ // TransformationWrapVectorSynonym will create should be a vector of the
+ // right size, with the scalar instruction's result type as its element
+ // type. This can be distinct from the types of the operands, if the
+ // scalar instruction adds two signed integers and stores the result in
+ // an unsigned id, for example. A transformation is applied to add the
+ // right type to the module.
+ FindOrCreateVectorType(instruction_iterator->type_id(), vector_size);
+
+ // Apply transformation to do vector operation and add synonym between
+ // the result vector id and the id of the original instruction.
+ ApplyTransformation(TransformationWrapVectorSynonym(
+ instruction_iterator->result_id(), result_id1, result_id2,
+ GetFuzzerContext()->GetFreshId(), position));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_wrap_vector_synonym.h b/source/fuzz/fuzzer_pass_wrap_vector_synonym.h
new file mode 100644
index 00000000..51458383
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_wrap_vector_synonym.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2021 Shiyu Liu
+//
+// 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_FUZZ_FUZZER_PASS_WRAP_VECTOR_SYNONYM_H_
+#define SOURCE_FUZZ_FUZZER_PASS_WRAP_VECTOR_SYNONYM_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Randomly wrap a scalar operation into a vector operation.
+class FuzzerPassWrapVectorSynonym : public FuzzerPass {
+ public:
+ FuzzerPassWrapVectorSynonym(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations,
+ bool ignore_inapplicable_transformations);
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_WRAP_VECTOR_SYNONYM_H_
diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp
index 08b927e0..1d368a9f 100644
--- a/source/fuzz/fuzzer_util.cpp
+++ b/source/fuzz/fuzzer_util.cpp
@@ -252,11 +252,11 @@ bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id,
return false;
}
- // |block_id| must be reachable and be dominated by |loop_header|.
+ // |block| must be reachable and be dominated by |loop_header|.
opt::DominatorAnalysis* dominator_analysis =
context->GetDominatorAnalysis(loop_header->GetParent());
- return dominator_analysis->IsReachable(block_id) &&
- dominator_analysis->Dominates(loop_header_id, block_id);
+ return context->IsReachable(*block) &&
+ dominator_analysis->Dominates(loop_header, block);
}
bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
@@ -284,13 +284,6 @@ opt::BasicBlock::iterator GetIteratorForInstruction(
return block->end();
}
-bool BlockIsReachableInItsFunction(opt::IRContext* context,
- opt::BasicBlock* bb) {
- auto enclosing_function = bb->GetParent();
- return context->GetDominatorAnalysis(enclosing_function)
- ->Dominates(enclosing_function->entry().get(), bb);
-}
-
bool CanInsertOpcodeBeforeInstruction(
SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block) {
if (instruction_in_block->PreviousNode() &&
@@ -312,34 +305,34 @@ bool CanInsertOpcodeBeforeInstruction(
bool CanMakeSynonymOf(opt::IRContext* ir_context,
const TransformationContext& transformation_context,
- opt::Instruction* inst) {
- if (inst->opcode() == SpvOpSampledImage) {
+ const opt::Instruction& inst) {
+ if (inst.opcode() == SpvOpSampledImage) {
// The SPIR-V data rules say that only very specific instructions may
// may consume the result id of an OpSampledImage, and this excludes the
// instructions that are used for making synonyms.
return false;
}
- if (!inst->HasResultId()) {
+ if (!inst.HasResultId()) {
// We can only make a synonym of an instruction that generates an id.
return false;
}
if (transformation_context.GetFactManager()->IdIsIrrelevant(
- inst->result_id())) {
+ inst.result_id())) {
// An irrelevant id can't be a synonym of anything.
return false;
}
- if (!inst->type_id()) {
+ if (!inst.type_id()) {
// We can only make a synonym of an instruction that has a type.
return false;
}
- auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst->type_id());
+ auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst.type_id());
if (type_inst->opcode() == SpvOpTypeVoid) {
// We only make synonyms of instructions that define objects, and an object
// cannot have void type.
return false;
}
if (type_inst->opcode() == SpvOpTypePointer) {
- switch (inst->opcode()) {
+ switch (inst.opcode()) {
case SpvOpConstantNull:
case SpvOpUndef:
// We disallow making synonyms of null or undefined pointers. This is
@@ -355,7 +348,7 @@ bool CanMakeSynonymOf(opt::IRContext* ir_context,
// not decorated analogously, using the original object vs. its synonymous
// form may not be equivalent.
return ir_context->get_decoration_mgr()
- ->GetDecorationsFor(inst->result_id(), true)
+ ->GetDecorationsFor(inst.result_id(), true)
.empty();
}
@@ -462,6 +455,30 @@ uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst,
}
}
+SpvMemorySemanticsMask GetMemorySemanticsForStorageClass(
+ SpvStorageClass storage_class) {
+ switch (storage_class) {
+ case SpvStorageClassWorkgroup:
+ return SpvMemorySemanticsWorkgroupMemoryMask;
+
+ case SpvStorageClassStorageBuffer:
+ case SpvStorageClassPhysicalStorageBuffer:
+ return SpvMemorySemanticsUniformMemoryMask;
+
+ case SpvStorageClassCrossWorkgroup:
+ return SpvMemorySemanticsCrossWorkgroupMemoryMask;
+
+ case SpvStorageClassAtomicCounter:
+ return SpvMemorySemanticsAtomicCounterMemoryMask;
+
+ case SpvStorageClassImage:
+ return SpvMemorySemanticsImageMemoryMask;
+
+ default:
+ return SpvMemorySemanticsMaskNone;
+ }
+}
+
bool IsValid(const opt::IRContext* context,
spv_validator_options validator_options,
MessageConsumer consumer) {
@@ -509,8 +526,12 @@ bool IsValidAndWellFormed(const opt::IRContext* ir_context,
// this is a useful aid to debugging.
std::unordered_map<uint32_t, opt::Instruction*> unique_ids;
bool found_duplicate = false;
- ir_context->module()->ForEachInst([&consumer, &found_duplicate,
+ ir_context->module()->ForEachInst([&consumer, &found_duplicate, ir_context,
&unique_ids](opt::Instruction* inst) {
+ (void)ir_context; // Only used in an assertion; keep release-mode compilers
+ // happy.
+ assert(inst->context() == ir_context &&
+ "Instruction has wrong IR context.");
if (unique_ids.count(inst->unique_id()) != 0) {
consumer(SPV_MSG_INFO, nullptr, {},
"Two instructions have the same unique id (set a breakpoint to "
@@ -660,13 +681,12 @@ bool IdIsAvailableAtUse(opt::IRContext* context,
// It is not OK for a definition to use itself.
return false;
}
- auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function);
- if (!dominator_analysis->IsReachable(
- context->get_instr_block(use_instruction)) ||
- !dominator_analysis->IsReachable(context->get_instr_block(id))) {
+ if (!context->IsReachable(*context->get_instr_block(use_instruction)) ||
+ !context->IsReachable(*context->get_instr_block(id))) {
// Skip unreachable blocks.
return false;
}
+ auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function);
if (use_instruction->opcode() == SpvOpPhi) {
// In the case where the use is an operand to OpPhi, it is actually the
// *parent* block associated with the operand that must be dominated by
@@ -704,8 +724,8 @@ bool IdIsAvailableBeforeInstruction(opt::IRContext* context,
}
const auto* dominator_analysis =
context->GetDominatorAnalysis(function_enclosing_instruction);
- if (dominator_analysis->IsReachable(context->get_instr_block(instruction)) &&
- dominator_analysis->IsReachable(context->get_instr_block(id)) &&
+ if (context->IsReachable(*context->get_instr_block(instruction)) &&
+ context->IsReachable(*context->get_instr_block(id)) &&
dominator_analysis->Dominates(id_definition, instruction)) {
// The id's definition dominates the instruction, and both the definition
// and the instruction are in reachable blocks, thus the id is available at
@@ -715,8 +735,7 @@ bool IdIsAvailableBeforeInstruction(opt::IRContext* context,
if (id_definition->opcode() == SpvOpVariable &&
function_enclosing_instruction ==
context->get_instr_block(id)->GetParent()) {
- assert(!dominator_analysis->IsReachable(
- context->get_instr_block(instruction)) &&
+ assert(!context->IsReachable(*context->get_instr_block(instruction)) &&
"If the instruction were in a reachable block we should already "
"have returned true.");
// The id is a variable and it is in the same function as |instruction|.
@@ -796,11 +815,38 @@ uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst,
return absolute_index - inst.NumOperands() + inst.NumInOperands();
}
-bool IsNullConstantSupported(const opt::analysis::Type& type) {
- return type.AsBool() || type.AsInteger() || type.AsFloat() ||
- type.AsMatrix() || type.AsVector() || type.AsArray() ||
- type.AsStruct() || type.AsPointer() || type.AsEvent() ||
- type.AsDeviceEvent() || type.AsReserveId() || type.AsQueue();
+bool IsNullConstantSupported(opt::IRContext* ir_context,
+ const opt::Instruction& type_inst) {
+ switch (type_inst.opcode()) {
+ case SpvOpTypeArray:
+ case SpvOpTypeBool:
+ case SpvOpTypeDeviceEvent:
+ case SpvOpTypeEvent:
+ case SpvOpTypeFloat:
+ case SpvOpTypeInt:
+ case SpvOpTypeMatrix:
+ case SpvOpTypeQueue:
+ case SpvOpTypeReserveId:
+ case SpvOpTypeVector:
+ case SpvOpTypeStruct:
+ return true;
+ case SpvOpTypePointer:
+ // Null pointers are allowed if the VariablePointers capability is
+ // enabled, or if the VariablePointersStorageBuffer capability is enabled
+ // and the pointer type has StorageBuffer as its storage class.
+ if (ir_context->get_feature_mgr()->HasCapability(
+ SpvCapabilityVariablePointers)) {
+ return true;
+ }
+ if (ir_context->get_feature_mgr()->HasCapability(
+ SpvCapabilityVariablePointersStorageBuffer)) {
+ return type_inst.GetSingleWordInOperand(0) ==
+ SpvStorageClassStorageBuffer;
+ }
+ return false;
+ default:
+ return false;
+ }
}
bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
@@ -1625,6 +1671,44 @@ bool IdUseCanBeReplaced(opt::IRContext* ir_context,
return false;
}
+ if (ir_context->get_feature_mgr()->HasCapability(SpvCapabilityShader)) {
+ // With the Shader capability, memory scope and memory semantics operands
+ // are required to be constants, so they cannot be replaced arbitrarily.
+ switch (use_instruction->opcode()) {
+ case SpvOpAtomicLoad:
+ case SpvOpAtomicStore:
+ case SpvOpAtomicExchange:
+ case SpvOpAtomicIIncrement:
+ case SpvOpAtomicIDecrement:
+ case SpvOpAtomicIAdd:
+ case SpvOpAtomicISub:
+ case SpvOpAtomicSMin:
+ case SpvOpAtomicUMin:
+ case SpvOpAtomicSMax:
+ case SpvOpAtomicUMax:
+ case SpvOpAtomicAnd:
+ case SpvOpAtomicOr:
+ case SpvOpAtomicXor:
+ if (use_in_operand_index == 1 || use_in_operand_index == 2) {
+ return false;
+ }
+ break;
+ case SpvOpAtomicCompareExchange:
+ if (use_in_operand_index == 1 || use_in_operand_index == 2 ||
+ use_in_operand_index == 3) {
+ return false;
+ }
+ break;
+ case SpvOpAtomicCompareExchangeWeak:
+ case SpvOpAtomicFlagTestAndSet:
+ case SpvOpAtomicFlagClear:
+ case SpvOpAtomicFAddEXT:
+ assert(false && "Not allowed with the Shader capability.");
+ default:
+ break;
+ }
+ }
+
return true;
}
@@ -1883,7 +1967,7 @@ bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context,
// all its dependencies satisfy domination rules (i.e. all id operands
// dominate that instruction).
for (const auto& block : *mutated_block->GetParent()) {
- if (!dominator_analysis.IsReachable(&block)) {
+ if (!ir_context->IsReachable(block)) {
// If some block is not reachable then we don't need to worry about the
// preservation of domination rules for its instructions.
continue;
@@ -1934,6 +2018,93 @@ opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context,
});
}
+// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3582): Add all
+// opcodes that are agnostic to signedness of operands to function.
+// This is not exhaustive yet.
+bool IsAgnosticToSignednessOfOperand(SpvOp opcode,
+ uint32_t use_in_operand_index) {
+ switch (opcode) {
+ case SpvOpSNegate:
+ case SpvOpNot:
+ case SpvOpIAdd:
+ case SpvOpISub:
+ case SpvOpIMul:
+ case SpvOpSDiv:
+ case SpvOpSRem:
+ case SpvOpSMod:
+ case SpvOpShiftRightLogical:
+ case SpvOpShiftRightArithmetic:
+ case SpvOpShiftLeftLogical:
+ case SpvOpBitwiseOr:
+ case SpvOpBitwiseXor:
+ case SpvOpBitwiseAnd:
+ case SpvOpIEqual:
+ case SpvOpINotEqual:
+ case SpvOpULessThan:
+ case SpvOpSLessThan:
+ case SpvOpUGreaterThan:
+ case SpvOpSGreaterThan:
+ case SpvOpULessThanEqual:
+ case SpvOpSLessThanEqual:
+ case SpvOpUGreaterThanEqual:
+ case SpvOpSGreaterThanEqual:
+ return true;
+
+ case SpvOpAtomicStore:
+ case SpvOpAtomicExchange:
+ case SpvOpAtomicIAdd:
+ case SpvOpAtomicISub:
+ case SpvOpAtomicSMin:
+ case SpvOpAtomicUMin:
+ case SpvOpAtomicSMax:
+ case SpvOpAtomicUMax:
+ case SpvOpAtomicAnd:
+ case SpvOpAtomicOr:
+ case SpvOpAtomicXor:
+ case SpvOpAtomicFAddEXT: // Capability AtomicFloat32AddEXT,
+ // AtomicFloat64AddEXT.
+ assert(use_in_operand_index != 0 &&
+ "Signedness check should not occur on a pointer operand.");
+ return use_in_operand_index == 1 || use_in_operand_index == 2;
+
+ case SpvOpAtomicCompareExchange:
+ case SpvOpAtomicCompareExchangeWeak: // Capability Kernel.
+ assert(use_in_operand_index != 0 &&
+ "Signedness check should not occur on a pointer operand.");
+ return use_in_operand_index >= 1 && use_in_operand_index <= 3;
+
+ case SpvOpAtomicLoad:
+ case SpvOpAtomicIIncrement:
+ case SpvOpAtomicIDecrement:
+ case SpvOpAtomicFlagTestAndSet: // Capability Kernel.
+ case SpvOpAtomicFlagClear: // Capability Kernel.
+ assert(use_in_operand_index != 0 &&
+ "Signedness check should not occur on a pointer operand.");
+ return use_in_operand_index >= 1;
+
+ case SpvOpAccessChain:
+ // The signedness of indices does not matter.
+ return use_in_operand_index > 0;
+
+ default:
+ // Conservatively assume that the id cannot be swapped in other
+ // instructions.
+ return false;
+ }
+}
+
+bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode,
+ uint32_t use_in_operand_index, uint32_t type_id_1,
+ uint32_t type_id_2) {
+ assert(ir_context->get_type_mgr()->GetType(type_id_1) &&
+ ir_context->get_type_mgr()->GetType(type_id_2) &&
+ "Type ids are invalid");
+
+ return type_id_1 == type_id_2 ||
+ (IsAgnosticToSignednessOfOperand(opcode, use_in_operand_index) &&
+ fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_1, type_id_2));
+}
+
} // namespace fuzzerutil
} // namespace fuzz
} // namespace spvtools
diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h
index dd7bd961..54aa14a2 100644
--- a/source/fuzz/fuzzer_util.h
+++ b/source/fuzz/fuzzer_util.h
@@ -108,11 +108,6 @@ bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
opt::BasicBlock::iterator GetIteratorForInstruction(
opt::BasicBlock* block, const opt::Instruction* inst);
-// Returns true if and only if there is a path to |bb| from the entry block of
-// the function that contains |bb|.
-bool BlockIsReachableInItsFunction(opt::IRContext* context,
- opt::BasicBlock* bb);
-
// Determines whether it is OK to insert an instruction with opcode |opcode|
// before |instruction_in_block|.
bool CanInsertOpcodeBeforeInstruction(
@@ -123,7 +118,7 @@ bool CanInsertOpcodeBeforeInstruction(
// does not participate in IdIsIrrelevant fact.
bool CanMakeSynonymOf(opt::IRContext* ir_context,
const TransformationContext& transformation_context,
- opt::Instruction* inst);
+ const opt::Instruction& inst);
// Determines whether the given type is a composite; that is: an array, matrix,
// struct or vector.
@@ -174,6 +169,10 @@ uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst,
opt::IRContext* ir_context);
+// Returns memory semantics mask for specific storage class.
+SpvMemorySemanticsMask GetMemorySemanticsForStorageClass(
+ SpvStorageClass storage_class);
+
// Returns true if and only if |context| is valid, according to the validator
// instantiated with |validator_options|. |consumer| is used for error
// reporting.
@@ -278,8 +277,10 @@ uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst,
uint32_t absolute_index);
// Returns true if and only if |type| is one of the types for which it is legal
-// to have an OpConstantNull value.
-bool IsNullConstantSupported(const opt::analysis::Type& type);
+// to have an OpConstantNull value. This may depend on the capabilities declared
+// in |context|.
+bool IsNullConstantSupported(opt::IRContext* context,
+ const opt::Instruction& type);
// Returns true if and only if the SPIR-V version being used requires that
// global variables accessed in the static call graph of an entry point need
@@ -603,6 +604,21 @@ bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context,
opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context,
uint32_t function_id);
+// Returns true if the instruction with opcode |opcode| does not change its
+// behaviour depending on the signedness of the operand at
+// |use_in_operand_index|.
+// Assumes that the operand must be the id of an integer scalar or vector.
+bool IsAgnosticToSignednessOfOperand(SpvOp opcode,
+ uint32_t use_in_operand_index);
+
+// Returns true if |type_id_1| and |type_id_2| represent compatible types
+// given the context of the instruction with |opcode| (i.e. we can replace
+// an operand of |opcode| of the first type with an id of the second type
+// and vice-versa).
+bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode,
+ uint32_t use_in_operand_index, uint32_t type_id_1,
+ uint32_t type_id_2);
+
} // namespace fuzzerutil
} // namespace fuzz
} // namespace spvtools
diff --git a/source/fuzz/pass_management/repeated_pass_instances.h b/source/fuzz/pass_management/repeated_pass_instances.h
index 80ac0875..da61fdab 100644
--- a/source/fuzz/pass_management/repeated_pass_instances.h
+++ b/source/fuzz/pass_management/repeated_pass_instances.h
@@ -73,6 +73,7 @@
#include "source/fuzz/fuzzer_pass_split_blocks.h"
#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
#include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h"
+#include "source/fuzz/fuzzer_pass_wrap_vector_synonym.h"
namespace spvtools {
namespace fuzz {
@@ -168,6 +169,7 @@ class RepeatedPassInstances {
REPEATED_PASS_INSTANCE(SplitBlocks);
REPEATED_PASS_INSTANCE(SwapBranchConditionalOperands);
REPEATED_PASS_INSTANCE(WrapRegionsInSelections);
+ REPEATED_PASS_INSTANCE(WrapVectorSynonym);
#undef REPEATED_PASS_INSTANCE
public:
diff --git a/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp b/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp
index a933848d..6c61c0d4 100644
--- a/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp
+++ b/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp
@@ -351,6 +351,12 @@ RepeatedPassRecommenderStandard::GetFuturePassRecommendations(
pass_instances_->GetReplaceIrrelevantIds(),
pass_instances_->GetFlattenConditionalBranches()});
}
+ if (&pass == pass_instances_->GetWrapVectorSynonym()) {
+ // This transformation introduces synonym facts and irrelevant ids.
+ return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms(),
+ pass_instances_->GetReplaceIrrelevantIds()});
+ }
+
assert(false && "Unreachable: every fuzzer pass should be dealt with.");
return {};
}
diff --git a/source/fuzz/protobufs/spirvfuzz_protobufs.h b/source/fuzz/protobufs/spirvfuzz_protobufs.h
index eb8cb145..46c21881 100644
--- a/source/fuzz/protobufs/spirvfuzz_protobufs.h
+++ b/source/fuzz/protobufs/spirvfuzz_protobufs.h
@@ -23,8 +23,11 @@
#if defined(__clang__)
#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-parameter"
+#pragma clang diagnostic ignored "-Wunknown-warning-option" // Must come first
+#pragma clang diagnostic ignored "-Wreserved-identifier"
#pragma clang diagnostic ignored "-Wshadow"
+#pragma clang diagnostic ignored "-Wsuggest-destructor-override"
+#pragma clang diagnostic ignored "-Wunused-parameter"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index 657e8076..e71b6a3f 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -559,6 +559,7 @@ message Transformation {
TransformationExpandVectorReduction expand_vector_reduction = 85;
TransformationSwapFunctionVariables swap_function_variables = 86;
TransformationSwapTwoFunctions swap_two_functions = 87;
+ TransformationWrapVectorSynonym wrap_vector_synonym = 88;
// Add additional option using the next available number.
}
}
@@ -1568,17 +1569,26 @@ message TransformationInvertComparisonOperator {
message TransformationLoad {
- // Transformation that adds an OpLoad instruction from a pointer into an id.
+ // Transformation that adds an OpLoad or OpAtomicLoad instruction from a pointer into an id.
- // The result of the load instruction
+ // The result of the load instruction.
uint32 fresh_id = 1;
- // The pointer to be loaded from
+ // The pointer to be loaded from.
uint32 pointer_id = 2;
+ // True if and only if the load should be atomic.
+ bool is_atomic = 3;
+
+ // The memory scope for the atomic load. Ignored unless |is_atomic| is true.
+ uint32 memory_scope_id = 4;
+
+ // The memory semantics for the atomic load. Ignored unless |is_atomic| is true.
+ uint32 memory_semantics_id = 5;
+
// A descriptor for an instruction in a block before which the new OpLoad
- // instruction should be inserted
- InstructionDescriptor instruction_to_insert_before = 3;
+ // instruction should be inserted.
+ InstructionDescriptor instruction_to_insert_before = 6;
}
@@ -1951,10 +1961,10 @@ message TransformationReplaceBooleanConstantWithConstantBinary {
// A descriptor for the boolean constant id we would like to replace
IdUseDescriptor id_use_descriptor = 1;
- // Id for the constant to be used on the LHS of the comparision
+ // Id for the constant to be used on the LHS of the comparison
uint32 lhs_id = 2;
- // Id for the constant to be used on the RHS of the comparision
+ // Id for the constant to be used on the RHS of the comparison
uint32 rhs_id = 3;
// Opcode for binary operator
@@ -2235,17 +2245,26 @@ message TransformationSplitBlock {
message TransformationStore {
- // Transformation that adds an OpStore instruction of an id to a pointer.
+ // Transformation that adds an OpStore or OpAtomicStore instruction of an id to a pointer.
- // The pointer to be stored to
+ // The pointer to be stored to.
uint32 pointer_id = 1;
- // The value to be stored
- uint32 value_id = 2;
+ // True if and only if the load should be atomic.
+ bool is_atomic = 2;
+
+ // The memory scope for the atomic load. Ignored unless |is_atomic| is true.
+ uint32 memory_scope_id = 3;
+
+ // The memory semantics for the atomic load. Ignored unless |is_atomic| is true.
+ uint32 memory_semantics_id = 4;
+
+ // The value to be stored.
+ uint32 value_id = 5;
// A descriptor for an instruction in a block before which the new OpStore
- // instruction should be inserted
- InstructionDescriptor instruction_to_insert_before = 3;
+ // instruction should be inserted.
+ InstructionDescriptor instruction_to_insert_before = 6;
}
@@ -2371,3 +2390,39 @@ message TransformationWrapRegionInSelection {
bool branch_condition = 3;
}
+
+message TransformationWrapVectorSynonym {
+ // A transformation that wraps an arithmetic operation into a vector operation
+ // and get the result of the original operation from the corresponding index.
+ // For instance, for this transformation, an scalar operation between two scalars:
+ // define op ∈ {+, -, *}
+ // c = a op b
+ //
+ // requires the availability of two vectors:
+ //
+ // va = vector(..., a, ...)
+ // vb = vector(..., b, ...)
+ //
+ // where a and b are in the same position i in each of their corresponding vector
+ // and a is synonymous with va[i] and b is synonymous with vb[i].
+ //
+ // The transformation then add an instruction vc = va op vb where c is synonymous
+ // with vc[i].
+
+ // The result if of the original scalar operation instruction.
+ uint32 instruction_id = 1;
+
+ // The result id for the first vector that contains the first value of the scalar operation.
+ uint32 vector_operand1 = 2;
+
+ // The result id for the second vector that contains the second value of the scalar operation.
+ uint32 vector_operand2 = 3;
+
+ // A fresh id for the resulted vector from the addition of the first and second vector.
+ uint32 fresh_id = 4;
+
+ // The position in the vector where the value of original instruction is located. Must be in
+ // the corresponding vector range.
+ uint32 scalar_position = 5;
+
+}
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index 4ea0c773..70a302b7 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -104,6 +104,7 @@
#include "source/fuzz/transformation_vector_shuffle.h"
#include "source/fuzz/transformation_wrap_early_terminator_in_function.h"
#include "source/fuzz/transformation_wrap_region_in_selection.h"
+#include "source/fuzz/transformation_wrap_vector_synonym.h"
#include "source/util/make_unique.h"
namespace spvtools {
@@ -382,6 +383,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
case protobufs::Transformation::TransformationCase::kWrapRegionInSelection:
return MakeUnique<TransformationWrapRegionInSelection>(
message.wrap_region_in_selection());
+ case protobufs::Transformation::TransformationCase::kWrapVectorSynonym:
+ return MakeUnique<TransformationWrapVectorSynonym>(
+ message.wrap_vector_synonym());
case protobufs::Transformation::TRANSFORMATION_NOT_SET:
assert(false && "An unset transformation was encountered.");
return nullptr;
diff --git a/source/fuzz/transformation_add_constant_composite.cpp b/source/fuzz/transformation_add_constant_composite.cpp
index e6cd5a96..89007ab6 100644
--- a/source/fuzz/transformation_add_constant_composite.cpp
+++ b/source/fuzz/transformation_add_constant_composite.cpp
@@ -75,7 +75,7 @@ bool TransformationAddConstantComposite::IsApplicable(
// We do not create constants of structs decorated with Block nor
// BufferBlock. The SPIR-V spec does not explicitly disallow this, but it
// seems like a strange thing to do, so we disallow it to avoid triggering
- // low priorty edge case issues related to it.
+ // low priority edge case issues related to it.
if (fuzzerutil::HasBlockOrBufferBlockDecoration(
ir_context, composite_type_instruction->result_id())) {
return false;
diff --git a/source/fuzz/transformation_add_constant_null.cpp b/source/fuzz/transformation_add_constant_null.cpp
index 32544e6d..c0f73670 100644
--- a/source/fuzz/transformation_add_constant_null.cpp
+++ b/source/fuzz/transformation_add_constant_null.cpp
@@ -35,14 +35,14 @@ bool TransformationAddConstantNull::IsApplicable(
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
return false;
}
- auto type = context->get_type_mgr()->GetType(message_.type_id());
+ auto type = context->get_def_use_mgr()->GetDef(message_.type_id());
// The type must exist.
if (!type) {
return false;
}
// The type must be one of the types for which null constants are allowed,
// according to the SPIR-V spec.
- return fuzzerutil::IsNullConstantSupported(*type);
+ return fuzzerutil::IsNullConstantSupported(context, *type);
}
void TransformationAddConstantNull::Apply(
diff --git a/source/fuzz/transformation_add_dead_block.cpp b/source/fuzz/transformation_add_dead_block.cpp
index 82e8cd8f..df700ce5 100644
--- a/source/fuzz/transformation_add_dead_block.cpp
+++ b/source/fuzz/transformation_add_dead_block.cpp
@@ -79,9 +79,7 @@ bool TransformationAddDeadBlock::IsApplicable(
}
// |existing_block| must be reachable.
- opt::DominatorAnalysis* dominator_analysis =
- ir_context->GetDominatorAnalysis(existing_block->GetParent());
- if (!dominator_analysis->IsReachable(existing_block->id())) {
+ if (!ir_context->IsReachable(*existing_block)) {
return false;
}
@@ -94,6 +92,8 @@ bool TransformationAddDeadBlock::IsApplicable(
// the selection construct, its header |existing_block| will not dominate the
// merge block |successor_block_id|, which is invalid. Thus, |existing_block|
// must dominate |successor_block_id|.
+ opt::DominatorAnalysis* dominator_analysis =
+ ir_context->GetDominatorAnalysis(existing_block->GetParent());
if (!dominator_analysis->Dominates(existing_block->id(),
successor_block_id)) {
return false;
diff --git a/source/fuzz/transformation_add_dead_break.cpp b/source/fuzz/transformation_add_dead_break.cpp
index ad46ce7f..32080ca4 100644
--- a/source/fuzz/transformation_add_dead_break.cpp
+++ b/source/fuzz/transformation_add_dead_break.cpp
@@ -134,7 +134,7 @@ bool TransformationAddDeadBreak::IsApplicable(
return false;
}
- if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, bb_to)) {
+ if (!ir_context->IsReachable(*bb_to)) {
// If the target of the break is unreachable, we conservatively do not
// allow adding a dead break, to avoid the compilations that arise due to
// the lack of sensible dominance information for unreachable blocks.
diff --git a/source/fuzz/transformation_add_dead_continue.cpp b/source/fuzz/transformation_add_dead_continue.cpp
index be6294e8..f2b9ab3f 100644
--- a/source/fuzz/transformation_add_dead_continue.cpp
+++ b/source/fuzz/transformation_add_dead_continue.cpp
@@ -83,8 +83,7 @@ bool TransformationAddDeadContinue::IsApplicable(
auto continue_block =
ir_context->cfg()->block(loop_header)->ContinueBlockId();
- if (!fuzzerutil::BlockIsReachableInItsFunction(
- ir_context, ir_context->cfg()->block(continue_block))) {
+ if (!ir_context->IsReachable(*ir_context->cfg()->block(continue_block))) {
// If the loop's continue block is unreachable, we conservatively do not
// allow adding a dead continue, to avoid the compilations that arise due to
// the lack of sensible dominance information for unreachable blocks.
diff --git a/source/fuzz/transformation_add_global_undef.cpp b/source/fuzz/transformation_add_global_undef.cpp
index eb390ea0..ec0574a4 100644
--- a/source/fuzz/transformation_add_global_undef.cpp
+++ b/source/fuzz/transformation_add_global_undef.cpp
@@ -15,6 +15,7 @@
#include "source/fuzz/transformation_add_global_undef.h"
#include "source/fuzz/fuzzer_util.h"
+#include "source/opt/reflect.h"
namespace spvtools {
namespace fuzz {
@@ -35,9 +36,11 @@ bool TransformationAddGlobalUndef::IsApplicable(
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
return false;
}
- auto type = ir_context->get_type_mgr()->GetType(message_.type_id());
- // The type must exist, and must not be a function type.
- return type && !type->AsFunction();
+ auto type = ir_context->get_def_use_mgr()->GetDef(message_.type_id());
+ // The type must exist, and must not be a function or pointer type.
+ return type != nullptr && opt::IsTypeInst(type->opcode()) &&
+ type->opcode() != SpvOpTypeFunction &&
+ type->opcode() != SpvOpTypePointer;
}
void TransformationAddGlobalUndef::Apply(
diff --git a/source/fuzz/transformation_add_global_undef.h b/source/fuzz/transformation_add_global_undef.h
index 37542c3f..fff1ad9d 100644
--- a/source/fuzz/transformation_add_global_undef.h
+++ b/source/fuzz/transformation_add_global_undef.h
@@ -31,7 +31,7 @@ class TransformationAddGlobalUndef : public Transformation {
TransformationAddGlobalUndef(uint32_t fresh_id, uint32_t type_id);
// - |message_.fresh_id| must be fresh
- // - |message_.type_id| must be the id of a non-function type
+ // - |message_.type_id| must be the id of a non-function, non-pointer type
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
diff --git a/source/fuzz/transformation_add_synonym.cpp b/source/fuzz/transformation_add_synonym.cpp
index a1949fbc..69269e5e 100644
--- a/source/fuzz/transformation_add_synonym.cpp
+++ b/source/fuzz/transformation_add_synonym.cpp
@@ -151,7 +151,8 @@ bool TransformationAddSynonym::IsInstructionValid(
return false;
}
- if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, inst)) {
+ if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+ *inst)) {
return false;
}
diff --git a/source/fuzz/transformation_composite_construct.cpp b/source/fuzz/transformation_composite_construct.cpp
index 0cd2308b..2d8e5991 100644
--- a/source/fuzz/transformation_composite_construct.cpp
+++ b/source/fuzz/transformation_composite_construct.cpp
@@ -297,7 +297,7 @@ void TransformationCompositeConstruct::AddDataSynonymFacts(
composite_type->AsVector() && component_type->AsVector();
if (!fuzzerutil::CanMakeSynonymOf(
ir_context, *transformation_context,
- ir_context->get_def_use_mgr()->GetDef(component))) {
+ *ir_context->get_def_use_mgr()->GetDef(component))) {
// We can't make a synonym of this component, so we skip on to the next
// component. In the case where we're packing a vector into a vector we
// have to skip as many components of the resulting vectors as there are
diff --git a/source/fuzz/transformation_composite_extract.cpp b/source/fuzz/transformation_composite_extract.cpp
index 647cd74e..0fbd4e1b 100644
--- a/source/fuzz/transformation_composite_extract.cpp
+++ b/source/fuzz/transformation_composite_extract.cpp
@@ -125,7 +125,7 @@ void TransformationCompositeExtract::AddDataSynonymFacts(
// or if the result id into which we are extracting is irrelevant.
if (!fuzzerutil::CanMakeSynonymOf(
ir_context, *transformation_context,
- ir_context->get_def_use_mgr()->GetDef(message_.composite_id())) ||
+ *ir_context->get_def_use_mgr()->GetDef(message_.composite_id())) ||
transformation_context->GetFactManager()->IdIsIrrelevant(
message_.fresh_id())) {
return;
diff --git a/source/fuzz/transformation_composite_insert.cpp b/source/fuzz/transformation_composite_insert.cpp
index 05162bfc..60fa5628 100644
--- a/source/fuzz/transformation_composite_insert.cpp
+++ b/source/fuzz/transformation_composite_insert.cpp
@@ -219,9 +219,9 @@ void TransformationCompositeInsert::AddDataSynonymFacts(
continue;
}
current_index.push_back(i);
- if (fuzzerutil::CanMakeSynonymOf(
- ir_context, *transformation_context,
- ir_context->get_def_use_mgr()->GetDef(message_.composite_id()))) {
+ if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context,
+ *ir_context->get_def_use_mgr()->GetDef(
+ message_.composite_id()))) {
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(message_.fresh_id(), current_index),
MakeDataDescriptor(message_.composite_id(), current_index));
@@ -235,7 +235,7 @@ void TransformationCompositeInsert::AddDataSynonymFacts(
// synonymous with the result of the insert instruction at the given index.
if (fuzzerutil::CanMakeSynonymOf(
ir_context, *transformation_context,
- ir_context->get_def_use_mgr()->GetDef(message_.object_id()))) {
+ *ir_context->get_def_use_mgr()->GetDef(message_.object_id()))) {
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(message_.object_id(), {}),
MakeDataDescriptor(message_.fresh_id(), index));
diff --git a/source/fuzz/transformation_duplicate_region_with_selection.cpp b/source/fuzz/transformation_duplicate_region_with_selection.cpp
index dee1207f..db88610f 100644
--- a/source/fuzz/transformation_duplicate_region_with_selection.cpp
+++ b/source/fuzz/transformation_duplicate_region_with_selection.cpp
@@ -309,9 +309,9 @@ void TransformationDuplicateRegionWithSelection::Apply(
// Construct the merge block.
std::unique_ptr<opt::BasicBlock> merge_block =
- MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(opt::Instruction(
+ MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
ir_context, SpvOpLabel, 0, message_.merge_label_fresh_id(),
- opt::Instruction::OperandList())));
+ opt::Instruction::OperandList()));
// Get the maps from the protobuf.
std::map<uint32_t, uint32_t> original_label_to_duplicate_label =
@@ -325,7 +325,7 @@ void TransformationDuplicateRegionWithSelection::Apply(
std::map<uint32_t, uint32_t> original_id_to_phi_id =
fuzzerutil::RepeatedUInt32PairToMap(message_.original_id_to_phi_id());
- // Use oveflow ids to fill in any required ids that are missing from these
+ // Use overflow ids to fill in any required ids that are missing from these
// maps.
for (auto block : region_blocks) {
if (original_label_to_duplicate_label.count(block->id()) == 0) {
diff --git a/source/fuzz/transformation_expand_vector_reduction.cpp b/source/fuzz/transformation_expand_vector_reduction.cpp
index 99387066..bafcf929 100644
--- a/source/fuzz/transformation_expand_vector_reduction.cpp
+++ b/source/fuzz/transformation_expand_vector_reduction.cpp
@@ -129,7 +129,7 @@ void TransformationExpandVectorReduction::Apply(
// If it's possible to make a synonym of |instruction|, then add the fact that
// the last |logical_instruction| is a synonym of |instruction|.
if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context,
- instruction)) {
+ *instruction)) {
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(logical_instruction.result_id(), {}),
MakeDataDescriptor(instruction->result_id(), {}));
diff --git a/source/fuzz/transformation_flatten_conditional_branch.cpp b/source/fuzz/transformation_flatten_conditional_branch.cpp
index b8c6de0c..127e7628 100644
--- a/source/fuzz/transformation_flatten_conditional_branch.cpp
+++ b/source/fuzz/transformation_flatten_conditional_branch.cpp
@@ -441,17 +441,17 @@ bool TransformationFlattenConditionalBranch::
header->terminator()->opcode() == SpvOpBranchConditional &&
"|header| must be the header of a conditional.");
+ // |header| must be reachable.
+ if (!ir_context->IsReachable(*header)) {
+ return false;
+ }
+
auto enclosing_function = header->GetParent();
auto dominator_analysis =
ir_context->GetDominatorAnalysis(enclosing_function);
auto postdominator_analysis =
ir_context->GetPostDominatorAnalysis(enclosing_function);
- // |header| must be reachable.
- if (!dominator_analysis->IsReachable(header)) {
- return false;
- }
-
// Check that the header and the merge block describe a single-entry,
// single-exit region.
if (!dominator_analysis->Dominates(header->id(), merge_block_id) ||
diff --git a/source/fuzz/transformation_load.cpp b/source/fuzz/transformation_load.cpp
index e22f8dd2..bf48d996 100644
--- a/source/fuzz/transformation_load.cpp
+++ b/source/fuzz/transformation_load.cpp
@@ -24,10 +24,15 @@ TransformationLoad::TransformationLoad(protobufs::TransformationLoad message)
: message_(std::move(message)) {}
TransformationLoad::TransformationLoad(
- uint32_t fresh_id, uint32_t pointer_id,
+ uint32_t fresh_id, uint32_t pointer_id, bool is_atomic,
+ uint32_t memory_scope, uint32_t memory_semantics,
const protobufs::InstructionDescriptor& instruction_to_insert_before) {
message_.set_fresh_id(fresh_id);
message_.set_pointer_id(pointer_id);
+ message_.set_is_atomic(is_atomic);
+ message_.set_memory_scope_id(memory_scope);
+ message_.set_memory_semantics_id(memory_semantics);
+
*message_.mutable_instruction_to_insert_before() =
instruction_to_insert_before;
}
@@ -68,11 +73,98 @@ bool TransformationLoad::IsApplicable(
if (!insert_before) {
return false;
}
- // ... and it must be legitimate to insert a store before it.
- if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, insert_before)) {
+ // ... and it must be legitimate to insert a load before it.
+ if (!message_.is_atomic() &&
+ !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, insert_before)) {
+ return false;
+ }
+
+ if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction(
+ SpvOpAtomicLoad, insert_before)) {
return false;
}
+ if (message_.is_atomic()) {
+ // Check the exists of memory scope and memory semantics ids.
+ auto memory_scope_instruction =
+ ir_context->get_def_use_mgr()->GetDef(message_.memory_scope_id());
+ auto memory_semantics_instruction =
+ ir_context->get_def_use_mgr()->GetDef(message_.memory_semantics_id());
+
+ if (!memory_scope_instruction) {
+ return false;
+ }
+ if (!memory_semantics_instruction) {
+ return false;
+ }
+ // The memory scope and memory semantics instructions must have the
+ // 'OpConstant' opcode.
+ if (memory_scope_instruction->opcode() != SpvOpConstant) {
+ return false;
+ }
+ if (memory_semantics_instruction->opcode() != SpvOpConstant) {
+ return false;
+ }
+ // The memory scope and memory semantics need to be available before
+ // |insert_before|.
+ if (!fuzzerutil::IdIsAvailableBeforeInstruction(
+ ir_context, insert_before, message_.memory_scope_id())) {
+ return false;
+ }
+ if (!fuzzerutil::IdIsAvailableBeforeInstruction(
+ ir_context, insert_before, message_.memory_semantics_id())) {
+ return false;
+ }
+ // The memory scope and memory semantics instructions must have an Integer
+ // operand type with signedness does not matters.
+ if (ir_context->get_def_use_mgr()
+ ->GetDef(memory_scope_instruction->type_id())
+ ->opcode() != SpvOpTypeInt) {
+ return false;
+ }
+ if (ir_context->get_def_use_mgr()
+ ->GetDef(memory_semantics_instruction->type_id())
+ ->opcode() != SpvOpTypeInt) {
+ return false;
+ }
+
+ // The size of the integer for memory scope and memory semantics
+ // instructions must be equal to 32 bits.
+ auto memory_scope_int_width =
+ ir_context->get_def_use_mgr()
+ ->GetDef(memory_scope_instruction->type_id())
+ ->GetSingleWordInOperand(0);
+ auto memory_semantics_int_width =
+ ir_context->get_def_use_mgr()
+ ->GetDef(memory_semantics_instruction->type_id())
+ ->GetSingleWordInOperand(0);
+
+ if (memory_scope_int_width != 32) {
+ return false;
+ }
+ if (memory_semantics_int_width != 32) {
+ return false;
+ }
+
+ // The memory scope constant value must be that of SpvScopeInvocation.
+ auto memory_scope_const_value =
+ memory_scope_instruction->GetSingleWordInOperand(0);
+ if (memory_scope_const_value != SpvScopeInvocation) {
+ return false;
+ }
+
+ // The memory semantics constant value must match the storage class of the
+ // pointer being loaded from.
+ auto memory_semantics_const_value = static_cast<SpvMemorySemanticsMask>(
+ memory_semantics_instruction->GetSingleWordInOperand(0));
+ if (memory_semantics_const_value !=
+ fuzzerutil::GetMemorySemanticsForStorageClass(
+ static_cast<SpvStorageClass>(
+ pointer_type->GetSingleWordInOperand(0)))) {
+ return false;
+ }
+ }
+
// The pointer needs to be available at the insertion point.
return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
message_.pointer_id());
@@ -80,22 +172,46 @@ bool TransformationLoad::IsApplicable(
void TransformationLoad::Apply(opt::IRContext* ir_context,
TransformationContext* /*unused*/) const {
- uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
- ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id()));
- fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
- auto insert_before =
- FindInstruction(message_.instruction_to_insert_before(), ir_context);
- auto new_instruction = MakeUnique<opt::Instruction>(
- ir_context, SpvOpLoad, result_type, message_.fresh_id(),
- opt::Instruction::OperandList(
- {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}}));
- auto new_instruction_ptr = new_instruction.get();
- insert_before->InsertBefore(std::move(new_instruction));
- // Inform the def-use manager about the new instruction and record its basic
- // block.
- ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
- ir_context->set_instr_block(new_instruction_ptr,
- ir_context->get_instr_block(insert_before));
+ if (message_.is_atomic()) {
+ // OpAtomicLoad instruction.
+ uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
+ ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id()));
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+ auto insert_before =
+ FindInstruction(message_.instruction_to_insert_before(), ir_context);
+ auto new_instruction = MakeUnique<opt::Instruction>(
+ ir_context, SpvOpAtomicLoad, result_type, message_.fresh_id(),
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
+ {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}},
+ {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID,
+ {message_.memory_semantics_id()}}}));
+ auto new_instruction_ptr = new_instruction.get();
+ insert_before->InsertBefore(std::move(new_instruction));
+ // Inform the def-use manager about the new instruction and record its basic
+ // block.
+ ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+ ir_context->set_instr_block(new_instruction_ptr,
+ ir_context->get_instr_block(insert_before));
+ } else {
+ // OpLoad instruction.
+ uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
+ ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id()));
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+ auto insert_before =
+ FindInstruction(message_.instruction_to_insert_before(), ir_context);
+ auto new_instruction = MakeUnique<opt::Instruction>(
+ ir_context, SpvOpLoad, result_type, message_.fresh_id(),
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}}));
+ auto new_instruction_ptr = new_instruction.get();
+ insert_before->InsertBefore(std::move(new_instruction));
+ // Inform the def-use manager about the new instruction and record its basic
+ // block.
+ ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+ ir_context->set_instr_block(new_instruction_ptr,
+ ir_context->get_instr_block(insert_before));
+ }
}
protobufs::Transformation TransformationLoad::ToMessage() const {
diff --git a/source/fuzz/transformation_load.h b/source/fuzz/transformation_load.h
index d10b0073..57b4a535 100644
--- a/source/fuzz/transformation_load.h
+++ b/source/fuzz/transformation_load.h
@@ -28,11 +28,20 @@ class TransformationLoad : public Transformation {
explicit TransformationLoad(protobufs::TransformationLoad message);
TransformationLoad(
- uint32_t fresh_id, uint32_t pointer_id,
+ uint32_t fresh_id, uint32_t pointer_id, bool is_atomic,
+ uint32_t memory_scope, uint32_t memory_semantics,
const protobufs::InstructionDescriptor& instruction_to_insert_before);
// - |message_.fresh_id| must be fresh
// - |message_.pointer_id| must be the id of a pointer
+ // - |message_.is_atomic| must be true if want to work with OpAtomicLoad
+ // - If |is_atomic| is true then |message_memory_scope_id| must be the id of
+ // an OpConstant 32 bit integer instruction with the value
+ // SpvScopeInvocation.
+ // - If |is_atomic| is true then |message_.memory_semantics_id| must be the id
+ // of an OpConstant 32 bit integer instruction with the values
+ // SpvMemorySemanticsWorkgroupMemoryMask or
+ // SpvMemorySemanticsUniformMemoryMask.
// - The pointer must not be OpConstantNull or OpUndef
// - |message_.instruction_to_insert_before| must identify an instruction
// before which it is valid to insert an OpLoad, and where
diff --git a/source/fuzz/transformation_merge_blocks.cpp b/source/fuzz/transformation_merge_blocks.cpp
index 22236795..dbf782e2 100644
--- a/source/fuzz/transformation_merge_blocks.cpp
+++ b/source/fuzz/transformation_merge_blocks.cpp
@@ -43,6 +43,9 @@ bool TransformationMergeBlocks::IsApplicable(
}
auto first_block = ir_context->cfg()->block(predecessors.at(0));
+ if (!ir_context->IsReachable(*first_block)) {
+ return false;
+ }
return opt::blockmergeutil::CanMergeWithSuccessor(ir_context, first_block);
}
diff --git a/source/fuzz/transformation_merge_blocks.h b/source/fuzz/transformation_merge_blocks.h
index f6306c5a..f3462fae 100644
--- a/source/fuzz/transformation_merge_blocks.h
+++ b/source/fuzz/transformation_merge_blocks.h
@@ -31,6 +31,7 @@ class TransformationMergeBlocks : public Transformation {
TransformationMergeBlocks(uint32_t block_id);
// - |message_.block_id| must be the id of a block, b
+ // - b must be statically reachable in the control flow graph of its function
// - b must have a single predecessor, a
// - b must be the sole successor of a
// - Replacing a with the merge of a and b (and removing b) must lead to a
diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp
index 84e8ac2c..3140fa6b 100644
--- a/source/fuzz/transformation_outline_function.cpp
+++ b/source/fuzz/transformation_outline_function.cpp
@@ -180,8 +180,7 @@ bool TransformationOutlineFunction::IsApplicable(
// predecessors. If it does, then we do not regard the region as single-
// entry-single-exit and hence do not outline it.
for (auto pred : ir_context->cfg()->preds(block.id())) {
- if (!fuzzerutil::BlockIsReachableInItsFunction(
- ir_context, ir_context->cfg()->block(pred))) {
+ if (!ir_context->IsReachable(*ir_context->cfg()->block(pred))) {
// The predecessor is unreachable.
return false;
}
diff --git a/source/fuzz/transformation_propagate_instruction_down.cpp b/source/fuzz/transformation_propagate_instruction_down.cpp
index 7713562e..c3b7c4d9 100644
--- a/source/fuzz/transformation_propagate_instruction_down.cpp
+++ b/source/fuzz/transformation_propagate_instruction_down.cpp
@@ -386,11 +386,8 @@ bool TransformationPropagateInstructionDown::IsApplicableToBlock(
return false;
}
- const auto* dominator_analysis =
- ir_context->GetDominatorAnalysis(block->GetParent());
-
// |block| must be reachable.
- if (!dominator_analysis->IsReachable(block)) {
+ if (!ir_context->IsReachable(*block)) {
return false;
}
@@ -430,6 +427,9 @@ bool TransformationPropagateInstructionDown::IsApplicableToBlock(
auto phi_block_id =
GetOpPhiBlockId(ir_context, block_id, *inst_to_propagate, successor_ids);
+ const auto* dominator_analysis =
+ ir_context->GetDominatorAnalysis(block->GetParent());
+
// Make sure we can adjust all users of the propagated instruction.
return ir_context->get_def_use_mgr()->WhileEachUse(
inst_to_propagate,
@@ -537,7 +537,7 @@ uint32_t TransformationPropagateInstructionDown::GetOpPhiBlockId(
// Check that |merge_block_id| is reachable in the CFG and |block_id|
// dominates |merge_block_id|.
- if (!dominator_analysis->IsReachable(merge_block_id) ||
+ if (!ir_context->IsReachable(*ir_context->cfg()->block(merge_block_id)) ||
!dominator_analysis->Dominates(block_id, merge_block_id)) {
return 0;
}
diff --git a/source/fuzz/transformation_push_id_through_variable.cpp b/source/fuzz/transformation_push_id_through_variable.cpp
index 0df1da6b..55a57a15 100644
--- a/source/fuzz/transformation_push_id_through_variable.cpp
+++ b/source/fuzz/transformation_push_id_through_variable.cpp
@@ -61,7 +61,7 @@ bool TransformationPushIdThroughVariable::IsApplicable(
// The instruction to insert before must belong to a reachable block.
auto basic_block = ir_context->get_instr_block(instruction_to_insert_before);
- if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, basic_block)) {
+ if (!ir_context->IsReachable(*basic_block)) {
return false;
}
@@ -155,7 +155,7 @@ void TransformationPushIdThroughVariable::Apply(
// We should be able to create a synonym of |value_id| if it's not irrelevant.
if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context,
- value_instruction) &&
+ *value_instruction) &&
!transformation_context->GetFactManager()->IdIsIrrelevant(
message_.value_synonym_id())) {
// Adds the fact that |message_.value_synonym_id|
diff --git a/source/fuzz/transformation_replace_id_with_synonym.cpp b/source/fuzz/transformation_replace_id_with_synonym.cpp
index 92ce751d..8d21d233 100644
--- a/source/fuzz/transformation_replace_id_with_synonym.cpp
+++ b/source/fuzz/transformation_replace_id_with_synonym.cpp
@@ -65,9 +65,10 @@ bool TransformationReplaceIdWithSynonym::IsApplicable(
// If the id of interest and the synonym are scalar or vector integer
// constants with different signedness, their use can only be swapped if the
// instruction is agnostic to the signedness of the operand.
- if (!TypesAreCompatible(ir_context, use_instruction->opcode(),
- message_.id_use_descriptor().in_operand_index(),
- type_id_of_interest, type_id_synonym)) {
+ if (!fuzzerutil::TypesAreCompatible(
+ ir_context, use_instruction->opcode(),
+ message_.id_use_descriptor().in_operand_index(), type_id_of_interest,
+ type_id_synonym)) {
return false;
}
@@ -109,59 +110,6 @@ protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage()
return result;
}
-// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3582): Add all
-// opcodes that are agnostic to signedness of operands to function.
-// This is not exhaustive yet.
-bool TransformationReplaceIdWithSynonym::IsAgnosticToSignednessOfOperand(
- SpvOp opcode, uint32_t use_in_operand_index) {
- switch (opcode) {
- case SpvOpSNegate:
- case SpvOpNot:
- case SpvOpIAdd:
- case SpvOpISub:
- case SpvOpIMul:
- case SpvOpSDiv:
- case SpvOpSRem:
- case SpvOpSMod:
- case SpvOpShiftRightLogical:
- case SpvOpShiftRightArithmetic:
- case SpvOpShiftLeftLogical:
- case SpvOpBitwiseOr:
- case SpvOpBitwiseXor:
- case SpvOpBitwiseAnd:
- case SpvOpIEqual:
- case SpvOpINotEqual:
- case SpvOpULessThan:
- case SpvOpSLessThan:
- case SpvOpUGreaterThan:
- case SpvOpSGreaterThan:
- case SpvOpULessThanEqual:
- case SpvOpSLessThanEqual:
- case SpvOpUGreaterThanEqual:
- case SpvOpSGreaterThanEqual:
- return true;
- case SpvOpAccessChain:
- // The signedness of indices does not matter.
- return use_in_operand_index > 0;
- default:
- // Conservatively assume that the id cannot be swapped in other
- // instructions.
- return false;
- }
-}
-
-bool TransformationReplaceIdWithSynonym::TypesAreCompatible(
- opt::IRContext* ir_context, SpvOp opcode, uint32_t use_in_operand_index,
- uint32_t type_id_1, uint32_t type_id_2) {
- assert(ir_context->get_type_mgr()->GetType(type_id_1) &&
- ir_context->get_type_mgr()->GetType(type_id_2) &&
- "Type ids are invalid");
-
- return type_id_1 == type_id_2 ||
- (IsAgnosticToSignednessOfOperand(opcode, use_in_operand_index) &&
- fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_1, type_id_2));
-}
-
std::unordered_set<uint32_t> TransformationReplaceIdWithSynonym::GetFreshIds()
const {
return std::unordered_set<uint32_t>();
diff --git a/source/fuzz/transformation_replace_id_with_synonym.h b/source/fuzz/transformation_replace_id_with_synonym.h
index 1ac636b4..66f8e439 100644
--- a/source/fuzz/transformation_replace_id_with_synonym.h
+++ b/source/fuzz/transformation_replace_id_with_synonym.h
@@ -32,7 +32,7 @@ class TransformationReplaceIdWithSynonym : public Transformation {
protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id);
// - The fact manager must know that the id identified by
- // |message_.id_use_descriptor| is synonomous with |message_.synonymous_id|.
+ // |message_.id_use_descriptor| is synonymous with |message_.synonymous_id|.
// - Replacing the id in |message_.id_use_descriptor| by
// |message_.synonymous_id| must respect SPIR-V's rules about uses being
// dominated by their definitions.
@@ -52,22 +52,7 @@ class TransformationReplaceIdWithSynonym : public Transformation {
protobufs::Transformation ToMessage() const override;
- // Returns true if |type_id_1| and |type_id_2| represent compatible types
- // given the context of the instruction with |opcode| (i.e. we can replace
- // an operand of |opcode| of the first type with an id of the second type
- // and vice-versa).
- static bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode,
- uint32_t use_in_operand_index,
- uint32_t type_id_1, uint32_t type_id_2);
-
private:
- // Returns true if the instruction with opcode |opcode| does not change its
- // behaviour depending on the signedness of the operand at
- // |use_in_operand_index|.
- // Assumes that the operand must be the id of an integer scalar or vector.
- static bool IsAgnosticToSignednessOfOperand(SpvOp opcode,
- uint32_t use_in_operand_index);
-
protobufs::TransformationReplaceIdWithSynonym message_;
};
diff --git a/source/fuzz/transformation_store.cpp b/source/fuzz/transformation_store.cpp
index f8c6d013..c00cd345 100644
--- a/source/fuzz/transformation_store.cpp
+++ b/source/fuzz/transformation_store.cpp
@@ -24,9 +24,13 @@ TransformationStore::TransformationStore(protobufs::TransformationStore message)
: message_(std::move(message)) {}
TransformationStore::TransformationStore(
- uint32_t pointer_id, uint32_t value_id,
+ uint32_t pointer_id, bool is_atomic, uint32_t memory_scope,
+ uint32_t memory_semantics, uint32_t value_id,
const protobufs::InstructionDescriptor& instruction_to_insert_before) {
message_.set_pointer_id(pointer_id);
+ message_.set_is_atomic(is_atomic);
+ message_.set_memory_scope_id(memory_scope);
+ message_.set_memory_semantics_id(memory_semantics);
message_.set_value_id(value_id);
*message_.mutable_instruction_to_insert_before() =
instruction_to_insert_before;
@@ -70,8 +74,12 @@ bool TransformationStore::IsApplicable(
return false;
}
// ... and it must be legitimate to insert a store before it.
- if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
- insert_before)) {
+ if (!message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction(
+ SpvOpStore, insert_before)) {
+ return false;
+ }
+ if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction(
+ SpvOpAtomicStore, insert_before)) {
return false;
}
@@ -102,6 +110,87 @@ bool TransformationStore::IsApplicable(
return false;
}
+ if (message_.is_atomic()) {
+ // Check the exists of memory scope and memory semantics ids.
+ auto memory_scope_instruction =
+ ir_context->get_def_use_mgr()->GetDef(message_.memory_scope_id());
+ auto memory_semantics_instruction =
+ ir_context->get_def_use_mgr()->GetDef(message_.memory_semantics_id());
+
+ if (!memory_scope_instruction) {
+ return false;
+ }
+ if (!memory_semantics_instruction) {
+ return false;
+ }
+ // The memory scope and memory semantics instructions must have the
+ // 'OpConstant' opcode.
+ if (memory_scope_instruction->opcode() != SpvOpConstant) {
+ return false;
+ }
+ if (memory_semantics_instruction->opcode() != SpvOpConstant) {
+ return false;
+ }
+ // The memory scope and memory semantics need to be available before
+ // |insert_before|.
+ if (!fuzzerutil::IdIsAvailableBeforeInstruction(
+ ir_context, insert_before, message_.memory_scope_id())) {
+ return false;
+ }
+ if (!fuzzerutil::IdIsAvailableBeforeInstruction(
+ ir_context, insert_before, message_.memory_semantics_id())) {
+ return false;
+ }
+ // The memory scope and memory semantics instructions must have an Integer
+ // operand type with signedness does not matters.
+ if (ir_context->get_def_use_mgr()
+ ->GetDef(memory_scope_instruction->type_id())
+ ->opcode() != SpvOpTypeInt) {
+ return false;
+ }
+ if (ir_context->get_def_use_mgr()
+ ->GetDef(memory_semantics_instruction->type_id())
+ ->opcode() != SpvOpTypeInt) {
+ return false;
+ }
+
+ // The size of the integer for memory scope and memory semantics
+ // instructions must be equal to 32 bits.
+ auto memory_scope_int_width =
+ ir_context->get_def_use_mgr()
+ ->GetDef(memory_scope_instruction->type_id())
+ ->GetSingleWordInOperand(0);
+ auto memory_semantics_int_width =
+ ir_context->get_def_use_mgr()
+ ->GetDef(memory_semantics_instruction->type_id())
+ ->GetSingleWordInOperand(0);
+
+ if (memory_scope_int_width != 32) {
+ return false;
+ }
+ if (memory_semantics_int_width != 32) {
+ return false;
+ }
+
+ // The memory scope constant value must be that of SpvScopeInvocation.
+ auto memory_scope_const_value =
+ memory_scope_instruction->GetSingleWordInOperand(0);
+ if (memory_scope_const_value != SpvScopeInvocation) {
+ return false;
+ }
+
+ // The memory semantics constant value must match the storage class of the
+ // pointer being loaded from.
+ auto memory_semantics_const_value = static_cast<SpvMemorySemanticsMask>(
+ memory_semantics_instruction->GetSingleWordInOperand(0));
+ if (memory_semantics_const_value !=
+ fuzzerutil::GetMemorySemanticsForStorageClass(
+ static_cast<SpvStorageClass>(
+ pointer_type->GetSingleWordInOperand(0)))) {
+ return false;
+ }
+ }
+
// The value needs to be available at the insertion point.
return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
message_.value_id());
@@ -109,20 +198,43 @@ bool TransformationStore::IsApplicable(
void TransformationStore::Apply(opt::IRContext* ir_context,
TransformationContext* /*unused*/) const {
- auto insert_before =
- FindInstruction(message_.instruction_to_insert_before(), ir_context);
- auto new_instruction = MakeUnique<opt::Instruction>(
- ir_context, SpvOpStore, 0, 0,
- opt::Instruction::OperandList(
- {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
- {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}));
- auto new_instruction_ptr = new_instruction.get();
- insert_before->InsertBefore(std::move(new_instruction));
- // Inform the def-use manager about the new instruction and record its basic
- // block.
- ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
- ir_context->set_instr_block(new_instruction_ptr,
- ir_context->get_instr_block(insert_before));
+ if (message_.is_atomic()) {
+ // OpAtomicStore instruction.
+ auto insert_before =
+ FindInstruction(message_.instruction_to_insert_before(), ir_context);
+ auto new_instruction = MakeUnique<opt::Instruction>(
+ ir_context, SpvOpAtomicStore, 0, 0,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
+ {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}},
+ {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID,
+ {message_.memory_semantics_id()}},
+ {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}));
+ auto new_instruction_ptr = new_instruction.get();
+ insert_before->InsertBefore(std::move(new_instruction));
+ // Inform the def-use manager about the new instruction and record its basic
+ // block.
+ ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+ ir_context->set_instr_block(new_instruction_ptr,
+ ir_context->get_instr_block(insert_before));
+
+ } else {
+ // OpStore instruction.
+ auto insert_before =
+ FindInstruction(message_.instruction_to_insert_before(), ir_context);
+ auto new_instruction = MakeUnique<opt::Instruction>(
+ ir_context, SpvOpStore, 0, 0,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
+ {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}));
+ auto new_instruction_ptr = new_instruction.get();
+ insert_before->InsertBefore(std::move(new_instruction));
+ // Inform the def-use manager about the new instruction and record its basic
+ // block.
+ ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+ ir_context->set_instr_block(new_instruction_ptr,
+ ir_context->get_instr_block(insert_before));
+ }
}
protobufs::Transformation TransformationStore::ToMessage() const {
diff --git a/source/fuzz/transformation_store.h b/source/fuzz/transformation_store.h
index 18ba1d7d..638713bb 100644
--- a/source/fuzz/transformation_store.h
+++ b/source/fuzz/transformation_store.h
@@ -28,12 +28,21 @@ class TransformationStore : public Transformation {
explicit TransformationStore(protobufs::TransformationStore message);
TransformationStore(
- uint32_t pointer_id, uint32_t value_id,
+ uint32_t pointer_id, bool is_atomic, uint32_t memory_scope,
+ uint32_t memory_semantics, uint32_t value_id,
const protobufs::InstructionDescriptor& instruction_to_insert_before);
// - |message_.pointer_id| must be the id of a pointer
// - The pointer type must not have read-only storage class
// - The pointer must not be OpConstantNull or OpUndef
+ // - |message_.is_atomic| must be true if want to work with OpAtomicStore.
+ // - If |is_atomic| is true then |message_memory_scope_id| must be the id of
+ // an OpConstant 32 bit integer instruction with the value
+ // SpvScopeInvocation.
+ // - If |is_atomic| is true then |message_.memory_semantics_id| must be the id
+ // of an OpConstant 32 bit integer instruction with the values
+ // SpvMemorySemanticsWorkgroupMemoryMask or
+ // SpvMemorySemanticsUniformMemoryMask.
// - |message_.value_id| must be an instruction result id that has the same
// type as the pointee type of |message_.pointer_id|
// - |message_.instruction_to_insert_before| must identify an instruction
diff --git a/source/fuzz/transformation_vector_shuffle.cpp b/source/fuzz/transformation_vector_shuffle.cpp
index ac0e3ccb..742a2c80 100644
--- a/source/fuzz/transformation_vector_shuffle.cpp
+++ b/source/fuzz/transformation_vector_shuffle.cpp
@@ -204,7 +204,7 @@ void TransformationVectorShuffle::AddDataSynonymFacts(
// Check that the first vector can participate in data synonym facts.
if (!fuzzerutil::CanMakeSynonymOf(
ir_context, *transformation_context,
- ir_context->get_def_use_mgr()->GetDef(message_.vector1()))) {
+ *ir_context->get_def_use_mgr()->GetDef(message_.vector1()))) {
continue;
}
descriptor_for_source_component =
@@ -213,7 +213,7 @@ void TransformationVectorShuffle::AddDataSynonymFacts(
// Check that the second vector can participate in data synonym facts.
if (!fuzzerutil::CanMakeSynonymOf(
ir_context, *transformation_context,
- ir_context->get_def_use_mgr()->GetDef(message_.vector2()))) {
+ *ir_context->get_def_use_mgr()->GetDef(message_.vector2()))) {
continue;
}
auto index_into_vector_2 =
diff --git a/source/fuzz/transformation_wrap_vector_synonym.cpp b/source/fuzz/transformation_wrap_vector_synonym.cpp
new file mode 100644
index 00000000..490bcd78
--- /dev/null
+++ b/source/fuzz/transformation_wrap_vector_synonym.cpp
@@ -0,0 +1,200 @@
+// Copyright (c) 2021 Shiyu Liu
+//
+// 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/fuzz/transformation_wrap_vector_synonym.h"
+
+#include "source/fuzz/data_descriptor.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/opt/instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationWrapVectorSynonym::TransformationWrapVectorSynonym(
+ protobufs::TransformationWrapVectorSynonym message)
+ : message_(std::move(message)) {}
+
+TransformationWrapVectorSynonym::TransformationWrapVectorSynonym(
+ uint32_t instruction_id, uint32_t vector_operand1, uint32_t vector_operand2,
+ uint32_t fresh_id, uint32_t pos) {
+ message_.set_instruction_id(instruction_id);
+ message_.set_vector_operand1(vector_operand1);
+ message_.set_vector_operand2(vector_operand2);
+ message_.set_fresh_id(fresh_id);
+ message_.set_scalar_position(pos);
+}
+
+bool TransformationWrapVectorSynonym::IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
+ // |fresh_id| must be fresh.
+ if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
+ return false;
+ }
+
+ const opt::Instruction* instruction =
+ ir_context->get_def_use_mgr()->GetDef(message_.instruction_id());
+
+ // |instruction_id| must refer to an existing instruction.
+ if (instruction == nullptr) {
+ return false;
+ }
+
+ if (!IsInstructionSupported(ir_context, *instruction)) {
+ return false;
+ }
+
+ // It must be possible to make a synonym of the result id of the scalar
+ // operation
+ if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+ *instruction)) {
+ return false;
+ }
+
+ // |vector_operand1| and |vector_operand2| must exist.
+ auto vec1 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand1());
+ auto vec2 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand2());
+
+ if (vec1 == nullptr || vec2 == nullptr) {
+ return false;
+ }
+
+ // The 2 vectors must have compatible vector types.
+ auto vec1_type_id = vec1->type_id();
+ auto vec2_type_id = vec2->type_id();
+
+ for (auto operand_index : {0, 1}) {
+ if (!fuzzerutil::TypesAreCompatible(ir_context, instruction->opcode(),
+ operand_index, vec1_type_id,
+ vec2_type_id)) {
+ return false;
+ }
+ }
+
+ auto vec1_type = ir_context->get_def_use_mgr()->GetDef(vec1_type_id);
+ if (vec1_type->opcode() != SpvOpTypeVector) {
+ return false;
+ }
+
+ // A suitable vector for the result type of the new vector instruction must
+ // exist in the module. This is a vector of the right length, whose element
+ // type matches the result type of the scalar instruction.
+ uint32_t vector_size = vec1_type->GetSingleWordInOperand(1);
+ if (!fuzzerutil::MaybeGetVectorType(ir_context, instruction->type_id(),
+ vector_size)) {
+ return false;
+ }
+
+ // |scalar_position| needs to be a non-negative integer less than the vector
+ // length.
+ // OpTypeVector instruction has the component count at index 2.
+ if (message_.scalar_position() >= ir_context->get_def_use_mgr()
+ ->GetDef(vec1_type_id)
+ ->GetSingleWordInOperand(1)) {
+ return false;
+ }
+
+ if (!transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(message_.vector_operand1(),
+ {message_.scalar_position()}),
+ MakeDataDescriptor(instruction->GetSingleWordInOperand(0), {}))) {
+ return false;
+ }
+
+ if (!transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(message_.vector_operand2(),
+ {message_.scalar_position()}),
+ MakeDataDescriptor(instruction->GetSingleWordInOperand(1), {}))) {
+ return false;
+ }
+
+ return true;
+}
+
+void TransformationWrapVectorSynonym::Apply(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const {
+ // Create an instruction descriptor for the original instruction.
+ auto instruction =
+ ir_context->get_def_use_mgr()->GetDef(message_.instruction_id());
+ auto destination_block = ir_context->get_instr_block(instruction);
+
+ // Populate input operand list with two vectors for vector operation.
+ opt::Instruction::OperandList in_operands;
+ in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.vector_operand1()}});
+ in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.vector_operand2()}});
+
+ // Make a new arithmetic instruction: %fresh_id = OpXX %type_id %result_id1
+ // %result_id2.
+ auto vector_operand_type = ir_context->get_def_use_mgr()->GetDef(
+ fuzzerutil::GetTypeId(ir_context, message_.vector_operand1()));
+ uint32_t vector_size = vector_operand_type->GetSingleWordInOperand(1);
+ auto vec_type_id = fuzzerutil::MaybeGetVectorType(
+ ir_context, instruction->type_id(), vector_size);
+ auto new_instruction = MakeUnique<opt::Instruction>(
+ ir_context, instruction->opcode(), vec_type_id, message_.fresh_id(),
+ std::move(in_operands));
+ auto new_instruction_ptr = new_instruction.get();
+ instruction->InsertBefore(std::move(new_instruction));
+ ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+ ir_context->set_instr_block(new_instruction_ptr, destination_block);
+
+ // Add |fresh_id| to id bound.
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+ // Add synonyms between |fresh_id| and |instruction_id|.
+ transformation_context->GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(message_.fresh_id(), {message_.scalar_position()}),
+ MakeDataDescriptor(message_.instruction_id(), {}));
+}
+
+protobufs::Transformation TransformationWrapVectorSynonym::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_wrap_vector_synonym() = message_;
+ return result;
+}
+
+std::unordered_set<uint32_t> TransformationWrapVectorSynonym::GetFreshIds()
+ const {
+ return std::unordered_set<uint32_t>{message_.fresh_id()};
+}
+
+bool TransformationWrapVectorSynonym::IsInstructionSupported(
+ opt::IRContext* ir_context, const opt::Instruction& instruction) {
+ if (!instruction.result_id() || !instruction.type_id()) {
+ return false;
+ }
+ auto type_instruction =
+ ir_context->get_def_use_mgr()->GetDef(instruction.type_id());
+
+ if ((type_instruction->opcode() != SpvOpTypeInt &&
+ type_instruction->opcode() != SpvOpTypeFloat)) {
+ return false;
+ }
+
+ switch (instruction.opcode()) {
+ case SpvOpIAdd:
+ case SpvOpISub:
+ case SpvOpIMul:
+ case SpvOpFAdd:
+ case SpvOpFSub:
+ case SpvOpFMul:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_wrap_vector_synonym.h b/source/fuzz/transformation_wrap_vector_synonym.h
new file mode 100644
index 00000000..94437fe5
--- /dev/null
+++ b/source/fuzz/transformation_wrap_vector_synonym.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2021 Shiyu Liu
+//
+// 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_FUZZ_TRANSFORMATION_WRAP_VECTOR_SYNONYM_H_
+#define SOURCE_FUZZ_TRANSFORMATION_WRAP_VECTOR_SYNONYM_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationWrapVectorSynonym : public Transformation {
+ public:
+ explicit TransformationWrapVectorSynonym(
+ protobufs::TransformationWrapVectorSynonym message);
+
+ TransformationWrapVectorSynonym(uint32_t instruction_id,
+ uint32_t vector_operand1,
+ uint32_t vector_operand2, uint32_t fresh_id,
+ uint32_t pos);
+ // - |instruction_id| must be the id of a supported arithmetic operation
+ // and must be relevant.
+ // - |vector_operand1| and |vector_operand2| represents the result ids of the
+ // two vector operands.
+ // - |fresh_id| is an unused id that will be used as a result id of the
+ // created instruction.
+ // - |vector_operand1| and |vector_operand2| must have compatible vector types
+ // that are supported by this transformation.
+ // - |pos| is an index of the operands of |instruction_id| in the
+ // |vector_operand1| and |vector_operand2|. It must be less than the size
+ // of those vector operands.
+ // - A vector type with the same width as the types of the vector operands,
+ // and element type matching the type of |instruction_id|, must exist in the
+ // module.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Adds a new instruction before the |instruction_id| with |fresh_id|
+ // result id and |instruction_id|'s opcode. The added instruction has
+ // two operands: |vector_operand1| and |vector_operand2| and its type
+ // id is equal to the type ids of those operands. A new fact is added
+ // to the fact manager specifying that |fresh_id[pos]| is synonymous
+ // to |instruction_id|.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ std::unordered_set<uint32_t> GetFreshIds() const override;
+ protobufs::Transformation ToMessage() const override;
+
+ // Checks whether the instruction given is supported by the transformation.
+ // A valid instruction must:
+ // - has both result id and type id.
+ // - is a supported scalar operation instruction.
+ // - has a supported type that is either int or float.
+ static bool IsInstructionSupported(opt::IRContext* ir_context,
+ const opt::Instruction& instruction);
+
+ private:
+ protobufs::TransformationWrapVectorSynonym message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_WRAP_VECTOR_SYNONYM_H_
diff --git a/source/link/CMakeLists.txt b/source/link/CMakeLists.txt
index c8dd2f71..a452a107 100644
--- a/source/link/CMakeLists.txt
+++ b/source/link/CMakeLists.txt
@@ -23,7 +23,7 @@ target_include_directories(SPIRV-Tools-link
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
PRIVATE ${spirv-tools_BINARY_DIR}
)
-# We need the IR functionnalities from the optimizer
+# We need the IR functionalities from the optimizer
target_link_libraries(SPIRV-Tools-link
PUBLIC SPIRV-Tools-opt)
diff --git a/source/link/linker.cpp b/source/link/linker.cpp
index 8da4a98d..76ce775d 100644
--- a/source/link/linker.cpp
+++ b/source/link/linker.cpp
@@ -19,6 +19,7 @@
#include <cstring>
#include <iostream>
#include <memory>
+#include <numeric>
#include <string>
#include <unordered_map>
#include <unordered_set>
@@ -34,8 +35,10 @@
#include "source/opt/pass_manager.h"
#include "source/opt/remove_duplicates_pass.h"
#include "source/opt/type_manager.h"
+#include "source/spirv_constant.h"
#include "source/spirv_target_env.h"
#include "source/util/make_unique.h"
+#include "source/util/string_utils.h"
#include "spirv-tools/libspirv.hpp"
namespace spvtools {
@@ -85,10 +88,6 @@ spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
//
// |header| should not be null, |modules| should not be empty and pointers
// should be non-null. |max_id_bound| should be strictly greater than 0.
-//
-// TODO(pierremoreau): What to do when binaries use different versions of
-// SPIR-V? For now, use the max of all versions found in
-// the input modules.
spv_result_t GenerateHeader(const MessageConsumer& consumer,
const std::vector<opt::Module*>& modules,
uint32_t max_id_bound, opt::ModuleHeader* header);
@@ -129,7 +128,7 @@ spv_result_t CheckImportExportCompatibility(const MessageConsumer& consumer,
// Remove linkage specific instructions, such as prototypes of imported
// functions, declarations of imported variables, import (and export if
-// necessary) linkage attribtes.
+// necessary) linkage attributes.
//
// |linked_context| and |decoration_manager| should not be null, and the
// 'RemoveDuplicatePass' should be run first.
@@ -147,6 +146,15 @@ spv_result_t RemoveLinkageSpecificInstructions(
spv_result_t VerifyIds(const MessageConsumer& consumer,
opt::IRContext* linked_context);
+// Verify that the universal limits are not crossed, and warn the user
+// otherwise.
+//
+// TODO(pierremoreau):
+// - Verify against the limits of the environment (e.g. Vulkan limits if
+// consuming vulkan1.x)
+spv_result_t VerifyLimits(const MessageConsumer& consumer,
+ const opt::IRContext& linked_context);
+
spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
std::vector<opt::Module*>* modules,
uint32_t* max_id_bound) {
@@ -162,29 +170,31 @@ spv_result_t ShiftIdsInModules(const MessageConsumer& consumer,
return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA)
<< "|max_id_bound| of ShiftIdsInModules should not be null.";
- uint32_t id_bound = modules->front()->IdBound() - 1u;
+ const size_t id_bound =
+ std::accumulate(modules->begin(), modules->end(), static_cast<size_t>(1),
+ [](const size_t& accumulation, opt::Module* module) {
+ return accumulation + module->IdBound() - 1u;
+ });
+ if (id_bound > std::numeric_limits<uint32_t>::max())
+ return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA)
+ << "Too many IDs (" << id_bound
+ << "): combining all modules would overflow the 32-bit word of the "
+ "SPIR-V header.";
+
+ *max_id_bound = static_cast<uint32_t>(id_bound);
+
+ uint32_t id_offset = modules->front()->IdBound() - 1u;
for (auto module_iter = modules->begin() + 1; module_iter != modules->end();
++module_iter) {
Module* module = *module_iter;
- module->ForEachInst([&id_bound](Instruction* insn) {
- insn->ForEachId([&id_bound](uint32_t* id) { *id += id_bound; });
+ module->ForEachInst([&id_offset](Instruction* insn) {
+ insn->ForEachId([&id_offset](uint32_t* id) { *id += id_offset; });
});
- id_bound += module->IdBound() - 1u;
- if (id_bound > 0x3FFFFF)
- return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_ID)
- << "The limit of IDs, 4194303, was exceeded:"
- << " " << id_bound << " is the current ID bound.";
+ id_offset += module->IdBound() - 1u;
// Invalidate the DefUseManager
module->context()->InvalidateAnalyses(opt::IRContext::kAnalysisDefUse);
}
- ++id_bound;
- if (id_bound > 0x3FFFFF)
- return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_ID)
- << "The limit of IDs, 4194303, was exceeded:"
- << " " << id_bound << " is the current ID bound.";
-
- *max_id_bound = id_bound;
return SPV_SUCCESS;
}
@@ -201,15 +211,25 @@ spv_result_t GenerateHeader(const MessageConsumer& consumer,
return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA)
<< "|max_id_bound| of GenerateHeader should not be null.";
- uint32_t version = 0u;
- for (const auto& module : modules)
- version = std::max(version, module->version());
+ const uint32_t linked_version = modules.front()->version();
+ for (std::size_t i = 1; i < modules.size(); ++i) {
+ const uint32_t module_version = modules[i]->version();
+ if (module_version != linked_version)
+ return DiagnosticStream({0, 0, 1}, consumer, "", SPV_ERROR_INTERNAL)
+ << "Conflicting SPIR-V versions: "
+ << SPV_SPIRV_VERSION_MAJOR_PART(linked_version) << "."
+ << SPV_SPIRV_VERSION_MINOR_PART(linked_version)
+ << " (input modules 1 through " << i << ") vs "
+ << SPV_SPIRV_VERSION_MAJOR_PART(module_version) << "."
+ << SPV_SPIRV_VERSION_MINOR_PART(module_version)
+ << " (input module " << (i + 1) << ").";
+ }
header->magic_number = SpvMagicNumber;
- header->version = version;
- header->generator = 17u;
+ header->version = linked_version;
+ header->generator = SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_LINKER, 0);
header->bound = max_id_bound;
- header->reserved = 0u;
+ header->schema = 0u;
return SPV_SUCCESS;
}
@@ -242,55 +262,65 @@ spv_result_t MergeModules(const MessageConsumer& consumer,
linked_module->AddExtInstImport(
std::unique_ptr<Instruction>(inst.Clone(linked_context)));
- do {
- const Instruction* memory_model_inst = input_modules[0]->GetMemoryModel();
- if (memory_model_inst == nullptr) break;
-
- uint32_t addressing_model = memory_model_inst->GetSingleWordOperand(0u);
- uint32_t memory_model = memory_model_inst->GetSingleWordOperand(1u);
- for (const auto& module : input_modules) {
- memory_model_inst = module->GetMemoryModel();
- if (memory_model_inst == nullptr) continue;
-
- if (addressing_model != memory_model_inst->GetSingleWordOperand(0u)) {
- spv_operand_desc initial_desc = nullptr, current_desc = nullptr;
- grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL,
- addressing_model, &initial_desc);
- grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL,
- memory_model_inst->GetSingleWordOperand(0u),
- &current_desc);
- return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL)
- << "Conflicting addressing models: " << initial_desc->name
- << " vs " << current_desc->name << ".";
- }
- if (memory_model != memory_model_inst->GetSingleWordOperand(1u)) {
- spv_operand_desc initial_desc = nullptr, current_desc = nullptr;
- grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, memory_model,
- &initial_desc);
- grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL,
- memory_model_inst->GetSingleWordOperand(1u),
- &current_desc);
- return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL)
- << "Conflicting memory models: " << initial_desc->name << " vs "
- << current_desc->name << ".";
- }
+ const Instruction* linked_memory_model_inst =
+ input_modules.front()->GetMemoryModel();
+ if (linked_memory_model_inst == nullptr) {
+ return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
+ << "Input module 1 is lacking an OpMemoryModel instruction.";
+ }
+ const uint32_t linked_addressing_model =
+ linked_memory_model_inst->GetSingleWordOperand(0u);
+ const uint32_t linked_memory_model =
+ linked_memory_model_inst->GetSingleWordOperand(1u);
+
+ for (std::size_t i = 1; i < input_modules.size(); ++i) {
+ const Module* module = input_modules[i];
+ const Instruction* memory_model_inst = module->GetMemoryModel();
+ if (memory_model_inst == nullptr)
+ return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
+ << "Input module " << (i + 1)
+ << " is lacking an OpMemoryModel instruction.";
+
+ const uint32_t module_addressing_model =
+ memory_model_inst->GetSingleWordOperand(0u);
+ if (module_addressing_model != linked_addressing_model) {
+ spv_operand_desc linked_desc = nullptr, module_desc = nullptr;
+ grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL,
+ linked_addressing_model, &linked_desc);
+ grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL,
+ module_addressing_model, &module_desc);
+ return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL)
+ << "Conflicting addressing models: " << linked_desc->name
+ << " (input modules 1 through " << i << ") vs "
+ << module_desc->name << " (input module " << (i + 1) << ").";
}
- if (memory_model_inst != nullptr)
- linked_module->SetMemoryModel(std::unique_ptr<Instruction>(
- memory_model_inst->Clone(linked_context)));
- } while (false);
+ const uint32_t module_memory_model =
+ memory_model_inst->GetSingleWordOperand(1u);
+ if (module_memory_model != linked_memory_model) {
+ spv_operand_desc linked_desc = nullptr, module_desc = nullptr;
+ grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, linked_memory_model,
+ &linked_desc);
+ grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, module_memory_model,
+ &module_desc);
+ return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL)
+ << "Conflicting memory models: " << linked_desc->name
+ << " (input modules 1 through " << i << ") vs "
+ << module_desc->name << " (input module " << (i + 1) << ").";
+ }
+ }
+ linked_module->SetMemoryModel(std::unique_ptr<Instruction>(
+ linked_memory_model_inst->Clone(linked_context)));
- std::vector<std::pair<uint32_t, const char*>> entry_points;
+ std::vector<std::pair<uint32_t, std::string>> entry_points;
for (const auto& module : input_modules)
for (const auto& inst : module->entry_points()) {
const uint32_t model = inst.GetSingleWordInOperand(0);
- const char* const name =
- reinterpret_cast<const char*>(inst.GetInOperand(2).words.data());
+ const std::string name = inst.GetInOperand(2).AsString();
const auto i = std::find_if(
entry_points.begin(), entry_points.end(),
- [model, name](const std::pair<uint32_t, const char*>& v) {
- return v.first == model && strcmp(name, v.second) == 0;
+ [model, name](const std::pair<uint32_t, std::string>& v) {
+ return v.first == model && v.second == name;
});
if (i != entry_points.end()) {
spv_operand_desc desc = nullptr;
@@ -331,13 +361,10 @@ spv_result_t MergeModules(const MessageConsumer& consumer,
// If the generated module uses SPIR-V 1.1 or higher, add an
// OpModuleProcessed instruction about the linking step.
- if (linked_module->version() >= 0x10100) {
+ if (linked_module->version() >= SPV_SPIRV_VERSION_WORD(1, 1)) {
const std::string processed_string("Linked by SPIR-V Tools Linker");
- const auto num_chars = processed_string.size();
- // Compute num words, accommodate the terminating null character.
- const auto num_words = (num_chars + 1 + 3) / 4;
- std::vector<uint32_t> processed_words(num_words, 0u);
- std::memcpy(processed_words.data(), processed_string.data(), num_chars);
+ std::vector<uint32_t> processed_words =
+ spvtools::utils::MakeVector(processed_string);
linked_module->AddDebug3Inst(std::unique_ptr<Instruction>(
new Instruction(linked_context, SpvOpModuleProcessed, 0u, 0u,
{{SPV_OPERAND_TYPE_LITERAL_STRING, processed_words}})));
@@ -351,18 +378,12 @@ spv_result_t MergeModules(const MessageConsumer& consumer,
// TODO(pierremoreau): Since the modules have not been validate, should we
// expect SpvStorageClassFunction variables outside
// functions?
- uint32_t num_global_values = 0u;
for (const auto& module : input_modules) {
for (const auto& inst : module->types_values()) {
linked_module->AddType(
std::unique_ptr<Instruction>(inst.Clone(linked_context)));
- num_global_values += inst.opcode() == SpvOpVariable;
}
}
- if (num_global_values > 0xFFFF)
- return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL)
- << "The limit of global values, 65535, was exceeded;"
- << " " << num_global_values << " global values were found.";
// Process functions and their basic blocks
for (const auto& module : input_modules) {
@@ -413,8 +434,7 @@ spv_result_t GetImportExportPairs(const MessageConsumer& consumer,
const uint32_t type = decoration.GetSingleWordInOperand(3u);
LinkageSymbolInfo symbol_info;
- symbol_info.name =
- reinterpret_cast<const char*>(decoration.GetInOperand(2u).words.data());
+ symbol_info.name = decoration.GetInOperand(2u).AsString();
symbol_info.id = id;
symbol_info.type_id = 0u;
@@ -635,6 +655,34 @@ spv_result_t VerifyIds(const MessageConsumer& consumer,
return SPV_SUCCESS;
}
+spv_result_t VerifyLimits(const MessageConsumer& consumer,
+ const opt::IRContext& linked_context) {
+ spv_position_t position = {};
+
+ const uint32_t max_id_bound = linked_context.module()->id_bound();
+ if (max_id_bound >= SPV_LIMIT_RESULT_ID_BOUND)
+ DiagnosticStream({0u, 0u, 4u}, consumer, "", SPV_WARNING)
+ << "The minimum limit of IDs, " << (SPV_LIMIT_RESULT_ID_BOUND - 1)
+ << ", was exceeded:"
+ << " " << max_id_bound << " is the current ID bound.\n"
+ << "The resulting module might not be supported by all "
+ "implementations.";
+
+ size_t num_global_values = 0u;
+ for (const auto& inst : linked_context.module()->types_values()) {
+ num_global_values += inst.opcode() == SpvOpVariable;
+ }
+ if (num_global_values >= SPV_LIMIT_GLOBAL_VARIABLES_MAX)
+ DiagnosticStream(position, consumer, "", SPV_WARNING)
+ << "The minimum limit of global values, "
+ << (SPV_LIMIT_GLOBAL_VARIABLES_MAX - 1) << ", was exceeded;"
+ << " " << num_global_values << " global values were found.\n"
+ << "The resulting module might not be supported by all "
+ "implementations.";
+
+ return SPV_SUCCESS;
+}
+
} // namespace
spv_result_t Link(const Context& context,
@@ -759,7 +807,11 @@ 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: Output the module
+ // Phase 11: Warn if SPIR-V limits were exceeded
+ res = VerifyLimits(consumer, linked_context);
+ if (res != SPV_SUCCESS) return res;
+
+ // Phase 12: Output the module
linked_context.module()->ToBinary(linked_binary, true);
return SPV_SUCCESS;
diff --git a/source/lint/CMakeLists.txt b/source/lint/CMakeLists.txt
new file mode 100644
index 00000000..1feae3f9
--- /dev/null
+++ b/source/lint/CMakeLists.txt
@@ -0,0 +1,61 @@
+# Copyright (c) 2021 Google LLC.
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+set(SPIRV_TOOLS_LINT_SOURCES
+ divergence_analysis.h
+ lints.h
+
+ linter.cpp
+ divergence_analysis.cpp
+ lint_divergent_derivatives.cpp
+)
+
+if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
+ # Enable parallel builds across four cores for this lib.
+ add_definitions(/MP4)
+endif()
+
+add_library(SPIRV-Tools-lint ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_LINT_SOURCES})
+
+spvtools_default_compile_options(SPIRV-Tools-lint)
+target_include_directories(SPIRV-Tools-lint
+ 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-lint
+ PUBLIC ${SPIRV_TOOLS_FULL_VISIBILITY})
+# We need the internals of spirv-opt.
+target_link_libraries(SPIRV-Tools-lint
+ PUBLIC SPIRV-Tools-opt)
+
+set_property(TARGET SPIRV-Tools-lint PROPERTY FOLDER "SPIRV-Tools libraries")
+spvtools_check_symbol_exports(SPIRV-Tools-lint)
+
+if(ENABLE_SPIRV_TOOLS_INSTALL)
+ install(TARGETS SPIRV-Tools-lint EXPORT SPIRV-Tools-lintTargets
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+ export(EXPORT SPIRV-Tools-lintTargets FILE SPIRV-Tools-lintTargets.cmake)
+
+ spvtools_config_package_dir(SPIRV-Tools-lint PACKAGE_DIR)
+ install(EXPORT SPIRV-Tools-lintTargets FILE SPIRV-Tools-lintTargets.cmake
+ DESTINATION ${PACKAGE_DIR})
+
+ spvtools_generate_config_file(SPIRV-Tools-lint)
+ install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-lintConfig.cmake DESTINATION ${PACKAGE_DIR})
+endif(ENABLE_SPIRV_TOOLS_INSTALL)
diff --git a/source/lint/divergence_analysis.cpp b/source/lint/divergence_analysis.cpp
new file mode 100644
index 00000000..b5a72b45
--- /dev/null
+++ b/source/lint/divergence_analysis.cpp
@@ -0,0 +1,245 @@
+// Copyright (c) 2021 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/lint/divergence_analysis.h"
+
+#include "source/opt/basic_block.h"
+#include "source/opt/control_dependence.h"
+#include "source/opt/dataflow.h"
+#include "source/opt/function.h"
+#include "source/opt/instruction.h"
+#include "spirv/unified1/spirv.h"
+
+namespace spvtools {
+namespace lint {
+
+void DivergenceAnalysis::EnqueueSuccessors(opt::Instruction* inst) {
+ // Enqueue control dependents of block, if applicable.
+ // There are two ways for a dependence source to be updated:
+ // 1. control -> control: source block is marked divergent.
+ // 2. data -> control: branch condition is marked divergent.
+ uint32_t block_id;
+ if (inst->IsBlockTerminator()) {
+ block_id = context().get_instr_block(inst)->id();
+ } else if (inst->opcode() == SpvOpLabel) {
+ block_id = inst->result_id();
+ opt::BasicBlock* bb = context().cfg()->block(block_id);
+ // Only enqueue phi instructions, as other uses don't affect divergence.
+ bb->ForEachPhiInst([this](opt::Instruction* phi) { Enqueue(phi); });
+ } else {
+ opt::ForwardDataFlowAnalysis::EnqueueUsers(inst);
+ return;
+ }
+ if (!cd_.HasBlock(block_id)) {
+ return;
+ }
+ for (const spvtools::opt::ControlDependence& dep :
+ cd_.GetDependenceTargets(block_id)) {
+ opt::Instruction* target_inst =
+ context().cfg()->block(dep.target_bb_id())->GetLabelInst();
+ Enqueue(target_inst);
+ }
+}
+
+opt::DataFlowAnalysis::VisitResult DivergenceAnalysis::Visit(
+ opt::Instruction* inst) {
+ if (inst->opcode() == SpvOpLabel) {
+ return VisitBlock(inst->result_id());
+ } else {
+ return VisitInstruction(inst);
+ }
+}
+
+opt::DataFlowAnalysis::VisitResult DivergenceAnalysis::VisitBlock(uint32_t id) {
+ if (!cd_.HasBlock(id)) {
+ return opt::DataFlowAnalysis::VisitResult::kResultFixed;
+ }
+ DivergenceLevel& cur_level = divergence_[id];
+ if (cur_level == DivergenceLevel::kDivergent) {
+ return opt::DataFlowAnalysis::VisitResult::kResultFixed;
+ }
+ DivergenceLevel orig = cur_level;
+ for (const spvtools::opt::ControlDependence& dep :
+ cd_.GetDependenceSources(id)) {
+ if (divergence_[dep.source_bb_id()] > cur_level) {
+ cur_level = divergence_[dep.source_bb_id()];
+ divergence_source_[id] = dep.source_bb_id();
+ } else if (dep.source_bb_id() != 0) {
+ uint32_t condition_id = dep.GetConditionID(*context().cfg());
+ DivergenceLevel dep_level = divergence_[condition_id];
+ // Check if we are along the chain of unconditional branches starting from
+ // the branch target.
+ if (follow_unconditional_branches_[dep.branch_target_bb_id()] !=
+ follow_unconditional_branches_[dep.target_bb_id()]) {
+ // We must have reconverged in order to reach this block.
+ // Promote partially uniform to divergent.
+ if (dep_level == DivergenceLevel::kPartiallyUniform) {
+ dep_level = DivergenceLevel::kDivergent;
+ }
+ }
+ if (dep_level > cur_level) {
+ cur_level = dep_level;
+ divergence_source_[id] = condition_id;
+ divergence_dependence_source_[id] = dep.source_bb_id();
+ }
+ }
+ }
+ return cur_level > orig ? VisitResult::kResultChanged
+ : VisitResult::kResultFixed;
+}
+
+opt::DataFlowAnalysis::VisitResult DivergenceAnalysis::VisitInstruction(
+ opt::Instruction* inst) {
+ if (inst->IsBlockTerminator()) {
+ // This is called only when the condition has changed, so return changed.
+ return VisitResult::kResultChanged;
+ }
+ if (!inst->HasResultId()) {
+ return VisitResult::kResultFixed;
+ }
+ uint32_t id = inst->result_id();
+ DivergenceLevel& cur_level = divergence_[id];
+ if (cur_level == DivergenceLevel::kDivergent) {
+ return opt::DataFlowAnalysis::VisitResult::kResultFixed;
+ }
+ DivergenceLevel orig = cur_level;
+ cur_level = ComputeInstructionDivergence(inst);
+ return cur_level > orig ? VisitResult::kResultChanged
+ : VisitResult::kResultFixed;
+}
+
+DivergenceAnalysis::DivergenceLevel
+DivergenceAnalysis::ComputeInstructionDivergence(opt::Instruction* inst) {
+ // TODO(kuhar): Check to see if inst is decorated with Uniform or UniformId
+ // and use that to short circuit other checks. Uniform is for subgroups which
+ // would satisfy derivative groups too. UniformId takes a scope, so if it is
+ // subgroup or greater it could satisfy derivative group and
+ // Device/QueueFamily could satisfy fully uniform.
+ uint32_t id = inst->result_id();
+ // Handle divergence roots.
+ if (inst->opcode() == SpvOpFunctionParameter) {
+ divergence_source_[id] = 0;
+ return divergence_[id] = DivergenceLevel::kDivergent;
+ } else if (inst->IsLoad()) {
+ spvtools::opt::Instruction* var = inst->GetBaseAddress();
+ if (var->opcode() != SpvOpVariable) {
+ // Assume divergent.
+ divergence_source_[id] = 0;
+ return DivergenceLevel::kDivergent;
+ }
+ DivergenceLevel ret = ComputeVariableDivergence(var);
+ if (ret > DivergenceLevel::kUniform) {
+ divergence_source_[inst->result_id()] = 0;
+ }
+ return divergence_[id] = ret;
+ }
+ // Get the maximum divergence of the operands.
+ DivergenceLevel ret = DivergenceLevel::kUniform;
+ inst->ForEachInId([this, inst, &ret](const uint32_t* op) {
+ if (!op) return;
+ if (divergence_[*op] > ret) {
+ divergence_source_[inst->result_id()] = *op;
+ ret = divergence_[*op];
+ }
+ });
+ divergence_[inst->result_id()] = ret;
+ return ret;
+}
+
+DivergenceAnalysis::DivergenceLevel
+DivergenceAnalysis::ComputeVariableDivergence(opt::Instruction* var) {
+ uint32_t type_id = var->type_id();
+ spvtools::opt::analysis::Pointer* type =
+ context().get_type_mgr()->GetType(type_id)->AsPointer();
+ assert(type != nullptr);
+ uint32_t def_id = var->result_id();
+ DivergenceLevel ret;
+ switch (type->storage_class()) {
+ case SpvStorageClassFunction:
+ case SpvStorageClassGeneric:
+ case SpvStorageClassAtomicCounter:
+ case SpvStorageClassStorageBuffer:
+ case SpvStorageClassPhysicalStorageBuffer:
+ case SpvStorageClassOutput:
+ case SpvStorageClassWorkgroup:
+ case SpvStorageClassImage: // Image atomics probably aren't uniform.
+ case SpvStorageClassPrivate:
+ ret = DivergenceLevel::kDivergent;
+ break;
+ case SpvStorageClassInput:
+ ret = DivergenceLevel::kDivergent;
+ // If this variable has a Flat decoration, it is partially uniform.
+ // TODO(kuhar): Track access chain indices and also consider Flat members
+ // of a structure.
+ context().get_decoration_mgr()->WhileEachDecoration(
+ def_id, SpvDecorationFlat, [&ret](const opt::Instruction&) {
+ ret = DivergenceLevel::kPartiallyUniform;
+ return false;
+ });
+ break;
+ case SpvStorageClassUniformConstant:
+ // May be a storage image which is also written to; mark those as
+ // divergent.
+ if (!var->IsVulkanStorageImage() || var->IsReadOnlyPointer()) {
+ ret = DivergenceLevel::kUniform;
+ } else {
+ ret = DivergenceLevel::kDivergent;
+ }
+ break;
+ case SpvStorageClassUniform:
+ case SpvStorageClassPushConstant:
+ case SpvStorageClassCrossWorkgroup: // Not for shaders; default uniform.
+ default:
+ ret = DivergenceLevel::kUniform;
+ break;
+ }
+ return ret;
+}
+
+void DivergenceAnalysis::Setup(opt::Function* function) {
+ // TODO(kuhar): Run functions called by |function| so we can detect
+ // reconvergence caused by multiple returns.
+ cd_.ComputeControlDependenceGraph(
+ *context().cfg(), *context().GetPostDominatorAnalysis(function));
+ context().cfg()->ForEachBlockInPostOrder(
+ function->entry().get(), [this](const opt::BasicBlock* bb) {
+ uint32_t id = bb->id();
+ if (bb->terminator() == nullptr ||
+ bb->terminator()->opcode() != SpvOpBranch) {
+ follow_unconditional_branches_[id] = id;
+ } else {
+ uint32_t target_id = bb->terminator()->GetSingleWordInOperand(0);
+ // Target is guaranteed to have been visited before us in postorder.
+ follow_unconditional_branches_[id] =
+ follow_unconditional_branches_[target_id];
+ }
+ });
+}
+
+std::ostream& operator<<(std::ostream& os,
+ DivergenceAnalysis::DivergenceLevel level) {
+ switch (level) {
+ case DivergenceAnalysis::DivergenceLevel::kUniform:
+ return os << "uniform";
+ case DivergenceAnalysis::DivergenceLevel::kPartiallyUniform:
+ return os << "partially uniform";
+ case DivergenceAnalysis::DivergenceLevel::kDivergent:
+ return os << "divergent";
+ default:
+ return os << "<invalid divergence level>";
+ }
+}
+
+} // namespace lint
+} // namespace spvtools
diff --git a/source/lint/divergence_analysis.h b/source/lint/divergence_analysis.h
new file mode 100644
index 00000000..4d595ec2
--- /dev/null
+++ b/source/lint/divergence_analysis.h
@@ -0,0 +1,163 @@
+// Copyright (c) 2021 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_LINT_DIVERGENCE_ANALYSIS_H_
+#define SOURCE_LINT_DIVERGENCE_ANALYSIS_H_
+
+#include <cstdint>
+#include <ostream>
+#include <unordered_map>
+
+#include "source/opt/basic_block.h"
+#include "source/opt/control_dependence.h"
+#include "source/opt/dataflow.h"
+#include "source/opt/function.h"
+#include "source/opt/instruction.h"
+
+namespace spvtools {
+namespace lint {
+
+// Computes the static divergence level for blocks (control flow) and values.
+//
+// A value is uniform if all threads that execute it are guaranteed to have the
+// same value. Similarly, a value is partially uniform if this is true only
+// within each derivative group. If neither apply, it is divergent.
+//
+// Control flow through a block is uniform if for any possible execution and
+// point in time, all threads are executing it, or no threads are executing it.
+// In particular, it is never possible for some threads to be inside the block
+// and some threads not executing.
+// TODO(kuhar): Clarify the difference between uniform, divergent, and
+// partially-uniform execution in this analysis.
+//
+// Caveat:
+// As we use control dependence to determine how divergence is propagated, this
+// analysis can be overly permissive when the merge block for a conditional
+// branch or switch is later than (strictly postdominates) the expected merge
+// block, which is the immediate postdominator. However, this is not expected to
+// be a problem in practice, given that SPIR-V is generally output by compilers
+// and other automated tools, which would assign the earliest possible merge
+// block, rather than written by hand.
+// TODO(kuhar): Handle late merges.
+class DivergenceAnalysis : public opt::ForwardDataFlowAnalysis {
+ public:
+ // The tightest (most uniform) level of divergence that can be determined
+ // statically for a value or control flow for a block.
+ //
+ // The values are ordered such that A > B means that A is potentially more
+ // divergent than B.
+ // TODO(kuhar): Rename |PartiallyUniform' to something less confusing. For
+ // example, the enum could be based on scopes.
+ enum class DivergenceLevel {
+ // The value or control flow is uniform across the entire invocation group.
+ kUniform = 0,
+ // The value or control flow is uniform across the derivative group, but not
+ // the invocation group.
+ kPartiallyUniform = 1,
+ // The value or control flow is not statically uniform.
+ kDivergent = 2,
+ };
+
+ DivergenceAnalysis(opt::IRContext& context)
+ : ForwardDataFlowAnalysis(context, LabelPosition::kLabelsAtEnd) {}
+
+ // Returns the divergence level for the given value (non-label instructions),
+ // or control flow for the given block.
+ DivergenceLevel GetDivergenceLevel(uint32_t id) {
+ auto it = divergence_.find(id);
+ if (it == divergence_.end()) {
+ return DivergenceLevel::kUniform;
+ }
+ return it->second;
+ }
+
+ // Returns the divergence source for the given id. The following types of
+ // divergence flows from A to B are possible:
+ //
+ // data -> data: A is used as an operand in the definition of B.
+ // data -> control: B is control-dependent on a branch with condition A.
+ // control -> data: B is a OpPhi instruction in which A is a block operand.
+ // control -> control: B is control-dependent on A.
+ uint32_t GetDivergenceSource(uint32_t id) {
+ auto it = divergence_source_.find(id);
+ if (it == divergence_source_.end()) {
+ return 0;
+ }
+ return it->second;
+ }
+
+ // Returns the dependence source for the control dependence for the given id.
+ // This only exists for data -> control edges.
+ //
+ // In other words, if block 2 is dependent on block 1 due to value 3 (e.g.
+ // block 1 terminates with OpBranchConditional %3 %2 %4):
+ // * GetDivergenceSource(2) = 3
+ // * GetDivergenceDependenceSource(2) = 1
+ //
+ // Returns 0 if not applicable.
+ uint32_t GetDivergenceDependenceSource(uint32_t id) {
+ auto it = divergence_dependence_source_.find(id);
+ if (it == divergence_dependence_source_.end()) {
+ return 0;
+ }
+ return it->second;
+ }
+
+ void InitializeWorklist(opt::Function* function,
+ bool is_first_iteration) override {
+ // Since |EnqueueSuccessors| is complete, we only need one pass.
+ if (is_first_iteration) {
+ Setup(function);
+ opt::ForwardDataFlowAnalysis::InitializeWorklist(function, true);
+ }
+ }
+
+ void EnqueueSuccessors(opt::Instruction* inst) override;
+
+ VisitResult Visit(opt::Instruction* inst) override;
+
+ private:
+ VisitResult VisitBlock(uint32_t id);
+ VisitResult VisitInstruction(opt::Instruction* inst);
+
+ // Computes the divergence level for the result of the given instruction
+ // based on the current state of the analysis. This is always an
+ // underapproximation, which will be improved as the analysis proceeds.
+ DivergenceLevel ComputeInstructionDivergence(opt::Instruction* inst);
+
+ // Computes the divergence level for a variable, which is used for loads.
+ DivergenceLevel ComputeVariableDivergence(opt::Instruction* var);
+
+ // Initializes data structures for performing dataflow on the given function.
+ void Setup(opt::Function* function);
+
+ std::unordered_map<uint32_t, DivergenceLevel> divergence_;
+ std::unordered_map<uint32_t, uint32_t> divergence_source_;
+ std::unordered_map<uint32_t, uint32_t> divergence_dependence_source_;
+
+ // Stores the result of following unconditional branches starting from the
+ // given block. This is used to detect when reconvergence needs to be
+ // accounted for.
+ std::unordered_map<uint32_t, uint32_t> follow_unconditional_branches_;
+
+ opt::ControlDependenceAnalysis cd_;
+};
+
+std::ostream& operator<<(std::ostream& os,
+ DivergenceAnalysis::DivergenceLevel level);
+
+} // namespace lint
+} // namespace spvtools
+
+#endif // SOURCE_LINT_DIVERGENCE_ANALYSIS_H_
diff --git a/source/lint/lint_divergent_derivatives.cpp b/source/lint/lint_divergent_derivatives.cpp
new file mode 100644
index 00000000..512847b0
--- /dev/null
+++ b/source/lint/lint_divergent_derivatives.cpp
@@ -0,0 +1,169 @@
+// Copyright (c) 2021 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cassert>
+#include <sstream>
+#include <string>
+
+#include "source/diagnostic.h"
+#include "source/lint/divergence_analysis.h"
+#include "source/lint/lints.h"
+#include "source/opt/basic_block.h"
+#include "source/opt/cfg.h"
+#include "source/opt/control_dependence.h"
+#include "source/opt/def_use_manager.h"
+#include "source/opt/dominator_analysis.h"
+#include "source/opt/instruction.h"
+#include "source/opt/ir_context.h"
+#include "spirv-tools/libspirv.h"
+#include "spirv/unified1/spirv.h"
+
+namespace spvtools {
+namespace lint {
+namespace lints {
+namespace {
+// Returns the %name[id], where `name` is the first name associated with the
+// given id, or just %id if one is not found.
+std::string GetFriendlyName(opt::IRContext* context, uint32_t id) {
+ auto names = context->GetNames(id);
+ std::stringstream ss;
+ ss << "%";
+ if (names.empty()) {
+ ss << id;
+ } else {
+ opt::Instruction* inst_name = names.begin()->second;
+ if (inst_name->opcode() == SpvOpName) {
+ ss << names.begin()->second->GetInOperand(0).AsString();
+ ss << "[" << id << "]";
+ } else {
+ ss << id;
+ }
+ }
+ return ss.str();
+}
+
+bool InstructionHasDerivative(const opt::Instruction& inst) {
+ static const SpvOp derivative_opcodes[] = {
+ // Implicit derivatives.
+ SpvOpImageSampleImplicitLod,
+ SpvOpImageSampleDrefImplicitLod,
+ SpvOpImageSampleProjImplicitLod,
+ SpvOpImageSampleProjDrefImplicitLod,
+ SpvOpImageSparseSampleImplicitLod,
+ SpvOpImageSparseSampleDrefImplicitLod,
+ SpvOpImageSparseSampleProjImplicitLod,
+ SpvOpImageSparseSampleProjDrefImplicitLod,
+ // Explicit derivatives.
+ SpvOpDPdx,
+ SpvOpDPdy,
+ SpvOpFwidth,
+ SpvOpDPdxFine,
+ SpvOpDPdyFine,
+ SpvOpFwidthFine,
+ SpvOpDPdxCoarse,
+ SpvOpDPdyCoarse,
+ SpvOpFwidthCoarse,
+ };
+ return std::find(std::begin(derivative_opcodes), std::end(derivative_opcodes),
+ inst.opcode()) != std::end(derivative_opcodes);
+}
+
+spvtools::DiagnosticStream Warn(opt::IRContext* context,
+ opt::Instruction* inst) {
+ if (inst == nullptr) {
+ return DiagnosticStream({0, 0, 0}, context->consumer(), "", SPV_WARNING);
+ } else {
+ // TODO(kuhar): Use line numbers based on debug info.
+ return DiagnosticStream(
+ {0, 0, 0}, context->consumer(),
+ inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES),
+ SPV_WARNING);
+ }
+}
+
+void PrintDivergenceFlow(opt::IRContext* context, DivergenceAnalysis div,
+ uint32_t id) {
+ opt::analysis::DefUseManager* def_use = context->get_def_use_mgr();
+ opt::CFG* cfg = context->cfg();
+ while (id != 0) {
+ bool is_block = def_use->GetDef(id)->opcode() == SpvOpLabel;
+ if (is_block) {
+ Warn(context, nullptr)
+ << "block " << GetFriendlyName(context, id) << " is divergent";
+ uint32_t source = div.GetDivergenceSource(id);
+ // Skip intermediate blocks.
+ while (source != 0 && def_use->GetDef(source)->opcode() == SpvOpLabel) {
+ id = source;
+ source = div.GetDivergenceSource(id);
+ }
+ if (source == 0) break;
+ spvtools::opt::Instruction* branch =
+ cfg->block(div.GetDivergenceDependenceSource(id))->terminator();
+ Warn(context, branch)
+ << "because it depends on a conditional branch on divergent value "
+ << GetFriendlyName(context, source) << "";
+ id = source;
+ } else {
+ Warn(context, nullptr)
+ << "value " << GetFriendlyName(context, id) << " is divergent";
+ uint32_t source = div.GetDivergenceSource(id);
+ opt::Instruction* def = def_use->GetDef(id);
+ opt::Instruction* source_def =
+ source == 0 ? nullptr : def_use->GetDef(source);
+ // First print data -> data dependencies.
+ while (source != 0 && source_def->opcode() != SpvOpLabel) {
+ Warn(context, def_use->GetDef(id))
+ << "because " << GetFriendlyName(context, id) << " uses value "
+ << GetFriendlyName(context, source)
+ << "in its definition, which is divergent";
+ id = source;
+ def = source_def;
+ source = div.GetDivergenceSource(id);
+ source_def = def_use->GetDef(source);
+ }
+ if (source == 0) {
+ Warn(context, def) << "because it has a divergent definition";
+ break;
+ }
+ Warn(context, def) << "because it is conditionally set in block "
+ << GetFriendlyName(context, source);
+ id = source;
+ }
+ }
+}
+} // namespace
+
+bool CheckDivergentDerivatives(opt::IRContext* context) {
+ DivergenceAnalysis div(*context);
+ for (opt::Function& func : *context->module()) {
+ div.Run(&func);
+ for (const opt::BasicBlock& bb : func) {
+ for (const opt::Instruction& inst : bb) {
+ if (InstructionHasDerivative(inst) &&
+ div.GetDivergenceLevel(bb.id()) >
+ DivergenceAnalysis::DivergenceLevel::kPartiallyUniform) {
+ Warn(context, nullptr)
+ << "derivative with divergent control flow"
+ << " located in block " << GetFriendlyName(context, bb.id());
+ PrintDivergenceFlow(context, div, bb.id());
+ }
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace lints
+} // namespace lint
+} // namespace spvtools
diff --git a/source/lint/linter.cpp b/source/lint/linter.cpp
new file mode 100644
index 00000000..e4ed04ea
--- /dev/null
+++ b/source/lint/linter.cpp
@@ -0,0 +1,60 @@
+// Copyright (c) 2021 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "spirv-tools/linter.hpp"
+
+#include "source/lint/lints.h"
+#include "source/opt/build_module.h"
+#include "source/opt/ir_context.h"
+#include "spirv-tools/libspirv.h"
+#include "spirv-tools/libspirv.hpp"
+#include "spirv/unified1/spirv.h"
+
+namespace spvtools {
+
+struct Linter::Impl {
+ explicit Impl(spv_target_env env) : target_env(env) {
+ message_consumer = [](spv_message_level_t /*level*/, const char* /*source*/,
+ const spv_position_t& /*position*/,
+ const char* /*message*/) {};
+ }
+
+ spv_target_env target_env; // Target environment.
+ MessageConsumer message_consumer; // Message consumer.
+};
+
+Linter::Linter(spv_target_env env) : impl_(new Impl(env)) {}
+
+Linter::~Linter() {}
+
+void Linter::SetMessageConsumer(MessageConsumer consumer) {
+ impl_->message_consumer = std::move(consumer);
+}
+
+const MessageConsumer& Linter::Consumer() const {
+ return impl_->message_consumer;
+}
+
+bool Linter::Run(const uint32_t* binary, size_t binary_size) {
+ std::unique_ptr<opt::IRContext> context =
+ BuildModule(SPV_ENV_VULKAN_1_2, Consumer(), binary, binary_size);
+ if (context == nullptr) return false;
+
+ bool result = true;
+ result &= lint::lints::CheckDivergentDerivatives(context.get());
+
+ return result;
+}
+
+} // namespace spvtools
diff --git a/source/lint/lints.h b/source/lint/lints.h
new file mode 100644
index 00000000..a1995d2f
--- /dev/null
+++ b/source/lint/lints.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2021 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_LINT_LINTS_H_
+#define SOURCE_LINT_LINTS_H_
+
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace lint {
+
+// All of the functions in this namespace output to the error consumer in the
+// |context| argument and return |true| if no errors are found. They do not
+// modify the IR.
+namespace lints {
+
+bool CheckDivergentDerivatives(opt::IRContext* context);
+
+} // namespace lints
+} // namespace lint
+} // namespace spvtools
+
+#endif // SOURCE_LINT_LINTS_H_
diff --git a/source/name_mapper.cpp b/source/name_mapper.cpp
index eb08f8fe..3b31d33a 100644
--- a/source/name_mapper.cpp
+++ b/source/name_mapper.cpp
@@ -22,10 +22,10 @@
#include <unordered_map>
#include <unordered_set>
-#include "spirv-tools/libspirv.h"
-
+#include "source/binary.h"
#include "source/latest_version_spirv_header.h"
#include "source/parsed_operand.h"
+#include "spirv-tools/libspirv.h"
namespace spvtools {
namespace {
@@ -172,7 +172,7 @@ spv_result_t FriendlyNameMapper::ParseInstruction(
const auto result_id = inst.result_id;
switch (inst.opcode) {
case SpvOpName:
- SaveName(inst.words[1], reinterpret_cast<const char*>(inst.words + 2));
+ SaveName(inst.words[1], spvDecodeLiteralStringOperand(inst, 1));
break;
case SpvOpDecorate:
// Decorations come after OpName. So OpName will take precedence over
@@ -274,9 +274,8 @@ spv_result_t FriendlyNameMapper::ParseInstruction(
SaveName(result_id, "Queue");
break;
case SpvOpTypeOpaque:
- SaveName(result_id,
- std::string("Opaque_") +
- Sanitize(reinterpret_cast<const char*>(inst.words + 2)));
+ SaveName(result_id, std::string("Opaque_") +
+ Sanitize(spvDecodeLiteralStringOperand(inst, 1)));
break;
case SpvOpTypePipeStorage:
SaveName(result_id, "PipeStorage");
diff --git a/source/opcode.cpp b/source/opcode.cpp
index c96cde8d..88085df7 100644
--- a/source/opcode.cpp
+++ b/source/opcode.cpp
@@ -40,12 +40,12 @@ struct OpcodeDescPtrLen {
static const spv_opcode_table_t kOpcodeTable = {ARRAY_SIZE(kOpcodeTableEntries),
kOpcodeTableEntries};
-// Represents a vendor tool entry in the SPIR-V XML Regsitry.
+// Represents a vendor tool entry in the SPIR-V XML Registry.
struct VendorTool {
uint32_t value;
const char* vendor;
const char* tool; // Might be empty string.
- const char* vendor_tool; // Combiantion of vendor and tool.
+ const char* vendor_tool; // Combination of vendor and tool.
};
const VendorTool vendor_tools[] = {
diff --git a/source/operand.cpp b/source/operand.cpp
index c00c9b64..6d83e81e 100644
--- a/source/operand.cpp
+++ b/source/operand.cpp
@@ -578,6 +578,12 @@ std::function<bool(unsigned)> spvOperandCanBeForwardDeclaredFunction(
std::function<bool(unsigned)> spvDbgInfoExtOperandCanBeForwardDeclaredFunction(
spv_ext_inst_type_t ext_type, uint32_t key) {
+ // The Vulkan debug info extended instruction set is non-semantic so allows no
+ // forward references ever
+ if (ext_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
+ return [](unsigned) { return false; };
+ }
+
// TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/532): Forward
// references for debug info instructions are still in discussion. We must
// update the following lines of code when we conclude the spec.
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt
index 88d56589..7508dc02 100644
--- a/source/opt/CMakeLists.txt
+++ b/source/opt/CMakeLists.txt
@@ -27,8 +27,11 @@ set(SPIRV_TOOLS_OPT_SOURCES
composite.h
const_folding_rules.h
constants.h
+ control_dependence.h
+ convert_to_sampled_image_pass.h
convert_to_half_pass.h
copy_prop_arrays.h
+ dataflow.h
dead_branch_elim_pass.h
dead_insert_elim_pass.h
dead_variable_elimination.h
@@ -36,6 +39,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
debug_info_manager.h
def_use_manager.h
desc_sroa.h
+ desc_sroa_util.h
dominator_analysis.h
dominator_tree.h
eliminate_dead_constant_pass.h
@@ -96,16 +100,19 @@ set(SPIRV_TOOLS_OPT_SOURCES
register_pressure.h
relax_float_ops_pass.h
remove_duplicates_pass.h
+ remove_unused_interface_variables_pass.h
+ replace_desc_array_access_using_var_index.h
replace_invalid_opc.h
scalar_analysis.h
scalar_analysis_nodes.h
scalar_replacement_pass.h
set_spec_constant_default_value_pass.h
simplification_pass.h
+ spread_volatile_semantics.h
ssa_rewrite_pass.h
strength_reduction_pass.h
strip_debug_info_pass.h
- strip_reflect_info_pass.h
+ strip_nonsemantic_info_pass.h
struct_cfg_analysis.h
tree_iterator.h
type_manager.h
@@ -132,8 +139,11 @@ set(SPIRV_TOOLS_OPT_SOURCES
composite.cpp
const_folding_rules.cpp
constants.cpp
+ control_dependence.cpp
+ convert_to_sampled_image_pass.cpp
convert_to_half_pass.cpp
copy_prop_arrays.cpp
+ dataflow.cpp
dead_branch_elim_pass.cpp
dead_insert_elim_pass.cpp
dead_variable_elimination.cpp
@@ -141,6 +151,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
debug_info_manager.cpp
def_use_manager.cpp
desc_sroa.cpp
+ desc_sroa_util.cpp
dominator_analysis.cpp
dominator_tree.cpp
eliminate_dead_constant_pass.cpp
@@ -197,16 +208,19 @@ set(SPIRV_TOOLS_OPT_SOURCES
register_pressure.cpp
relax_float_ops_pass.cpp
remove_duplicates_pass.cpp
+ remove_unused_interface_variables_pass.cpp
+ replace_desc_array_access_using_var_index.cpp
replace_invalid_opc.cpp
scalar_analysis.cpp
scalar_analysis_simplification.cpp
scalar_replacement_pass.cpp
set_spec_constant_default_value_pass.cpp
simplification_pass.cpp
+ spread_volatile_semantics.cpp
ssa_rewrite_pass.cpp
strength_reduction_pass.cpp
strip_debug_info_pass.cpp
- strip_reflect_info_pass.cpp
+ strip_nonsemantic_info_pass.cpp
struct_cfg_analysis.cpp
type_manager.cpp
types.cpp
diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp
index 7cffff57..9827c535 100644
--- a/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -1,7 +1,7 @@
// Copyright (c) 2017 The Khronos Group Inc.
// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG Inc.
-// Copyright (c) 2018 Google LLC
+// Copyright (c) 2018-2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -23,9 +23,11 @@
#include "source/cfa.h"
#include "source/latest_version_glsl_std_450_header.h"
#include "source/opt/eliminate_dead_functions_util.h"
+#include "source/opt/ir_builder.h"
#include "source/opt/iterator.h"
#include "source/opt/reflect.h"
#include "source/spirv_constant.h"
+#include "source/util/string_utils.h"
namespace spvtools {
namespace opt {
@@ -35,10 +37,10 @@ namespace {
const uint32_t kTypePointerStorageClassInIdx = 0;
const uint32_t kEntryPointFunctionIdInIdx = 1;
const uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
-const uint32_t kLoopMergeMergeBlockIdInIdx = 0;
const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
const uint32_t kCopyMemoryTargetAddrInIdx = 0;
const uint32_t kCopyMemorySourceAddrInIdx = 1;
+const uint32_t kLoadSourceAddrInIdx = 0;
const uint32_t kDebugDeclareOperandVariableIndex = 5;
const uint32_t kGlobalVariableVariableIndex = 12;
@@ -96,16 +98,21 @@ bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId, uint32_t storageClass) {
storageClass;
}
-bool AggressiveDCEPass::IsLocalVar(uint32_t varId) {
+bool AggressiveDCEPass::IsLocalVar(uint32_t varId, Function* func) {
if (IsVarOfStorage(varId, SpvStorageClassFunction)) {
return true;
}
- if (!private_like_local_) {
+
+ if (!IsVarOfStorage(varId, SpvStorageClassPrivate) &&
+ !IsVarOfStorage(varId, SpvStorageClassWorkgroup)) {
return false;
}
- return IsVarOfStorage(varId, SpvStorageClassPrivate) ||
- IsVarOfStorage(varId, SpvStorageClassWorkgroup);
+ // For a variable in the Private or WorkGroup storage class, the variable will
+ // get a new instance for every call to an entry point. If the entry point
+ // does not have a call, then no other function can read or write to that
+ // instance of the variable.
+ return IsEntryPointWithNoCalls(func);
}
void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) {
@@ -140,20 +147,21 @@ void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) {
bool AggressiveDCEPass::AllExtensionsSupported() const {
// If any extension not in allowlist, return false
for (auto& ei : get_module()->extensions()) {
- const char* extName =
- reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
+ const std::string extName = ei.GetInOperand(0).AsString();
if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
return false;
}
- return true;
-}
-
-bool AggressiveDCEPass::IsDead(Instruction* inst) {
- if (IsLive(inst)) return false;
- if ((inst->IsBranch() || inst->opcode() == SpvOpUnreachable) &&
- !IsStructuredHeader(context()->get_instr_block(inst), nullptr, nullptr,
- nullptr))
- return false;
+ // Only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise
+ // around unknown extended instruction sets even if they are non-semantic
+ for (auto& inst : context()->module()->ext_inst_imports()) {
+ assert(inst.opcode() == SpvOpExtInstImport &&
+ "Expecting an import of an extension's instruction set.");
+ const std::string extension_name = inst.GetInOperand(0).AsString();
+ if (spvtools::utils::starts_with(extension_name, "NonSemantic.") &&
+ extension_name != "NonSemantic.Shader.DebugInfo.100") {
+ return false;
+ }
+ }
return true;
}
@@ -173,12 +181,12 @@ bool AggressiveDCEPass::IsTargetDead(Instruction* inst) {
});
return dead;
}
- return IsDead(tInst);
+ return !IsLive(tInst);
}
void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) {
// Only process locals
- if (!IsLocalVar(varId)) return;
+ if (!IsLocalVar(varId, func)) return;
// Return if already processed
if (live_local_vars_.find(varId) != live_local_vars_.end()) return;
// Mark all stores to varId as live
@@ -187,66 +195,6 @@ void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) {
live_local_vars_.insert(varId);
}
-bool AggressiveDCEPass::IsStructuredHeader(BasicBlock* bp,
- Instruction** mergeInst,
- Instruction** branchInst,
- uint32_t* mergeBlockId) {
- if (!bp) return false;
- Instruction* mi = bp->GetMergeInst();
- if (mi == nullptr) return false;
- Instruction* bri = &*bp->tail();
- if (branchInst != nullptr) *branchInst = bri;
- if (mergeInst != nullptr) *mergeInst = mi;
- if (mergeBlockId != nullptr) *mergeBlockId = mi->GetSingleWordInOperand(0);
- return true;
-}
-
-void AggressiveDCEPass::ComputeBlock2HeaderMaps(
- std::list<BasicBlock*>& structuredOrder) {
- block2headerBranch_.clear();
- header2nextHeaderBranch_.clear();
- branch2merge_.clear();
- structured_order_index_.clear();
- std::stack<Instruction*> currentHeaderBranch;
- currentHeaderBranch.push(nullptr);
- uint32_t currentMergeBlockId = 0;
- uint32_t index = 0;
- for (auto bi = structuredOrder.begin(); bi != structuredOrder.end();
- ++bi, ++index) {
- structured_order_index_[*bi] = index;
- // If this block is the merge block of the current control construct,
- // we are leaving the current construct so we must update state
- if ((*bi)->id() == currentMergeBlockId) {
- currentHeaderBranch.pop();
- Instruction* chb = currentHeaderBranch.top();
- if (chb != nullptr)
- currentMergeBlockId = branch2merge_[chb]->GetSingleWordInOperand(0);
- }
- Instruction* mergeInst;
- Instruction* branchInst;
- uint32_t mergeBlockId;
- bool is_header =
- IsStructuredHeader(*bi, &mergeInst, &branchInst, &mergeBlockId);
- // Map header block to next enclosing header.
- if (is_header) header2nextHeaderBranch_[*bi] = currentHeaderBranch.top();
- // If this is a loop header, update state first so the block will map to
- // itself.
- if (is_header && mergeInst->opcode() == SpvOpLoopMerge) {
- currentHeaderBranch.push(branchInst);
- branch2merge_[branchInst] = mergeInst;
- currentMergeBlockId = mergeBlockId;
- }
- // Map the block to the current construct.
- block2headerBranch_[*bi] = currentHeaderBranch.top();
- // If this is an if header, update state so following blocks map to the if.
- if (is_header && mergeInst->opcode() == SpvOpSelectionMerge) {
- currentHeaderBranch.push(branchInst);
- branch2merge_[branchInst] = mergeInst;
- currentMergeBlockId = mergeBlockId;
- }
- }
-}
-
void AggressiveDCEPass::AddBranch(uint32_t labelId, BasicBlock* bp) {
std::unique_ptr<Instruction> newBranch(
new Instruction(context(), SpvOpBranch, 0, 0,
@@ -262,23 +210,18 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
mergeInst->opcode() == SpvOpLoopMerge);
BasicBlock* header = context()->get_instr_block(mergeInst);
- uint32_t headerIndex = structured_order_index_[header];
const uint32_t mergeId = mergeInst->GetSingleWordInOperand(0);
- BasicBlock* merge = context()->get_instr_block(mergeId);
- uint32_t mergeIndex = structured_order_index_[merge];
- get_def_use_mgr()->ForEachUser(
- mergeId, [headerIndex, mergeIndex, this](Instruction* user) {
- if (!user->IsBranch()) return;
- BasicBlock* block = context()->get_instr_block(user);
- uint32_t index = structured_order_index_[block];
- if (headerIndex < index && index < mergeIndex) {
- // This is a break from the loop.
- AddToWorklist(user);
- // Add branch's merge if there is one.
- Instruction* userMerge = branch2merge_[user];
- if (userMerge != nullptr) AddToWorklist(userMerge);
- }
- });
+ get_def_use_mgr()->ForEachUser(mergeId, [header, this](Instruction* user) {
+ if (!user->IsBranch()) return;
+ BasicBlock* block = context()->get_instr_block(user);
+ if (BlockIsInConstruct(header, block)) {
+ // This is a break from the loop.
+ AddToWorklist(user);
+ // Add branch's merge if there is one.
+ Instruction* userMerge = GetMergeInstruction(user);
+ if (userMerge != nullptr) AddToWorklist(userMerge);
+ }
+ });
if (mergeInst->opcode() != SpvOpLoopMerge) {
return;
@@ -292,7 +235,7 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
if (op == SpvOpBranchConditional || op == SpvOpSwitch) {
// A conditional branch or switch can only be a continue if it does not
// have a merge instruction or its merge block is not the continue block.
- Instruction* hdrMerge = branch2merge_[user];
+ Instruction* hdrMerge = GetMergeInstruction(user);
if (hdrMerge != nullptr && hdrMerge->opcode() == SpvOpSelectionMerge) {
uint32_t hdrMergeId =
hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
@@ -304,9 +247,9 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
// An unconditional branch can only be a continue if it is not
// branching to its own merge block.
BasicBlock* blk = context()->get_instr_block(user);
- Instruction* hdrBranch = block2headerBranch_[blk];
+ Instruction* hdrBranch = GetHeaderBranch(blk);
if (hdrBranch == nullptr) return;
- Instruction* hdrMerge = branch2merge_[hdrBranch];
+ Instruction* hdrMerge = GetMergeInstruction(hdrBranch);
if (hdrMerge->opcode() == SpvOpLoopMerge) return;
uint32_t hdrMergeId =
hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
@@ -319,254 +262,36 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
}
bool AggressiveDCEPass::AggressiveDCE(Function* func) {
- // Mark function parameters as live.
- AddToWorklist(&func->DefInst());
- func->ForEachParam(
- [this](const Instruction* param) {
- AddToWorklist(const_cast<Instruction*>(param));
- },
- false);
-
- // Compute map from block to controlling conditional branch
- std::list<BasicBlock*> structuredOrder;
- cfg()->ComputeStructuredOrder(func, &*func->begin(), &structuredOrder);
- ComputeBlock2HeaderMaps(structuredOrder);
- bool modified = false;
- // Add instructions with external side effects to worklist. Also add branches
- // EXCEPT those immediately contained in an "if" selection construct or a loop
- // or continue construct.
- // TODO(greg-lunarg): Handle Frexp, Modf more optimally
- call_in_func_ = false;
- func_is_entry_point_ = false;
- private_stores_.clear();
+ std::list<BasicBlock*> structured_order;
+ cfg()->ComputeStructuredOrder(func, &*func->begin(), &structured_order);
live_local_vars_.clear();
- // Stacks to keep track of when we are inside an if- or loop-construct.
- // When immediately inside an if- or loop-construct, we do not initially
- // mark branches live. All other branches must be marked live.
- std::stack<bool> assume_branches_live;
- std::stack<uint32_t> currentMergeBlockId;
- // Push sentinel values on stack for when outside of any control flow.
- assume_branches_live.push(true);
- currentMergeBlockId.push(0);
- for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
- // If exiting if or loop, update stacks
- if ((*bi)->id() == currentMergeBlockId.top()) {
- assume_branches_live.pop();
- currentMergeBlockId.pop();
- }
- for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) {
- SpvOp op = ii->opcode();
- switch (op) {
- case SpvOpStore: {
- uint32_t varId;
- (void)GetPtr(&*ii, &varId);
- // Mark stores as live if their variable is not function scope
- // and is not private scope. Remember private stores for possible
- // later inclusion. We cannot call IsLocalVar at this point because
- // private_like_local_ has not been set yet.
- if (IsVarOfStorage(varId, SpvStorageClassPrivate) ||
- IsVarOfStorage(varId, SpvStorageClassWorkgroup))
- private_stores_.push_back(&*ii);
- else if (!IsVarOfStorage(varId, SpvStorageClassFunction))
- AddToWorklist(&*ii);
- } break;
- case SpvOpCopyMemory:
- case SpvOpCopyMemorySized: {
- uint32_t varId;
- (void)GetPtr(ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx),
- &varId);
- if (IsVarOfStorage(varId, SpvStorageClassPrivate) ||
- IsVarOfStorage(varId, SpvStorageClassWorkgroup))
- private_stores_.push_back(&*ii);
- else if (!IsVarOfStorage(varId, SpvStorageClassFunction))
- AddToWorklist(&*ii);
- } break;
- case SpvOpLoopMerge: {
- assume_branches_live.push(false);
- currentMergeBlockId.push(
- ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx));
- } break;
- case SpvOpSelectionMerge: {
- assume_branches_live.push(false);
- currentMergeBlockId.push(
- ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx));
- } break;
- case SpvOpSwitch:
- case SpvOpBranch:
- case SpvOpBranchConditional:
- case SpvOpUnreachable: {
- if (assume_branches_live.top()) {
- AddToWorklist(&*ii);
- }
- } break;
- default: {
- // Function calls, atomics, function params, function returns, etc.
- // TODO(greg-lunarg): function calls live only if write to non-local
- if (!ii->IsOpcodeSafeToDelete()) {
- AddToWorklist(&*ii);
- }
- // Remember function calls
- if (op == SpvOpFunctionCall) call_in_func_ = true;
- } break;
- }
- }
- }
- // See if current function is an entry point
- for (auto& ei : get_module()->entry_points()) {
- if (ei.GetSingleWordInOperand(kEntryPointFunctionIdInIdx) ==
- func->result_id()) {
- func_is_entry_point_ = true;
- break;
- }
- }
- // If the current function is an entry point and has no function calls,
- // we can optimize private variables as locals
- private_like_local_ = func_is_entry_point_ && !call_in_func_;
- // If privates are not like local, add their stores to worklist
- if (!private_like_local_)
- for (auto& ps : private_stores_) AddToWorklist(ps);
- // Perform closure on live instruction set.
- while (!worklist_.empty()) {
- Instruction* liveInst = worklist_.front();
- // Add all operand instructions if not already live
- liveInst->ForEachInId([&liveInst, this](const uint32_t* iid) {
- Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
- // Do not add label if an operand of a branch. This is not needed
- // as part of live code discovery and can create false live code,
- // for example, the branch to a header of a loop.
- if (inInst->opcode() == SpvOpLabel && liveInst->IsBranch()) return;
- AddToWorklist(inInst);
- });
- if (liveInst->type_id() != 0) {
- AddToWorklist(get_def_use_mgr()->GetDef(liveInst->type_id()));
- }
- // If in a structured if or loop construct, add the controlling
- // conditional branch and its merge.
- BasicBlock* blk = context()->get_instr_block(liveInst);
- Instruction* branchInst = block2headerBranch_[blk];
- if (branchInst != nullptr) {
- AddToWorklist(branchInst);
- Instruction* mergeInst = branch2merge_[branchInst];
- AddToWorklist(mergeInst);
- }
- // If the block is a header, add the next outermost controlling
- // conditional branch and its merge.
- Instruction* nextBranchInst = header2nextHeaderBranch_[blk];
- if (nextBranchInst != nullptr) {
- AddToWorklist(nextBranchInst);
- Instruction* mergeInst = branch2merge_[nextBranchInst];
- AddToWorklist(mergeInst);
- }
- // If local load, add all variable's stores if variable not already live
- if (liveInst->opcode() == SpvOpLoad || liveInst->IsAtomicWithLoad()) {
- uint32_t varId;
- (void)GetPtr(liveInst, &varId);
- if (varId != 0) {
- ProcessLoad(func, varId);
- }
- // Process memory copies like loads
- } else if (liveInst->opcode() == SpvOpCopyMemory ||
- liveInst->opcode() == SpvOpCopyMemorySized) {
- uint32_t varId;
- (void)GetPtr(liveInst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx),
- &varId);
- if (varId != 0) {
- ProcessLoad(func, varId);
- }
- // If DebugDeclare, process as load of variable
- } else if (liveInst->GetOpenCL100DebugOpcode() ==
- OpenCLDebugInfo100DebugDeclare) {
- uint32_t varId =
- liveInst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
- ProcessLoad(func, varId);
- // If DebugValue with Deref, process as load of variable
- } else if (liveInst->GetOpenCL100DebugOpcode() ==
- OpenCLDebugInfo100DebugValue) {
- uint32_t varId = context()
- ->get_debug_info_mgr()
- ->GetVariableIdOfDebugValueUsedForDeclare(liveInst);
- if (varId != 0) ProcessLoad(func, varId);
- // If merge, add other branches that are part of its control structure
- } else if (liveInst->opcode() == SpvOpLoopMerge ||
- liveInst->opcode() == SpvOpSelectionMerge) {
- AddBreaksAndContinuesToWorklist(liveInst);
- // If function call, treat as if it loads from all pointer arguments
- } else if (liveInst->opcode() == SpvOpFunctionCall) {
- liveInst->ForEachInId([this, func](const uint32_t* iid) {
- // Skip non-ptr args
- if (!IsPtr(*iid)) return;
- uint32_t varId;
- (void)GetPtr(*iid, &varId);
- ProcessLoad(func, varId);
- });
- // If function parameter, treat as if it's result id is loaded from
- } else if (liveInst->opcode() == SpvOpFunctionParameter) {
- ProcessLoad(func, liveInst->result_id());
- // We treat an OpImageTexelPointer as a load of the pointer, and
- // that value is manipulated to get the result.
- } else if (liveInst->opcode() == SpvOpImageTexelPointer) {
- uint32_t varId;
- (void)GetPtr(liveInst, &varId);
- if (varId != 0) {
- ProcessLoad(func, varId);
- }
- }
-
- // Add OpDecorateId instructions that apply to this instruction to the work
- // list. We use the decoration manager to look through the group
- // decorations to get to the OpDecorate* instructions themselves.
- auto decorations =
- get_decoration_mgr()->GetDecorationsFor(liveInst->result_id(), false);
- for (Instruction* dec : decorations) {
- // We only care about OpDecorateId instructions because the are the only
- // decorations that will reference an id that will have to be kept live
- // because of that use.
- if (dec->opcode() != SpvOpDecorateId) {
- continue;
- }
- if (dec->GetSingleWordInOperand(1) ==
- SpvDecorationHlslCounterBufferGOOGLE) {
- // These decorations should not force the use id to be live. It will be
- // removed if either the target or the in operand are dead.
- continue;
- }
- AddToWorklist(dec);
- }
-
- // Add DebugScope and DebugInlinedAt for |liveInst| to the work list.
- if (liveInst->GetDebugScope().GetLexicalScope() != kNoDebugScope) {
- auto* scope = get_def_use_mgr()->GetDef(
- liveInst->GetDebugScope().GetLexicalScope());
- AddToWorklist(scope);
- }
- if (liveInst->GetDebugInlinedAt() != kNoInlinedAt) {
- auto* inlined_at =
- get_def_use_mgr()->GetDef(liveInst->GetDebugInlinedAt());
- AddToWorklist(inlined_at);
- }
- worklist_.pop();
- }
+ InitializeWorkList(func, structured_order);
+ ProcessWorkList(func);
+ return KillDeadInstructions(func, structured_order);
+}
- // Kill dead instructions and remember dead blocks
- for (auto bi = structuredOrder.begin(); bi != structuredOrder.end();) {
- uint32_t mergeBlockId = 0;
- (*bi)->ForEachInst([this, &modified, &mergeBlockId](Instruction* inst) {
- if (!IsDead(inst)) return;
+bool AggressiveDCEPass::KillDeadInstructions(
+ const Function* func, std::list<BasicBlock*>& structured_order) {
+ bool modified = false;
+ for (auto bi = structured_order.begin(); bi != structured_order.end();) {
+ uint32_t merge_block_id = 0;
+ (*bi)->ForEachInst([this, &modified, &merge_block_id](Instruction* inst) {
+ if (IsLive(inst)) return;
if (inst->opcode() == SpvOpLabel) return;
// If dead instruction is selection merge, remember merge block
// for new branch at end of block
if (inst->opcode() == SpvOpSelectionMerge ||
inst->opcode() == SpvOpLoopMerge)
- mergeBlockId = inst->GetSingleWordInOperand(0);
+ merge_block_id = inst->GetSingleWordInOperand(0);
to_kill_.push_back(inst);
modified = true;
});
// If a structured if or loop was deleted, add a branch to its merge
// block, and traverse to the merge block and continue processing there.
// We know the block still exists because the label is not deleted.
- if (mergeBlockId != 0) {
- AddBranch(mergeBlockId, *bi);
- for (++bi; (*bi)->id() != mergeBlockId; ++bi) {
+ if (merge_block_id != 0) {
+ AddBranch(merge_block_id, *bi);
+ for (++bi; (*bi)->id() != merge_block_id; ++bi) {
}
auto merge_terminator = (*bi)->terminator();
@@ -589,13 +314,252 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) {
live_insts_.Set(merge_terminator->unique_id());
}
} else {
+ Instruction* inst = (*bi)->terminator();
+ if (!IsLive(inst)) {
+ // If the terminator is not live, this block has no live instructions,
+ // and it will be unreachable.
+ AddUnreachable(*bi);
+ }
++bi;
}
}
-
return modified;
}
+void AggressiveDCEPass::ProcessWorkList(Function* func) {
+ while (!worklist_.empty()) {
+ Instruction* live_inst = worklist_.front();
+ worklist_.pop();
+ AddOperandsToWorkList(live_inst);
+ MarkBlockAsLive(live_inst);
+ MarkLoadedVariablesAsLive(func, live_inst);
+ AddDecorationsToWorkList(live_inst);
+ AddDebugInstructionsToWorkList(live_inst);
+ }
+}
+
+void AggressiveDCEPass::AddDebugInstructionsToWorkList(
+ const Instruction* inst) {
+ for (auto& line_inst : inst->dbg_line_insts()) {
+ if (line_inst.IsDebugLineInst()) {
+ AddOperandsToWorkList(&line_inst);
+ }
+ }
+
+ if (inst->GetDebugScope().GetLexicalScope() != kNoDebugScope) {
+ auto* scope =
+ get_def_use_mgr()->GetDef(inst->GetDebugScope().GetLexicalScope());
+ AddToWorklist(scope);
+ }
+ if (inst->GetDebugInlinedAt() != kNoInlinedAt) {
+ auto* inlined_at = get_def_use_mgr()->GetDef(inst->GetDebugInlinedAt());
+ AddToWorklist(inlined_at);
+ }
+}
+
+void AggressiveDCEPass::AddDecorationsToWorkList(const Instruction* inst) {
+ // Add OpDecorateId instructions that apply to this instruction to the work
+ // list. We use the decoration manager to look through the group
+ // decorations to get to the OpDecorate* instructions themselves.
+ auto decorations =
+ get_decoration_mgr()->GetDecorationsFor(inst->result_id(), false);
+ for (Instruction* dec : decorations) {
+ // We only care about OpDecorateId instructions because the are the only
+ // decorations that will reference an id that will have to be kept live
+ // because of that use.
+ if (dec->opcode() != SpvOpDecorateId) {
+ continue;
+ }
+ if (dec->GetSingleWordInOperand(1) ==
+ SpvDecorationHlslCounterBufferGOOGLE) {
+ // These decorations should not force the use id to be live. It will be
+ // removed if either the target or the in operand are dead.
+ continue;
+ }
+ AddToWorklist(dec);
+ }
+}
+
+void AggressiveDCEPass::MarkLoadedVariablesAsLive(Function* func,
+ Instruction* inst) {
+ std::vector<uint32_t> live_variables = GetLoadedVariables(inst);
+ for (uint32_t var_id : live_variables) {
+ ProcessLoad(func, var_id);
+ }
+}
+
+std::vector<uint32_t> AggressiveDCEPass::GetLoadedVariables(Instruction* inst) {
+ if (inst->opcode() == SpvOpFunctionCall) {
+ return GetLoadedVariablesFromFunctionCall(inst);
+ }
+ uint32_t var_id = GetLoadedVariableFromNonFunctionCalls(inst);
+ if (var_id == 0) {
+ return {};
+ }
+ return {var_id};
+}
+
+uint32_t AggressiveDCEPass::GetLoadedVariableFromNonFunctionCalls(
+ Instruction* inst) {
+ std::vector<uint32_t> live_variables;
+ if (inst->IsAtomicWithLoad()) {
+ return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx));
+ }
+
+ switch (inst->opcode()) {
+ case SpvOpLoad:
+ case SpvOpImageTexelPointer:
+ return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx));
+ case SpvOpCopyMemory:
+ case SpvOpCopyMemorySized:
+ return GetVariableId(
+ inst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx));
+ default:
+ break;
+ }
+
+ switch (inst->GetCommonDebugOpcode()) {
+ case CommonDebugInfoDebugDeclare:
+ return inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+ case CommonDebugInfoDebugValue: {
+ analysis::DebugInfoManager* debug_info_mgr =
+ context()->get_debug_info_mgr();
+ return debug_info_mgr->GetVariableIdOfDebugValueUsedForDeclare(inst);
+ }
+ default:
+ break;
+ }
+ return 0;
+}
+
+std::vector<uint32_t> AggressiveDCEPass::GetLoadedVariablesFromFunctionCall(
+ const Instruction* inst) {
+ assert(inst->opcode() == SpvOpFunctionCall);
+ std::vector<uint32_t> live_variables;
+ inst->ForEachInId([this, &live_variables](const uint32_t* operand_id) {
+ if (!IsPtr(*operand_id)) return;
+ uint32_t var_id = GetVariableId(*operand_id);
+ live_variables.push_back(var_id);
+ });
+ return live_variables;
+}
+
+uint32_t AggressiveDCEPass::GetVariableId(uint32_t ptr_id) {
+ assert(IsPtr(ptr_id) &&
+ "Cannot get the variable when input is not a pointer.");
+ uint32_t varId = 0;
+ (void)GetPtr(ptr_id, &varId);
+ return varId;
+}
+
+void AggressiveDCEPass::MarkBlockAsLive(Instruction* inst) {
+ BasicBlock* basic_block = context()->get_instr_block(inst);
+ if (basic_block == nullptr) {
+ return;
+ }
+
+ // If we intend to keep this instruction, we need the block label and
+ // block terminator to have a valid block for the instruction.
+ AddToWorklist(basic_block->GetLabelInst());
+
+ // We need to mark the successors blocks that follow as live. If this is
+ // header of the merge construct, the construct may be folded, but we will
+ // definitely need the merge label. If it is not a construct, the terminator
+ // must be live, and the successor blocks will be marked as live when
+ // processing the terminator.
+ uint32_t merge_id = basic_block->MergeBlockIdIfAny();
+ if (merge_id == 0) {
+ AddToWorklist(basic_block->terminator());
+ } else {
+ AddToWorklist(context()->get_def_use_mgr()->GetDef(merge_id));
+ }
+
+ // Mark the structured control flow constructs that contains this block as
+ // live. If |inst| is an instruction in the loop header, then it is part of
+ // the loop, so the loop construct must be live. We exclude the label because
+ // it does not matter how many times it is executed. This could be extended
+ // to more instructions, but we will need it for now.
+ if (inst->opcode() != SpvOpLabel)
+ MarkLoopConstructAsLiveIfLoopHeader(basic_block);
+
+ Instruction* next_branch_inst = GetBranchForNextHeader(basic_block);
+ if (next_branch_inst != nullptr) {
+ AddToWorklist(next_branch_inst);
+ Instruction* mergeInst = GetMergeInstruction(next_branch_inst);
+ AddToWorklist(mergeInst);
+ }
+
+ if (inst->opcode() == SpvOpLoopMerge ||
+ inst->opcode() == SpvOpSelectionMerge) {
+ AddBreaksAndContinuesToWorklist(inst);
+ }
+}
+void AggressiveDCEPass::MarkLoopConstructAsLiveIfLoopHeader(
+ BasicBlock* basic_block) {
+ // If this is the header for a loop, then loop structure needs to keep as well
+ // because the loop header is also part of the loop.
+ Instruction* merge_inst = basic_block->GetLoopMergeInst();
+ if (merge_inst != nullptr) {
+ AddToWorklist(basic_block->terminator());
+ AddToWorklist(merge_inst);
+ }
+}
+
+void AggressiveDCEPass::AddOperandsToWorkList(const Instruction* inst) {
+ inst->ForEachInId([this](const uint32_t* iid) {
+ Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
+ AddToWorklist(inInst);
+ });
+ if (inst->type_id() != 0) {
+ AddToWorklist(get_def_use_mgr()->GetDef(inst->type_id()));
+ }
+}
+
+void AggressiveDCEPass::InitializeWorkList(
+ Function* func, std::list<BasicBlock*>& structured_order) {
+ AddToWorklist(&func->DefInst());
+ MarkFunctionParameterAsLive(func);
+ MarkFirstBlockAsLive(func);
+
+ // Add instructions with external side effects to the worklist. Also add
+ // branches that are not attached to a structured construct.
+ // TODO(s-perron): The handling of branch seems to be adhoc. This needs to be
+ // cleaned up.
+ for (auto& bi : structured_order) {
+ for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
+ SpvOp op = ii->opcode();
+ if (ii->IsBranch()) {
+ continue;
+ }
+ switch (op) {
+ case SpvOpStore: {
+ uint32_t var_id = 0;
+ (void)GetPtr(&*ii, &var_id);
+ if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii);
+ } break;
+ case SpvOpCopyMemory:
+ case SpvOpCopyMemorySized: {
+ uint32_t var_id = 0;
+ uint32_t target_addr_id =
+ ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx);
+ (void)GetPtr(target_addr_id, &var_id);
+ if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii);
+ } break;
+ case SpvOpLoopMerge:
+ case SpvOpSelectionMerge:
+ case SpvOpUnreachable:
+ break;
+ default: {
+ // Function calls, atomics, function params, function returns, etc.
+ if (!ii->IsOpcodeSafeToDelete()) {
+ AddToWorklist(&*ii);
+ }
+ } break;
+ }
+ }
+ }
+}
+
void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
// Keep all execution modes.
for (auto& exec : get_module()->execution_modes()) {
@@ -603,11 +567,7 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
}
// Keep all entry points.
for (auto& entry : get_module()->entry_points()) {
- if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
- // In SPIR-V 1.4 and later, entry points must list all global variables
- // used. DCE can still remove non-input/output variables and update the
- // interface list. Mark the entry point as live and inputs and outputs as
- // live, but defer decisions all other interfaces.
+ if (!preserve_interface_) {
live_insts_.Set(entry.unique_id());
// The actual function is live always.
AddToWorklist(
@@ -615,8 +575,9 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
for (uint32_t i = 3; i < entry.NumInOperands(); ++i) {
auto* var = get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i));
auto storage_class = var->GetSingleWordInOperand(0u);
- if (storage_class == SpvStorageClassInput ||
- storage_class == SpvStorageClassOutput) {
+ // Vulkan support outputs without an associated input, but not inputs
+ // without an associated output.
+ if (storage_class == SpvStorageClassOutput) {
AddToWorklist(var);
}
}
@@ -650,16 +611,25 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
}
// For each DebugInfo GlobalVariable keep all operands except the Variable.
- // Later, if the variable is dead, we will set the operand to DebugInfoNone.
+ // Later, if the variable is killed with KillInst(), we will set the operand
+ // to DebugInfoNone. Create and save DebugInfoNone now for this possible
+ // later use. This is slightly unoptimal, but it avoids generating it during
+ // instruction killing when the module is not consistent.
+ bool debug_global_seen = false;
for (auto& dbg : get_module()->ext_inst_debuginfo()) {
- if (dbg.GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugGlobalVariable)
+ if (dbg.GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable)
continue;
+ debug_global_seen = true;
dbg.ForEachInId([this](const uint32_t* iid) {
- Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
- if (inInst->opcode() == SpvOpVariable) return;
- AddToWorklist(inInst);
+ Instruction* in_inst = get_def_use_mgr()->GetDef(*iid);
+ if (in_inst->opcode() == SpvOpVariable) return;
+ AddToWorklist(in_inst);
});
}
+ if (debug_global_seen) {
+ auto dbg_none = context()->get_debug_info_mgr()->GetDebugInfoNone();
+ AddToWorklist(dbg_none);
+ }
}
Pass::Status AggressiveDCEPass::ProcessImpl() {
@@ -691,7 +661,7 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
// Process all entry point functions.
ProcessFunction pfn = [this](Function* fp) { return AggressiveDCE(fp); };
- modified |= context()->ProcessEntryPointCallTree(pfn);
+ modified |= context()->ProcessReachableCallTree(pfn);
// 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
@@ -718,21 +688,20 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
// Cleanup all CFG including all unreachable blocks.
ProcessFunction cleanup = [this](Function* f) { return CFGCleanup(f); };
- modified |= context()->ProcessEntryPointCallTree(cleanup);
+ modified |= context()->ProcessReachableCallTree(cleanup);
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
bool AggressiveDCEPass::EliminateDeadFunctions() {
// Identify live functions first. Those that are not live
- // are dead. ADCE is disabled for non-shaders so we do not check for exported
- // functions here.
+ // are dead.
std::unordered_set<const Function*> live_function_set;
ProcessFunction mark_live = [&live_function_set](Function* fp) {
live_function_set.insert(fp);
return false;
};
- context()->ProcessEntryPointCallTree(mark_live);
+ context()->ProcessReachableCallTree(mark_live);
bool modified = false;
for (auto funcIter = get_module()->begin();
@@ -798,7 +767,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
uint32_t counter_buffer_id = annotation->GetSingleWordInOperand(2);
Instruction* counter_buffer_inst =
get_def_use_mgr()->GetDef(counter_buffer_id);
- if (IsDead(counter_buffer_inst)) {
+ if (!IsLive(counter_buffer_inst)) {
context()->KillInst(annotation);
modified = true;
}
@@ -813,7 +782,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
for (uint32_t i = 1; i < annotation->NumOperands();) {
Instruction* opInst =
get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
- if (IsDead(opInst)) {
+ if (!IsLive(opInst)) {
// Don't increment |i|.
annotation->RemoveOperand(i);
modified = true;
@@ -840,7 +809,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
for (uint32_t i = 1; i < annotation->NumOperands();) {
Instruction* opInst =
get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
- if (IsDead(opInst)) {
+ if (!IsLive(opInst)) {
// Don't increment |i|.
annotation->RemoveOperand(i + 1);
annotation->RemoveOperand(i);
@@ -874,14 +843,13 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
}
for (auto& dbg : get_module()->ext_inst_debuginfo()) {
- if (!IsDead(&dbg)) continue;
+ if (IsLive(&dbg)) continue;
// Save GlobalVariable if its variable is live, otherwise null out variable
// index
- if (dbg.GetOpenCL100DebugOpcode() ==
- OpenCLDebugInfo100DebugGlobalVariable) {
+ if (dbg.GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) {
auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex);
Instruction* var_inst = get_def_use_mgr()->GetDef(var_id);
- if (!IsDead(var_inst)) continue;
+ if (IsLive(var_inst)) continue;
context()->ForgetUses(&dbg);
dbg.SetOperand(
kGlobalVariableVariableIndex,
@@ -896,7 +864,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
// Since ADCE is disabled for non-shaders, we don't check for export linkage
// attributes here.
for (auto& val : get_module()->types_values()) {
- if (IsDead(&val)) {
+ if (!IsLive(&val)) {
// Save forwarded pointer if pointer is live since closure does not mark
// this live as it does not have a result id. This is a little too
// conservative since it is not known if the structure type that needed
@@ -904,14 +872,14 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
if (val.opcode() == SpvOpTypeForwardPointer) {
uint32_t ptr_ty_id = val.GetSingleWordInOperand(0);
Instruction* ptr_ty_inst = get_def_use_mgr()->GetDef(ptr_ty_id);
- if (!IsDead(ptr_ty_inst)) continue;
+ if (IsLive(ptr_ty_inst)) continue;
}
to_kill_.push_back(&val);
modified = true;
}
}
- if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
+ if (!preserve_interface_) {
// Remove the dead interface variables from the entry point interface list.
for (auto& entry : get_module()->entry_points()) {
std::vector<Operand> new_operands;
@@ -922,7 +890,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
} else {
auto* var =
get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i));
- if (!IsDead(var)) {
+ if (IsLive(var)) {
new_operands.push_back(entry.GetInOperand(i));
}
}
@@ -937,8 +905,6 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
return modified;
}
-AggressiveDCEPass::AggressiveDCEPass() = default;
-
Pass::Status AggressiveDCEPass::Process() {
// Initialize extensions allowlist
InitExtensions();
@@ -999,8 +965,119 @@ void AggressiveDCEPass::InitExtensions() {
"SPV_KHR_vulkan_memory_model",
"SPV_KHR_subgroup_uniform_control_flow",
"SPV_KHR_integer_dot_product",
+ "SPV_EXT_shader_image_int64",
+ "SPV_KHR_non_semantic_info",
});
}
+Instruction* AggressiveDCEPass::GetHeaderBranch(BasicBlock* blk) {
+ if (blk == nullptr) {
+ return nullptr;
+ }
+ BasicBlock* header_block = GetHeaderBlock(blk);
+ if (header_block == nullptr) {
+ return nullptr;
+ }
+ return header_block->terminator();
+}
+
+BasicBlock* AggressiveDCEPass::GetHeaderBlock(BasicBlock* blk) const {
+ if (blk == nullptr) {
+ return nullptr;
+ }
+
+ BasicBlock* header_block = nullptr;
+ if (blk->IsLoopHeader()) {
+ header_block = blk;
+ } else {
+ uint32_t header =
+ context()->GetStructuredCFGAnalysis()->ContainingConstruct(blk->id());
+ header_block = context()->get_instr_block(header);
+ }
+ return header_block;
+}
+
+Instruction* AggressiveDCEPass::GetMergeInstruction(Instruction* inst) {
+ BasicBlock* bb = context()->get_instr_block(inst);
+ if (bb == nullptr) {
+ return nullptr;
+ }
+ return bb->GetMergeInst();
+}
+
+Instruction* AggressiveDCEPass::GetBranchForNextHeader(BasicBlock* blk) {
+ if (blk == nullptr) {
+ return nullptr;
+ }
+
+ if (blk->IsLoopHeader()) {
+ uint32_t header =
+ context()->GetStructuredCFGAnalysis()->ContainingConstruct(blk->id());
+ blk = context()->get_instr_block(header);
+ }
+ return GetHeaderBranch(blk);
+}
+
+void AggressiveDCEPass::MarkFunctionParameterAsLive(const Function* func) {
+ func->ForEachParam(
+ [this](const Instruction* param) {
+ AddToWorklist(const_cast<Instruction*>(param));
+ },
+ false);
+}
+
+bool AggressiveDCEPass::BlockIsInConstruct(BasicBlock* header_block,
+ BasicBlock* bb) {
+ if (bb == nullptr || header_block == nullptr) {
+ return false;
+ }
+
+ uint32_t current_header = bb->id();
+ while (current_header != 0) {
+ if (current_header == header_block->id()) return true;
+ current_header = context()->GetStructuredCFGAnalysis()->ContainingConstruct(
+ current_header);
+ }
+ return false;
+}
+
+bool AggressiveDCEPass::IsEntryPointWithNoCalls(Function* func) {
+ auto cached_result = entry_point_with_no_calls_cache_.find(func->result_id());
+ if (cached_result != entry_point_with_no_calls_cache_.end()) {
+ return cached_result->second;
+ }
+ bool result = IsEntryPoint(func) && !HasCall(func);
+ entry_point_with_no_calls_cache_[func->result_id()] = result;
+ return result;
+}
+
+bool AggressiveDCEPass::IsEntryPoint(Function* func) {
+ for (const Instruction& entry_point : get_module()->entry_points()) {
+ uint32_t entry_point_id =
+ entry_point.GetSingleWordInOperand(kEntryPointFunctionIdInIdx);
+ if (entry_point_id == func->result_id()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool AggressiveDCEPass::HasCall(Function* func) {
+ return !func->WhileEachInst(
+ [](Instruction* inst) { return inst->opcode() != SpvOpFunctionCall; });
+}
+
+void AggressiveDCEPass::MarkFirstBlockAsLive(Function* func) {
+ BasicBlock* first_block = &*func->begin();
+ MarkBlockAsLive(first_block->GetLabelInst());
+}
+
+void AggressiveDCEPass::AddUnreachable(BasicBlock*& block) {
+ InstructionBuilder builder(
+ context(), block,
+ IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDefUse);
+ builder.AddUnreachable();
+}
+
} // namespace opt
} // namespace spvtools
diff --git a/source/opt/aggressive_dead_code_elim_pass.h b/source/opt/aggressive_dead_code_elim_pass.h
index f02e729f..1b3fd1e8 100644
--- a/source/opt/aggressive_dead_code_elim_pass.h
+++ b/source/opt/aggressive_dead_code_elim_pass.h
@@ -44,7 +44,9 @@ class AggressiveDCEPass : public MemPass {
using GetBlocksFunction =
std::function<std::vector<BasicBlock*>*(const BasicBlock*)>;
- AggressiveDCEPass();
+ AggressiveDCEPass(bool preserve_interface = false)
+ : preserve_interface_(preserve_interface) {}
+
const char* name() const override { return "eliminate-dead-code-aggressive"; }
Status Process() override;
@@ -55,23 +57,26 @@ class AggressiveDCEPass : public MemPass {
}
private:
+ // Preserve entry point interface if true. All variables in interface
+ // will be marked live and will not be eliminated. This mode is needed by
+ // GPU-Assisted Validation instrumentation where a change in the interface
+ // is not allowed.
+ bool preserve_interface_;
+
// Return true if |varId| is a variable of |storageClass|. |varId| must either
// be 0 or the result of an instruction.
bool IsVarOfStorage(uint32_t varId, uint32_t storageClass);
- // Return true if |varId| is variable of function storage class or is
- // private variable and privates can be optimized like locals (see
- // privates_like_local_).
- bool IsLocalVar(uint32_t varId);
+ // Return true if the instance of the variable |varId| can only be access in
+ // |func|. For example, a function scope variable, or a private variable
+ // where |func| is an entry point with no function calls.
+ bool IsLocalVar(uint32_t varId, Function* func);
// Return true if |inst| is marked live.
bool IsLive(const Instruction* inst) const {
return live_insts_.Get(inst->unique_id());
}
- // Returns true if |inst| is dead.
- bool IsDead(Instruction* inst);
-
// Adds entry points, execution modes and workgroup size decorations to the
// worklist for processing with the first function.
void InitializeModuleScopeLiveInstructions();
@@ -101,18 +106,6 @@ class AggressiveDCEPass : public MemPass {
// If |varId| is local, mark all stores of varId as live.
void ProcessLoad(Function* func, uint32_t varId);
- // If |bp| is structured header block, returns true and sets |mergeInst| to
- // the merge instruction, |branchInst| to the branch and |mergeBlockId| to the
- // merge block if they are not nullptr. Any of |mergeInst|, |branchInst| or
- // |mergeBlockId| may be a null pointer. Returns false if |bp| is a null
- // pointer.
- bool IsStructuredHeader(BasicBlock* bp, Instruction** mergeInst,
- Instruction** branchInst, uint32_t* mergeBlockId);
-
- // Initialize block2headerBranch_, header2nextHeaderBranch_, and
- // branch2merge_ using |structuredOrder| to order blocks.
- void ComputeBlock2HeaderMaps(std::list<BasicBlock*>& structuredOrder);
-
// Add branch to |labelId| to end of block |bp|.
void AddBranch(uint32_t labelId, BasicBlock* bp);
@@ -140,14 +133,100 @@ class AggressiveDCEPass : public MemPass {
Pass::Status ProcessImpl();
- // True if current function has a call instruction contained in it
- bool call_in_func_;
+ // Adds instructions which must be kept because of they have side-effects
+ // that ADCE cannot model to the work list.
+ void InitializeWorkList(Function* func,
+ std::list<BasicBlock*>& structured_order);
+
+ // Process each instruction in the work list by marking any instruction that
+ // that it depends on as live, and adding it to the work list. The work list
+ // will be empty at the end.
+ void ProcessWorkList(Function* func);
+
+ // Kills any instructions in |func| that have not been marked as live.
+ bool KillDeadInstructions(const Function* func,
+ std::list<BasicBlock*>& structured_order);
+
+ // Adds the instructions that define the operands of |inst| to the work list.
+ void AddOperandsToWorkList(const Instruction* inst);
+
+ // Marks all of the labels and branch that inst requires as live.
+ void MarkBlockAsLive(Instruction* inst);
+
+ // Marks any variables from which |inst| may require data as live.
+ void MarkLoadedVariablesAsLive(Function* func, Instruction* inst);
+
+ // Returns the id of the variable that |ptr_id| point to. |ptr_id| must be a
+ // value whose type is a pointer.
+ uint32_t GetVariableId(uint32_t ptr_id);
+
+ // Returns all of the ids for the variables from which |inst| will load data.
+ std::vector<uint32_t> GetLoadedVariables(Instruction* inst);
+
+ // Returns all of the ids for the variables from which |inst| will load data.
+ // The opcode of |inst| must be OpFunctionCall.
+ std::vector<uint32_t> GetLoadedVariablesFromFunctionCall(
+ const Instruction* inst);
+
+ // Returns the id of the variable from which |inst| will load data. |inst|
+ // must not be an OpFunctionCall. Returns 0 if no data is read or the
+ // variable cannot be determined. Note that in logical addressing mode the
+ // latter is not possible for function and private storage class because there
+ // cannot be variable pointers pointing to those storage classes.
+ uint32_t GetLoadedVariableFromNonFunctionCalls(Instruction* inst);
+
+ // Adds all decorations of |inst| to the work list.
+ void AddDecorationsToWorkList(const Instruction* inst);
+
+ // Adds all debug instruction associated with |inst| to the work list.
+ void AddDebugInstructionsToWorkList(const Instruction* inst);
- // True if current function is an entry point
- bool func_is_entry_point_;
+ // Marks all of the OpFunctionParameter instructions in |func| as live.
+ void MarkFunctionParameterAsLive(const Function* func);
- // True if current function is entry point and has no function calls.
- bool private_like_local_;
+ // Returns the terminator instruction in the header for the innermost
+ // construct that contains |blk|. Returns nullptr if no such header exists.
+ Instruction* GetHeaderBranch(BasicBlock* blk);
+
+ // Returns the header for the innermost construct that contains |blk|. A loop
+ // header will be its own header. Returns nullptr if no such header exists.
+ BasicBlock* GetHeaderBlock(BasicBlock* blk) const;
+
+ // Returns the same as |GetHeaderBlock| except if |blk| is a loop header it
+ // will return the header of the next enclosing construct. Returns nullptr if
+ // no such header exists.
+ Instruction* GetBranchForNextHeader(BasicBlock* blk);
+
+ // Returns the merge instruction in the same basic block as |inst|. Returns
+ // nullptr if one does not exist.
+ Instruction* GetMergeInstruction(Instruction* inst);
+
+ // Returns true if |bb| is in the construct with header |header_block|.
+ bool BlockIsInConstruct(BasicBlock* header_block, BasicBlock* bb);
+
+ // Returns true if |func| is an entry point that does not have any function
+ // calls.
+ bool IsEntryPointWithNoCalls(Function* func);
+
+ // Returns true if |func| is an entry point.
+ bool IsEntryPoint(Function* func);
+
+ // Returns true if |func| contains a function call.
+ bool HasCall(Function* func);
+
+ // Marks the first block, which is the entry block, in |func| as live.
+ void MarkFirstBlockAsLive(Function* func);
+
+ // Adds an OpUnreachable instruction at the end of |block|.
+ void AddUnreachable(BasicBlock*& block);
+
+ // Marks the OpLoopMerge and the terminator in |basic_block| as live if
+ // |basic_block| is a loop header.
+ void MarkLoopConstructAsLiveIfLoopHeader(BasicBlock* basic_block);
+
+ // The cached results for |IsEntryPointWithNoCalls|. It maps the function's
+ // result id to the return value.
+ std::unordered_map<uint32_t, bool> entry_point_with_no_calls_cache_;
// Live Instruction Worklist. An instruction is added to this list
// if it might have a side effect, either directly or indirectly.
@@ -156,27 +235,6 @@ class AggressiveDCEPass : public MemPass {
// building up the live instructions set |live_insts_|.
std::queue<Instruction*> worklist_;
- // Map from block to the branch instruction in the header of the most
- // immediate controlling structured if or loop. A loop header block points
- // to its own branch instruction. An if-selection block points to the branch
- // of an enclosing construct's header, if one exists.
- std::unordered_map<BasicBlock*, Instruction*> block2headerBranch_;
-
- // Map from header block to the branch instruction in the header of the
- // structured construct enclosing it.
- // The liveness algorithm is designed to iteratively mark as live all
- // structured constructs enclosing a live instruction.
- std::unordered_map<BasicBlock*, Instruction*> header2nextHeaderBranch_;
-
- // Maps basic block to their index in the structured order traversal.
- std::unordered_map<BasicBlock*, uint32_t> structured_order_index_;
-
- // Map from branch to its associated merge instruction, if any
- std::unordered_map<Instruction*, Instruction*> branch2merge_;
-
- // Store instructions to variables of private storage
- std::vector<Instruction*> private_stores_;
-
// Live Instructions
utils::BitVector live_insts_;
diff --git a/source/opt/amd_ext_to_khr.cpp b/source/opt/amd_ext_to_khr.cpp
index ccedc0bc..dd9bafda 100644
--- a/source/opt/amd_ext_to_khr.cpp
+++ b/source/opt/amd_ext_to_khr.cpp
@@ -584,9 +584,9 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
}
// Get the constants that will be used.
- uint32_t f0_const_id = const_mgr->GetFloatConst(0.0);
- uint32_t f2_const_id = const_mgr->GetFloatConst(2.0);
- uint32_t f0_5_const_id = const_mgr->GetFloatConst(0.5);
+ uint32_t f0_const_id = const_mgr->GetFloatConstId(0.0);
+ uint32_t f2_const_id = const_mgr->GetFloatConstId(2.0);
+ uint32_t f0_5_const_id = const_mgr->GetFloatConstId(0.5);
const analysis::Constant* vec_const =
const_mgr->GetConstant(v2_float_type, {f0_5_const_id, f0_5_const_id});
uint32_t vec_const_id =
@@ -731,12 +731,12 @@ bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst,
}
// Get the constants that will be used.
- uint32_t f0_const_id = const_mgr->GetFloatConst(0.0);
- uint32_t f1_const_id = const_mgr->GetFloatConst(1.0);
- uint32_t f2_const_id = const_mgr->GetFloatConst(2.0);
- uint32_t f3_const_id = const_mgr->GetFloatConst(3.0);
- uint32_t f4_const_id = const_mgr->GetFloatConst(4.0);
- uint32_t f5_const_id = const_mgr->GetFloatConst(5.0);
+ uint32_t f0_const_id = const_mgr->GetFloatConstId(0.0);
+ uint32_t f1_const_id = const_mgr->GetFloatConstId(1.0);
+ uint32_t f2_const_id = const_mgr->GetFloatConstId(2.0);
+ uint32_t f3_const_id = const_mgr->GetFloatConstId(3.0);
+ uint32_t f4_const_id = const_mgr->GetFloatConstId(4.0);
+ uint32_t f5_const_id = const_mgr->GetFloatConstId(5.0);
// Extract the input values.
Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0});
@@ -935,8 +935,7 @@ Pass::Status AmdExtensionToKhrPass::Process() {
std::vector<Instruction*> to_be_killed;
for (Instruction& inst : context()->module()->extensions()) {
if (inst.opcode() == SpvOpExtension) {
- if (ext_to_remove.count(reinterpret_cast<const char*>(
- &(inst.GetInOperand(0).words[0]))) != 0) {
+ if (ext_to_remove.count(inst.GetInOperand(0).AsString()) != 0) {
to_be_killed.push_back(&inst);
}
}
@@ -944,8 +943,7 @@ Pass::Status AmdExtensionToKhrPass::Process() {
for (Instruction& inst : context()->ext_inst_imports()) {
if (inst.opcode() == SpvOpExtInstImport) {
- if (ext_to_remove.count(reinterpret_cast<const char*>(
- &(inst.GetInOperand(0).words[0]))) != 0) {
+ if (ext_to_remove.count(inst.GetInOperand(0).AsString()) != 0) {
to_be_killed.push_back(&inst);
}
}
diff --git a/source/opt/amd_ext_to_khr.h b/source/opt/amd_ext_to_khr.h
index fd3dab4e..6a39d953 100644
--- a/source/opt/amd_ext_to_khr.h
+++ b/source/opt/amd_ext_to_khr.h
@@ -23,7 +23,7 @@ namespace spvtools {
namespace opt {
// Replaces the extensions VK_AMD_shader_ballot, VK_AMD_gcn_shader, and
-// VK_AMD_shader_trinary_minmax with equivalant code using core instructions and
+// VK_AMD_shader_trinary_minmax with equivalent code using core instructions and
// capabilities.
class AmdExtensionToKhrPass : public Pass {
public:
diff --git a/source/opt/basic_block.h b/source/opt/basic_block.h
index 6741a50f..dd3b2e28 100644
--- a/source/opt/basic_block.h
+++ b/source/opt/basic_block.h
@@ -83,7 +83,7 @@ class BasicBlock {
const Instruction* GetMergeInst() const;
Instruction* GetMergeInst();
- // Returns the OpLoopMerge instruciton in this basic block, if it exists.
+ // Returns the OpLoopMerge instruction in this basic block, if it exists.
// Otherwise return null. May be used whenever tail() can be used.
const Instruction* GetLoopMergeInst() const;
Instruction* GetLoopMergeInst();
diff --git a/source/opt/block_merge_pass.cpp b/source/opt/block_merge_pass.cpp
index c7315baf..ef7f31fe 100644
--- a/source/opt/block_merge_pass.cpp
+++ b/source/opt/block_merge_pass.cpp
@@ -28,7 +28,9 @@ namespace opt {
bool BlockMergePass::MergeBlocks(Function* func) {
bool modified = false;
for (auto bi = func->begin(); bi != func->end();) {
- if (blockmergeutil::CanMergeWithSuccessor(context(), &*bi)) {
+ // Don't bother trying to merge unreachable blocks.
+ if (context()->IsReachable(*bi) &&
+ blockmergeutil::CanMergeWithSuccessor(context(), &*bi)) {
blockmergeutil::MergeWithSuccessor(context(), func, bi);
// Reprocess block.
modified = true;
@@ -42,7 +44,7 @@ bool BlockMergePass::MergeBlocks(Function* func) {
Pass::Status BlockMergePass::Process() {
// Process all entry point functions.
ProcessFunction pfn = [this](Function* fp) { return MergeBlocks(fp); };
- bool modified = context()->ProcessEntryPointCallTree(pfn);
+ bool modified = context()->ProcessReachableCallTree(pfn);
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
diff --git a/source/opt/block_merge_util.cpp b/source/opt/block_merge_util.cpp
index 14b5d364..8ae8020a 100644
--- a/source/opt/block_merge_util.cpp
+++ b/source/opt/block_merge_util.cpp
@@ -103,11 +103,6 @@ bool CanMergeWithSuccessor(IRContext* context, BasicBlock* block) {
return false;
}
- // Don't bother trying to merge unreachable blocks.
- if (auto dominators = context->GetDominatorAnalysis(block->GetParent())) {
- if (!dominators->IsReachable(block)) return false;
- }
-
Instruction* merge_inst = block->GetMergeInst();
const bool pred_is_header = IsHeader(block);
if (pred_is_header && lab_id != merge_inst->GetSingleWordInOperand(0u)) {
@@ -176,10 +171,17 @@ void MergeWithSuccessor(IRContext* context, Function* func,
// and OpBranchConditional.
auto terminator = bi->terminator();
auto& vec = terminator->dbg_line_insts();
- auto& new_vec = merge_inst->dbg_line_insts();
- new_vec.insert(new_vec.end(), vec.begin(), vec.end());
- terminator->clear_dbg_line_insts();
-
+ if (vec.size() > 0) {
+ merge_inst->ClearDbgLineInsts();
+ auto& new_vec = merge_inst->dbg_line_insts();
+ new_vec.insert(new_vec.end(), vec.begin(), vec.end());
+ terminator->ClearDbgLineInsts();
+ for (auto& l_inst : new_vec)
+ context->get_def_use_mgr()->AnalyzeInstDefUse(&l_inst);
+ }
+ // Clear debug scope of terminator to avoid DebugScope
+ // emitted between terminator and merge.
+ terminator->SetDebugScope(DebugScope(kNoDebugScope, kNoInlinedAt));
// Move the merge instruction to just before the terminator.
merge_inst->InsertBefore(terminator);
}
diff --git a/source/opt/ccp_pass.cpp b/source/opt/ccp_pass.cpp
index d84f13f5..5099b477 100644
--- a/source/opt/ccp_pass.cpp
+++ b/source/opt/ccp_pass.cpp
@@ -102,6 +102,34 @@ SSAPropagator::PropStatus CCPPass::VisitPhi(Instruction* phi) {
return SSAPropagator::kInteresting;
}
+uint32_t CCPPass::ComputeLatticeMeet(Instruction* instr, uint32_t val2) {
+ // Given two values val1 and val2, the meet operation in the constant
+ // lattice uses the following rules:
+ //
+ // meet(val1, UNDEFINED) = val1
+ // meet(val1, VARYING) = VARYING
+ // meet(val1, val2) = val1 if val1 == val2
+ // meet(val1, val2) = VARYING if val1 != val2
+ //
+ // When two different values meet, the result is always varying because CCP
+ // does not allow lateral transitions in the lattice. This prevents
+ // infinite cycles during propagation.
+ auto val1_it = values_.find(instr->result_id());
+ if (val1_it == values_.end()) {
+ return val2;
+ }
+
+ uint32_t val1 = val1_it->second;
+ if (IsVaryingValue(val1)) {
+ return val1;
+ } else if (IsVaryingValue(val2)) {
+ return val2;
+ } else if (val1 != val2) {
+ return kVaryingSSAId;
+ }
+ return val2;
+}
+
SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) {
assert(instr->result_id() != 0 &&
"Expecting an instruction that produces a result");
@@ -115,8 +143,10 @@ SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) {
if (IsVaryingValue(it->second)) {
return MarkInstructionVarying(instr);
} else {
- values_[instr->result_id()] = it->second;
- return SSAPropagator::kInteresting;
+ uint32_t new_val = ComputeLatticeMeet(instr, it->second);
+ values_[instr->result_id()] = new_val;
+ return IsVaryingValue(new_val) ? SSAPropagator::kVarying
+ : SSAPropagator::kInteresting;
}
}
return SSAPropagator::kNotInteresting;
@@ -142,9 +172,12 @@ 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() && "CCP is only interested in constant.");
- values_[instr->result_id()] = folded_inst->result_id();
- return SSAPropagator::kInteresting;
+ assert(folded_inst->IsConstant() &&
+ "CCP is only interested in constant values.");
+ uint32_t new_val = ComputeLatticeMeet(instr, folded_inst->result_id());
+ values_[instr->result_id()] = new_val;
+ return IsVaryingValue(new_val) ? SSAPropagator::kVarying
+ : SSAPropagator::kInteresting;
}
// Conservatively mark this instruction as varying if any input id is varying.
@@ -291,6 +324,10 @@ bool CCPPass::ReplaceValues() {
}
bool CCPPass::PropagateConstants(Function* fp) {
+ if (fp->IsDeclaration()) {
+ return false;
+ }
+
// Mark function parameters as varying.
fp->ForEachParam([this](const Instruction* inst) {
values_[inst->result_id()] = kVaryingSSAId;
diff --git a/source/opt/ccp_pass.h b/source/opt/ccp_pass.h
index fb20c780..77ea9f80 100644
--- a/source/opt/ccp_pass.h
+++ b/source/opt/ccp_pass.h
@@ -92,6 +92,22 @@ class CCPPass : public MemPass {
// generated during propagation.
analysis::ConstantManager* const_mgr_;
+ // Returns a new value for |instr| by computing the meet operation between
+ // its existing value and |val2|.
+ //
+ // Given two values val1 and val2, the meet operation in the constant
+ // lattice uses the following rules:
+ //
+ // meet(val1, UNDEFINED) = val1
+ // meet(val1, VARYING) = VARYING
+ // meet(val1, val2) = val1 if val1 == val2
+ // meet(val1, val2) = VARYING if val1 != val2
+ //
+ // When two different values meet, the result is always varying because CCP
+ // does not allow lateral transitions in the lattice. This prevents
+ // infinite cycles during propagation.
+ uint32_t ComputeLatticeMeet(Instruction* instr, uint32_t val2);
+
// Constant value table. Each entry <id, const_decl_id> in this map
// represents the compile-time constant value for |id| as declared by
// |const_decl_id|. Each |const_decl_id| in this table is an OpConstant
diff --git a/source/opt/cfg.h b/source/opt/cfg.h
index f2806822..33412f18 100644
--- a/source/opt/cfg.h
+++ b/source/opt/cfg.h
@@ -30,7 +30,7 @@ class CFG {
public:
explicit CFG(Module* module);
- // Return the list of predecesors for basic block with label |blkid|.
+ // Return the list of predecessors for basic block with label |blkid|.
// TODO(dnovillo): Move this to BasicBlock.
const std::vector<uint32_t>& preds(uint32_t blk_id) const {
assert(label2preds_.count(blk_id));
diff --git a/source/opt/combine_access_chains.cpp b/source/opt/combine_access_chains.cpp
index facfc24b..142897a2 100644
--- a/source/opt/combine_access_chains.cpp
+++ b/source/opt/combine_access_chains.cpp
@@ -34,6 +34,10 @@ Pass::Status CombineAccessChains::Process() {
}
bool CombineAccessChains::ProcessFunction(Function& function) {
+ if (function.IsDeclaration()) {
+ return false;
+ }
+
bool modified = false;
cfg()->ForEachBlockInReversePostOrder(
diff --git a/source/opt/compact_ids_pass.cpp b/source/opt/compact_ids_pass.cpp
index 67091531..8815b8c6 100644
--- a/source/opt/compact_ids_pass.cpp
+++ b/source/opt/compact_ids_pass.cpp
@@ -86,9 +86,12 @@ Pass::Status CompactIdsPass::Process() {
},
true);
- if (modified)
+ if (modified) {
context()->module()->SetIdBound(
static_cast<uint32_t>(result_id_mapping.size() + 1));
+ // There are ids in the feature manager that could now be invalid
+ context()->ResetFeatureManager();
+ }
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
diff --git a/source/opt/const_folding_rules.cpp b/source/opt/const_folding_rules.cpp
index d262a7ec..249e11e5 100644
--- a/source/opt/const_folding_rules.cpp
+++ b/source/opt/const_folding_rules.cpp
@@ -22,6 +22,45 @@ namespace {
const uint32_t kExtractCompositeIdInIdx = 0;
+// Returns a constants with the value NaN of the given type. Only works for
+// 32-bit and 64-bit float point types. Returns |nullptr| if an error occurs.
+const analysis::Constant* GetNan(const analysis::Type* type,
+ analysis::ConstantManager* const_mgr) {
+ const analysis::Float* float_type = type->AsFloat();
+ if (float_type == nullptr) {
+ return nullptr;
+ }
+
+ switch (float_type->width()) {
+ case 32:
+ return const_mgr->GetFloatConst(std::numeric_limits<float>::quiet_NaN());
+ case 64:
+ return const_mgr->GetDoubleConst(
+ std::numeric_limits<double>::quiet_NaN());
+ default:
+ return nullptr;
+ }
+}
+
+// Returns a constants with the value INF of the given type. Only works for
+// 32-bit and 64-bit float point types. Returns |nullptr| if an error occurs.
+const analysis::Constant* GetInf(const analysis::Type* type,
+ analysis::ConstantManager* const_mgr) {
+ const analysis::Float* float_type = type->AsFloat();
+ if (float_type == nullptr) {
+ return nullptr;
+ }
+
+ switch (float_type->width()) {
+ case 32:
+ return const_mgr->GetFloatConst(std::numeric_limits<float>::infinity());
+ case 64:
+ return const_mgr->GetDoubleConst(std::numeric_limits<double>::infinity());
+ default:
+ return nullptr;
+ }
+}
+
// Returns true if |type| is Float or a vector of Float.
bool HasFloatingPoint(const analysis::Type* type) {
if (type->AsFloat()) {
@@ -33,6 +72,23 @@ bool HasFloatingPoint(const analysis::Type* type) {
return false;
}
+// Returns a constants with the value |-val| of the given type. Only works for
+// 32-bit and 64-bit float point types. Returns |nullptr| if an error occurs.
+const analysis::Constant* negateFPConst(const analysis::Type* result_type,
+ const analysis::Constant* val,
+ analysis::ConstantManager* const_mgr) {
+ const analysis::Float* float_type = result_type->AsFloat();
+ assert(float_type != nullptr);
+ if (float_type->width() == 32) {
+ float fa = val->GetFloat();
+ return const_mgr->GetFloatConst(-fa);
+ } else if (float_type->width() == 64) {
+ double da = val->GetDouble();
+ return const_mgr->GetDoubleConst(-da);
+ }
+ return nullptr;
+}
+
// Folds an OpcompositeExtract where input is a composite constant.
ConstantFoldingRule FoldExtractWithConstants() {
return [](IRContext* context, Instruction* inst,
@@ -492,7 +548,60 @@ ConstantFoldingRule FoldQuantizeToF16() {
ConstantFoldingRule FoldFSub() { return FoldFPBinaryOp(FOLD_FPARITH_OP(-)); }
ConstantFoldingRule FoldFAdd() { return FoldFPBinaryOp(FOLD_FPARITH_OP(+)); }
ConstantFoldingRule FoldFMul() { return FoldFPBinaryOp(FOLD_FPARITH_OP(*)); }
-ConstantFoldingRule FoldFDiv() { return FoldFPBinaryOp(FOLD_FPARITH_OP(/)); }
+
+// Returns the constant that results from evaluating |numerator| / 0.0. Returns
+// |nullptr| if the result could not be evaluated.
+const analysis::Constant* FoldFPScalarDivideByZero(
+ const analysis::Type* result_type, const analysis::Constant* numerator,
+ analysis::ConstantManager* const_mgr) {
+ if (numerator == nullptr) {
+ return nullptr;
+ }
+
+ if (numerator->IsZero()) {
+ return GetNan(result_type, const_mgr);
+ }
+
+ const analysis::Constant* result = GetInf(result_type, const_mgr);
+ if (result == nullptr) {
+ return nullptr;
+ }
+
+ if (numerator->AsFloatConstant()->GetValueAsDouble() < 0.0) {
+ result = negateFPConst(result_type, result, const_mgr);
+ }
+ return result;
+}
+
+// Returns the result of folding |numerator| / |denominator|. Returns |nullptr|
+// if it cannot be folded.
+const analysis::Constant* FoldScalarFPDivide(
+ const analysis::Type* result_type, const analysis::Constant* numerator,
+ const analysis::Constant* denominator,
+ analysis::ConstantManager* const_mgr) {
+ if (denominator == nullptr) {
+ return nullptr;
+ }
+
+ if (denominator->IsZero()) {
+ return FoldFPScalarDivideByZero(result_type, numerator, const_mgr);
+ }
+
+ const analysis::FloatConstant* denominator_float =
+ denominator->AsFloatConstant();
+ if (denominator_float && denominator->GetValueAsDouble() == -0.0) {
+ const analysis::Constant* result =
+ FoldFPScalarDivideByZero(result_type, numerator, const_mgr);
+ if (result != nullptr)
+ result = negateFPConst(result_type, result, const_mgr);
+ return result;
+ } else {
+ return FOLD_FPARITH_OP(/)(result_type, numerator, denominator, const_mgr);
+ }
+}
+
+// Returns the constant folding rule to fold |OpFDiv| with two constants.
+ConstantFoldingRule FoldFDiv() { return FoldFPBinaryOp(FoldScalarFPDivide); }
bool CompareFloatingPoint(bool op_result, bool op_unordered,
bool need_ordered) {
@@ -655,20 +764,7 @@ UnaryScalarFoldingRule FoldFNegateOp() {
analysis::ConstantManager* const_mgr) -> const analysis::Constant* {
assert(result_type != nullptr && a != nullptr);
assert(result_type == a->type());
- const analysis::Float* float_type = result_type->AsFloat();
- assert(float_type != nullptr);
- if (float_type->width() == 32) {
- float fa = a->GetFloat();
- utils::FloatProxy<float> result(-fa);
- std::vector<uint32_t> words = result.GetWords();
- return const_mgr->GetConstant(result_type, words);
- } else if (float_type->width() == 64) {
- double da = a->GetDouble();
- utils::FloatProxy<double> result(-da);
- std::vector<uint32_t> words = result.GetWords();
- return const_mgr->GetConstant(result_type, words);
- }
- return nullptr;
+ return negateFPConst(result_type, a, const_mgr);
};
}
@@ -1002,7 +1098,7 @@ const analysis::Constant* FoldClamp1(
"Expecting a GLSLstd450 extended instruction.");
// Make sure all Clamp operands are constants.
- for (uint32_t i = 1; i < 3; i++) {
+ for (uint32_t i = 1; i < 4; i++) {
if (constants[i] == nullptr) {
return nullptr;
}
@@ -1017,7 +1113,7 @@ const analysis::Constant* FoldClamp1(
context);
}
-// Fold a clamp instruction when |x >= min_val|.
+// Fold a clamp instruction when |x <= min_val|.
const analysis::Constant* FoldClamp2(
IRContext* context, Instruction* inst,
const std::vector<const analysis::Constant*>& constants) {
@@ -1250,7 +1346,7 @@ void ConstantFoldingRules::AddFoldingRules() {
FoldFPUnaryOp(FoldFTranscendentalUnary(std::log)));
#ifdef __ANDROID__
- // Android NDK r15c tageting ABI 15 doesn't have full support for C++11
+ // Android NDK r15c targeting ABI 15 doesn't have full support for C++11
// (no std::exp2/log2). ::exp2 is available from C99 but ::log2 isn't
// available up until ABI 18 so we use a shim
auto log2_shim = [](double v) -> double { return log(v) / log(2.0); };
diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp
index 19ca6008..d286cd26 100644
--- a/source/opt/constants.cpp
+++ b/source/opt/constants.cpp
@@ -217,7 +217,8 @@ Instruction* ConstantManager::BuildInstructionAndAddToModule(
auto* new_inst_ptr = new_inst.get();
*pos = pos->InsertBefore(std::move(new_inst));
++(*pos);
- context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inst_ptr);
+ if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inst_ptr);
MapConstantToInst(new_const, new_inst_ptr);
return new_inst_ptr;
}
@@ -419,19 +420,42 @@ const Constant* ConstantManager::GetNumericVectorConstantWithWords(
return GetConstant(type, element_ids);
}
-uint32_t ConstantManager::GetFloatConst(float val) {
+uint32_t ConstantManager::GetFloatConstId(float val) {
+ const Constant* c = GetFloatConst(val);
+ return GetDefiningInstruction(c)->result_id();
+}
+
+const Constant* ConstantManager::GetFloatConst(float val) {
Type* float_type = context()->get_type_mgr()->GetFloatType();
utils::FloatProxy<float> v(val);
const Constant* c = GetConstant(float_type, v.GetWords());
+ return c;
+}
+
+uint32_t ConstantManager::GetDoubleConstId(double val) {
+ const Constant* c = GetDoubleConst(val);
return GetDefiningInstruction(c)->result_id();
}
+const Constant* ConstantManager::GetDoubleConst(double val) {
+ Type* float_type = context()->get_type_mgr()->GetDoubleType();
+ utils::FloatProxy<double> v(val);
+ const Constant* c = GetConstant(float_type, v.GetWords());
+ return c;
+}
+
uint32_t ConstantManager::GetSIntConst(int32_t val) {
Type* sint_type = context()->get_type_mgr()->GetSIntType();
const Constant* c = GetConstant(sint_type, {static_cast<uint32_t>(val)});
return GetDefiningInstruction(c)->result_id();
}
+uint32_t ConstantManager::GetUIntConst(uint32_t val) {
+ Type* uint_type = context()->get_type_mgr()->GetUIntType();
+ const Constant* c = GetConstant(uint_type, {val});
+ return GetDefiningInstruction(c)->result_id();
+}
+
std::vector<const analysis::Constant*> Constant::GetVectorComponents(
analysis::ConstantManager* const_mgr) const {
std::vector<const analysis::Constant*> components;
diff --git a/source/opt/constants.h b/source/opt/constants.h
index 95d984fc..c039ae08 100644
--- a/source/opt/constants.h
+++ b/source/opt/constants.h
@@ -541,7 +541,7 @@ class ConstantManager {
// instruction at the end of the current module's types section.
//
// |type_id| is an optional argument for disambiguating equivalent types. If
- // |type_id| is specified, the contant returned will have that type id.
+ // |type_id| is specified, the constant returned will have that type id.
Instruction* GetDefiningInstruction(const Constant* c, uint32_t type_id = 0,
Module::inst_iterator* pos = nullptr);
@@ -637,11 +637,23 @@ class ConstantManager {
}
// Returns the id of a 32-bit floating point constant with value |val|.
- uint32_t GetFloatConst(float val);
+ uint32_t GetFloatConstId(float val);
+
+ // Returns a 32-bit float constant with the given value.
+ const Constant* GetFloatConst(float val);
+
+ // Returns the id of a 64-bit floating point constant with value |val|.
+ uint32_t GetDoubleConstId(double val);
+
+ // Returns a 64-bit float constant with the given value.
+ const Constant* GetDoubleConst(double val);
// Returns the id of a 32-bit signed integer constant with value |val|.
uint32_t GetSIntConst(int32_t val);
+ // Returns the id of a 32-bit unsigned integer constant with value |val|.
+ uint32_t GetUIntConst(uint32_t val);
+
private:
// Creates a Constant instance with the given type and a vector of constant
// defining words. Returns a unique pointer to the created Constant instance
diff --git a/source/opt/control_dependence.cpp b/source/opt/control_dependence.cpp
new file mode 100644
index 00000000..f4879e0f
--- /dev/null
+++ b/source/opt/control_dependence.cpp
@@ -0,0 +1,156 @@
+// Copyright (c) 2021 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/opt/control_dependence.h"
+
+#include <cassert>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "source/opt/basic_block.h"
+#include "source/opt/cfg.h"
+#include "source/opt/dominator_analysis.h"
+#include "source/opt/function.h"
+#include "source/opt/instruction.h"
+#include "spirv/unified1/spirv.h"
+
+// Computes the control dependence graph (CDG) using the algorithm in Cytron
+// 1991, "Efficiently Computing Static Single Assignment Form and the Control
+// Dependence Graph." It relies on the fact that the control dependence sources
+// (blocks on which a block is control dependent) are exactly the post-dominance
+// frontier for that block. The explanation and proofs are given in Section 6 of
+// that paper.
+// Link: https://www.cs.utexas.edu/~pingali/CS380C/2010/papers/ssaCytron.pdf
+//
+// The algorithm in Section 4.2 of the same paper is used to construct the
+// dominance frontier. It uses the post-dominance tree, which is available in
+// the IR context.
+
+namespace spvtools {
+namespace opt {
+constexpr uint32_t ControlDependenceAnalysis::kPseudoEntryBlock;
+
+uint32_t ControlDependence::GetConditionID(const CFG& cfg) const {
+ if (source_bb_id() == 0) {
+ // Entry dependence; return 0.
+ return 0;
+ }
+ const BasicBlock* source_bb = cfg.block(source_bb_id());
+ const Instruction* branch = source_bb->terminator();
+ assert((branch->opcode() == SpvOpBranchConditional ||
+ branch->opcode() == SpvOpSwitch) &&
+ "invalid control dependence; last instruction must be conditional "
+ "branch or switch");
+ return branch->GetSingleWordInOperand(0);
+}
+
+bool ControlDependence::operator<(const ControlDependence& other) const {
+ return std::tie(source_bb_id_, target_bb_id_, branch_target_bb_id_) <
+ std::tie(other.source_bb_id_, other.target_bb_id_,
+ other.branch_target_bb_id_);
+}
+
+bool ControlDependence::operator==(const ControlDependence& other) const {
+ return std::tie(source_bb_id_, target_bb_id_, branch_target_bb_id_) ==
+ std::tie(other.source_bb_id_, other.target_bb_id_,
+ other.branch_target_bb_id_);
+}
+
+std::ostream& operator<<(std::ostream& os, const ControlDependence& dep) {
+ os << dep.source_bb_id() << "->" << dep.target_bb_id();
+ if (dep.branch_target_bb_id() != dep.target_bb_id()) {
+ os << " through " << dep.branch_target_bb_id();
+ }
+ return os;
+}
+
+void ControlDependenceAnalysis::ComputePostDominanceFrontiers(
+ const CFG& cfg, const PostDominatorAnalysis& pdom) {
+ // Compute post-dominance frontiers (reverse graph).
+ // The dominance frontier for a block X is equal to (Equation 4)
+ // DF_local(X) U { B in DF_up(Z) | X = ipdom(Z) }
+ // (ipdom(Z) is the immediate post-dominator of Z.)
+ // where
+ // DF_local(X) = { Y | X -> Y in CFG, X does not strictly post-dominate Y }
+ // represents the contribution of X's predecessors to the DF, and
+ // DF_up(Z) = { Y | Y in DF(Z), ipdom(Z) does not strictly post-dominate Y }
+ // (note: ipdom(Z) = X.)
+ // represents the contribution of a block to its immediate post-
+ // dominator's DF.
+ // This is computed in one pass through a post-order traversal of the
+ // post-dominator tree.
+
+ // Assert that there is a block other than the pseudo exit in the pdom tree,
+ // as we need one to get the function entry point (as the pseudo exit is not
+ // actually part of the function.)
+ assert(!cfg.IsPseudoExitBlock(pdom.GetDomTree().post_begin()->bb_));
+ Function* function = pdom.GetDomTree().post_begin()->bb_->GetParent();
+ uint32_t function_entry = function->entry()->id();
+ // Explicitly initialize pseudo-entry block, as it doesn't depend on anything,
+ // so it won't be initialized in the following loop.
+ reverse_nodes_[kPseudoEntryBlock] = {};
+ for (auto it = pdom.GetDomTree().post_cbegin();
+ it != pdom.GetDomTree().post_cend(); ++it) {
+ ComputePostDominanceFrontierForNode(cfg, pdom, function_entry, *it);
+ }
+}
+
+void ControlDependenceAnalysis::ComputePostDominanceFrontierForNode(
+ const CFG& cfg, const PostDominatorAnalysis& pdom, uint32_t function_entry,
+ const DominatorTreeNode& pdom_node) {
+ const uint32_t label = pdom_node.id();
+ ControlDependenceList& edges = reverse_nodes_[label];
+ for (uint32_t pred : cfg.preds(label)) {
+ if (!pdom.StrictlyDominates(label, pred)) {
+ edges.push_back(ControlDependence(pred, label));
+ }
+ }
+ if (label == function_entry) {
+ // Add edge from pseudo-entry to entry.
+ // In CDG construction, an edge is added from entry to exit, so only the
+ // exit node can post-dominate entry.
+ edges.push_back(ControlDependence(kPseudoEntryBlock, label));
+ }
+ for (DominatorTreeNode* child : pdom_node) {
+ // Note: iterate dependences by value, as we need a copy.
+ for (const ControlDependence& dep : reverse_nodes_[child->id()]) {
+ // Special-case pseudo-entry, as above.
+ if (dep.source_bb_id() == kPseudoEntryBlock ||
+ !pdom.StrictlyDominates(label, dep.source_bb_id())) {
+ edges.push_back(ControlDependence(dep.source_bb_id(), label,
+ dep.branch_target_bb_id()));
+ }
+ }
+ }
+}
+
+void ControlDependenceAnalysis::ComputeControlDependenceGraph(
+ const CFG& cfg, const PostDominatorAnalysis& pdom) {
+ ComputePostDominanceFrontiers(cfg, pdom);
+ ComputeForwardGraphFromReverse();
+}
+
+void ControlDependenceAnalysis::ComputeForwardGraphFromReverse() {
+ for (const auto& entry : reverse_nodes_) {
+ // Ensure an entry is created for each node.
+ forward_nodes_[entry.first];
+ for (const ControlDependence& dep : entry.second) {
+ forward_nodes_[dep.source_bb_id()].push_back(dep);
+ }
+ }
+}
+
+} // namespace opt
+} // namespace spvtools
diff --git a/source/opt/control_dependence.h b/source/opt/control_dependence.h
new file mode 100644
index 00000000..993f3793
--- /dev/null
+++ b/source/opt/control_dependence.h
@@ -0,0 +1,197 @@
+// Copyright (c) 2021 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_OPT_CONTROL_DEPENDENCE_H_
+#define SOURCE_OPT_CONTROL_DEPENDENCE_H_
+
+#include <algorithm>
+#include <cstdint>
+#include <functional>
+#include <ostream>
+#include <unordered_map>
+#include <vector>
+
+#include "source/opt/cfg.h"
+#include "source/opt/dominator_analysis.h"
+
+namespace spvtools {
+namespace opt {
+
+class ControlDependence {
+ public:
+ // The label of the source of this dependence, i.e. the block on which the
+ // target is dependent on.
+ // A |source_bb_id| of 0 represents an "entry" dependence, meaning that the
+ // execution of |target_bb_id| is only dependent on entry to the function.
+ uint32_t source_bb_id() const { return source_bb_id_; }
+ // The label of the target of this dependence, i.e. the block which is
+ // dependent on the source.
+ uint32_t target_bb_id() const { return target_bb_id_; }
+ // The label of the target of the *branch* for this dependence.
+ // Equal to the ID of the entry block for entry dependences.
+ //
+ // For example, for the partial CFG pictured below:
+ // 1 ---> 2 ---> 4 ---> 6
+ // \ \ ^
+ // \-> 3 \-> 5 -----/
+ // Block 6 is control dependent on block 1, but this dependence comes from the
+ // branch 1 -> 2, so in this case the branch target ID would be 2.
+ uint32_t branch_target_bb_id() const { return branch_target_bb_id_; }
+
+ // Create a direct control dependence from BB ID |source| to |target|.
+ ControlDependence(uint32_t source, uint32_t target)
+ : source_bb_id_(source),
+ target_bb_id_(target),
+ branch_target_bb_id_(target) {}
+ // Create a control dependence from BB ID |source| to |target| through the
+ // branch from |source| to |branch_target|.
+ ControlDependence(uint32_t source, uint32_t target, uint32_t branch_target)
+ : source_bb_id_(source),
+ target_bb_id_(target),
+ branch_target_bb_id_(branch_target) {}
+
+ // Gets the ID of the conditional value for the branch corresponding to this
+ // control dependence. This is the first input operand for both
+ // OpConditionalBranch and OpSwitch.
+ // Returns 0 for entry dependences.
+ uint32_t GetConditionID(const CFG& cfg) const;
+
+ bool operator==(const ControlDependence& other) const;
+ bool operator!=(const ControlDependence& other) const {
+ return !(*this == other);
+ }
+
+ // Comparison operators, ordered lexicographically. Total ordering.
+ bool operator<(const ControlDependence& other) const;
+ bool operator>(const ControlDependence& other) const { return other < *this; }
+ bool operator<=(const ControlDependence& other) const {
+ return !(*this > other);
+ }
+ bool operator>=(const ControlDependence& other) const {
+ return !(*this < other);
+ }
+
+ private:
+ uint32_t source_bb_id_;
+ uint32_t target_bb_id_;
+ uint32_t branch_target_bb_id_;
+};
+
+// Prints |dep| to |os| in a human-readable way. For example,
+// 1->2 (target_bb_id = branch_target_bb_id = 2)
+// 3->4 through 5 (target_bb_id = 4, branch_target_bb_id = 5)
+std::ostream& operator<<(std::ostream& os, const ControlDependence& dep);
+
+// Represents the control dependence graph. A basic block is control dependent
+// on another if the result of that block (e.g. the condition of a conditional
+// branch) influences whether it is executed or not. More formally, a block A is
+// control dependent on B iff:
+// 1. there exists a path from A to the exit node that does *not* go through B
+// (i.e., A does not postdominate B), and
+// 2. there exists a path B -> b_1 -> ... -> b_n -> A such that A post-dominates
+// all nodes b_i.
+class ControlDependenceAnalysis {
+ public:
+ // Map basic block labels to control dependencies/dependents.
+ // Not guaranteed to be in any particular order.
+ using ControlDependenceList = std::vector<ControlDependence>;
+ using ControlDependenceListMap =
+ std::unordered_map<uint32_t, ControlDependenceList>;
+
+ // 0, the label number for the pseudo entry block.
+ // All control dependences on the pseudo entry block are of type kEntry, and
+ // vice versa.
+ static constexpr uint32_t kPseudoEntryBlock = 0;
+
+ // Build the control dependence graph for the given control flow graph |cfg|
+ // and corresponding post-dominator analysis |pdom|.
+ void ComputeControlDependenceGraph(const CFG& cfg,
+ const PostDominatorAnalysis& pdom);
+
+ // Get the list of the nodes that depend on a block.
+ // Return value is not guaranteed to be in any particular order.
+ const ControlDependenceList& GetDependenceTargets(uint32_t block) const {
+ return forward_nodes_.at(block);
+ }
+
+ // Get the list of the nodes on which a block depends on.
+ // Return value is not guaranteed to be in any particular order.
+ const ControlDependenceList& GetDependenceSources(uint32_t block) const {
+ return reverse_nodes_.at(block);
+ }
+
+ // Runs the function |f| on each block label in the CDG. If any iteration
+ // returns false, immediately stops iteration and returns false. Otherwise
+ // returns true. Nodes are iterated in some undefined order, including the
+ // pseudo-entry block.
+ bool WhileEachBlockLabel(std::function<bool(uint32_t)> f) const {
+ for (const auto& entry : forward_nodes_) {
+ if (!f(entry.first)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Runs the function |f| on each block label in the CDG. Nodes are iterated in
+ // some undefined order, including the pseudo-entry block.
+ void ForEachBlockLabel(std::function<void(uint32_t)> f) const {
+ WhileEachBlockLabel([&f](uint32_t label) {
+ f(label);
+ return true;
+ });
+ }
+
+ // Returns true if the block |id| exists in the control dependence graph.
+ // This can be false even if the block exists in the function when it is part
+ // of an infinite loop, since it is not part of the post-dominator tree.
+ bool HasBlock(uint32_t id) const { return forward_nodes_.count(id) > 0; }
+
+ // Returns true if block |a| is dependent on block |b|.
+ bool IsDependent(uint32_t a, uint32_t b) const {
+ if (!HasBlock(a)) return false;
+ // BBs tend to have more dependents (targets) than they are dependent on
+ // (sources), so search sources.
+ const ControlDependenceList& a_sources = GetDependenceSources(a);
+ return std::find_if(a_sources.begin(), a_sources.end(),
+ [b](const ControlDependence& dep) {
+ return dep.source_bb_id() == b;
+ }) != a_sources.end();
+ }
+
+ private:
+ // Computes the post-dominance frontiers (i.e. the reverse CDG) for each node
+ // in the post-dominator tree. Only modifies reverse_nodes_; forward_nodes_ is
+ // not modified.
+ void ComputePostDominanceFrontiers(const CFG& cfg,
+ const PostDominatorAnalysis& pdom);
+ // Computes the post-dominance frontier for a specific node |pdom_node| in the
+ // post-dominator tree. Result is placed in reverse_nodes_[pdom_node.id()].
+ void ComputePostDominanceFrontierForNode(const CFG& cfg,
+ const PostDominatorAnalysis& pdom,
+ uint32_t function_entry,
+ const DominatorTreeNode& pdom_node);
+
+ // Computes the forward graph (forward_nodes_) from the reverse graph
+ // (reverse_nodes_).
+ void ComputeForwardGraphFromReverse();
+
+ ControlDependenceListMap forward_nodes_;
+ ControlDependenceListMap reverse_nodes_;
+};
+
+} // namespace opt
+} // namespace spvtools
+
+#endif // SOURCE_OPT_CONTROL_DEPENDENCE_H_
diff --git a/source/opt/convert_to_half_pass.cpp b/source/opt/convert_to_half_pass.cpp
index 6b3b540a..4086e31a 100644
--- a/source/opt/convert_to_half_pass.cpp
+++ b/source/opt/convert_to_half_pass.cpp
@@ -177,18 +177,21 @@ bool ConvertToHalfPass::GenHalfArith(Instruction* inst) {
return modified;
}
-bool ConvertToHalfPass::ProcessPhi(Instruction* inst) {
- // Add float16 converts of any float32 operands and change type
- // of phi to float16 equivalent. Operand converts need to be added to
- // preceeding blocks.
+bool ConvertToHalfPass::ProcessPhi(Instruction* inst, uint32_t from_width,
+ uint32_t to_width) {
+ // Add converts of any float operands to to_width if they are of from_width.
+ // If converting to 16, change type of phi to float16 equivalent and remember
+ // result id. Converts need to be added to preceding blocks.
uint32_t ocnt = 0;
uint32_t* prev_idp;
- inst->ForEachInId([&ocnt, &prev_idp, this](uint32_t* idp) {
+ bool modified = false;
+ inst->ForEachInId([&ocnt, &prev_idp, &from_width, &to_width, &modified,
+ this](uint32_t* idp) {
if (ocnt % 2 == 0) {
prev_idp = idp;
} else {
Instruction* val_inst = get_def_use_mgr()->GetDef(*prev_idp);
- if (IsFloat(val_inst, 32)) {
+ if (IsFloat(val_inst, from_width)) {
BasicBlock* bp = context()->get_instr_block(*idp);
auto insert_before = bp->tail();
if (insert_before != bp->begin()) {
@@ -197,15 +200,19 @@ bool ConvertToHalfPass::ProcessPhi(Instruction* inst) {
insert_before->opcode() != SpvOpLoopMerge)
++insert_before;
}
- GenConvert(prev_idp, 16, &*insert_before);
+ GenConvert(prev_idp, to_width, &*insert_before);
+ modified = true;
}
}
++ocnt;
});
- inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
- get_def_use_mgr()->AnalyzeInstUse(inst);
- converted_ids_.insert(inst->result_id());
- return true;
+ if (to_width == 16u) {
+ inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16u));
+ converted_ids_.insert(inst->result_id());
+ modified = true;
+ }
+ if (modified) get_def_use_mgr()->AnalyzeInstUse(inst);
+ return modified;
}
bool ConvertToHalfPass::ProcessConvert(Instruction* inst) {
@@ -242,9 +249,10 @@ bool ConvertToHalfPass::ProcessImageRef(Instruction* inst) {
}
bool ConvertToHalfPass::ProcessDefault(Instruction* inst) {
- bool modified = false;
// If non-relaxed instruction has changed operands, need to convert
// them back to float32
+ if (inst->opcode() == SpvOpPhi) return ProcessPhi(inst, 16u, 32u);
+ bool modified = false;
inst->ForEachInId([&inst, &modified, this](uint32_t* idp) {
if (converted_ids_.count(*idp) == 0) return;
uint32_t old_id = *idp;
@@ -262,7 +270,7 @@ bool ConvertToHalfPass::GenHalfInst(Instruction* inst) {
if (IsArithmetic(inst) && inst_relaxed)
modified = GenHalfArith(inst);
else if (inst->opcode() == SpvOpPhi && inst_relaxed)
- modified = ProcessPhi(inst);
+ modified = ProcessPhi(inst, 32u, 16u);
else if (inst->opcode() == SpvOpFConvert)
modified = ProcessConvert(inst);
else if (image_ops_.count(inst->opcode()) != 0)
@@ -340,7 +348,7 @@ Pass::Status ConvertToHalfPass::ProcessImpl() {
Pass::ProcessFunction pfn = [this](Function* fp) {
return ProcessFunction(fp);
};
- bool modified = context()->ProcessEntryPointCallTree(pfn);
+ bool modified = context()->ProcessReachableCallTree(pfn);
// If modified, make sure module has Float16 capability
if (modified) context()->AddCapability(SpvCapabilityFloat16);
// Remove all RelaxedPrecision decorations from instructions and globals
diff --git a/source/opt/convert_to_half_pass.h b/source/opt/convert_to_half_pass.h
index b647dd4a..c6e84d1b 100644
--- a/source/opt/convert_to_half_pass.h
+++ b/source/opt/convert_to_half_pass.h
@@ -93,7 +93,7 @@ class ConvertToHalfPass : public Pass {
bool GenHalfArith(Instruction* inst);
// Gen code for relaxed phi |inst|
- bool ProcessPhi(Instruction* inst);
+ bool ProcessPhi(Instruction* inst, uint32_t from_width, uint32_t to_width);
// Gen code for relaxed convert |inst|
bool ProcessConvert(Instruction* inst);
diff --git a/source/opt/convert_to_sampled_image_pass.cpp b/source/opt/convert_to_sampled_image_pass.cpp
new file mode 100644
index 00000000..e84d3578
--- /dev/null
+++ b/source/opt/convert_to_sampled_image_pass.cpp
@@ -0,0 +1,437 @@
+// Copyright (c) 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/opt/convert_to_sampled_image_pass.h"
+
+#include <cctype>
+#include <cstring>
+#include <tuple>
+
+#include "source/opt/ir_builder.h"
+#include "source/util/make_unique.h"
+#include "source/util/parse_number.h"
+
+namespace spvtools {
+namespace opt {
+
+using VectorOfDescriptorSetAndBindingPairs =
+ std::vector<DescriptorSetAndBinding>;
+using DescriptorSetBindingToInstruction =
+ ConvertToSampledImagePass::DescriptorSetBindingToInstruction;
+
+namespace {
+
+using utils::ParseNumber;
+
+// Returns true if the given char is ':', '\0' or considered as blank space
+// (i.e.: '\n', '\r', '\v', '\t', '\f' and ' ').
+bool IsSeparator(char ch) {
+ return std::strchr(":\0", ch) || std::isspace(ch) != 0;
+}
+
+// Reads characters starting from |str| until it meets a separator. Parses a
+// number from the characters and stores it into |number|. Returns the pointer
+// to the separator if it succeeds. Otherwise, returns nullptr.
+const char* ParseNumberUntilSeparator(const char* str, uint32_t* number) {
+ const char* number_begin = str;
+ while (!IsSeparator(*str)) str++;
+ const char* number_end = str;
+ std::string number_in_str(number_begin, number_end - number_begin);
+ if (!utils::ParseNumber(number_in_str.c_str(), number)) {
+ // The descriptor set is not a valid uint32 number.
+ return nullptr;
+ }
+ return str;
+}
+
+// Returns id of the image type used for the sampled image type of
+// |sampled_image|.
+uint32_t GetImageTypeOfSampledImage(analysis::TypeManager* type_mgr,
+ Instruction* sampled_image) {
+ auto* sampled_image_type =
+ type_mgr->GetType(sampled_image->type_id())->AsSampledImage();
+ return type_mgr->GetTypeInstruction(sampled_image_type->image_type());
+}
+
+// Finds the instruction whose id is |inst_id|. Follows the operand of
+// OpCopyObject recursively if the opcode of the instruction is OpCopyObject
+// and returns the first instruction that does not have OpCopyObject as opcode.
+Instruction* GetNonCopyObjectDef(analysis::DefUseManager* def_use_mgr,
+ uint32_t inst_id) {
+ Instruction* inst = def_use_mgr->GetDef(inst_id);
+ while (inst->opcode() == SpvOpCopyObject) {
+ inst_id = inst->GetSingleWordInOperand(0u);
+ inst = def_use_mgr->GetDef(inst_id);
+ }
+ return inst;
+}
+
+} // namespace
+
+bool ConvertToSampledImagePass::GetDescriptorSetBinding(
+ const Instruction& inst,
+ DescriptorSetAndBinding* descriptor_set_binding) const {
+ auto* decoration_manager = context()->get_decoration_mgr();
+ bool found_descriptor_set_to_convert = false;
+ bool found_binding_to_convert = false;
+ for (auto decorate :
+ decoration_manager->GetDecorationsFor(inst.result_id(), false)) {
+ uint32_t decoration = decorate->GetSingleWordInOperand(1u);
+ if (decoration == SpvDecorationDescriptorSet) {
+ if (found_descriptor_set_to_convert) {
+ assert(false && "A resource has two OpDecorate for the descriptor set");
+ return false;
+ }
+ descriptor_set_binding->descriptor_set =
+ decorate->GetSingleWordInOperand(2u);
+ found_descriptor_set_to_convert = true;
+ } else if (decoration == SpvDecorationBinding) {
+ if (found_binding_to_convert) {
+ assert(false && "A resource has two OpDecorate for the binding");
+ return false;
+ }
+ descriptor_set_binding->binding = decorate->GetSingleWordInOperand(2u);
+ found_binding_to_convert = true;
+ }
+ }
+ return found_descriptor_set_to_convert && found_binding_to_convert;
+}
+
+bool ConvertToSampledImagePass::ShouldResourceBeConverted(
+ const DescriptorSetAndBinding& descriptor_set_binding) const {
+ return descriptor_set_binding_pairs_.find(descriptor_set_binding) !=
+ descriptor_set_binding_pairs_.end();
+}
+
+const analysis::Type* ConvertToSampledImagePass::GetVariableType(
+ const Instruction& variable) const {
+ if (variable.opcode() != SpvOpVariable) return nullptr;
+ auto* type = context()->get_type_mgr()->GetType(variable.type_id());
+ auto* pointer_type = type->AsPointer();
+ if (!pointer_type) return nullptr;
+
+ return pointer_type->pointee_type();
+}
+
+SpvStorageClass ConvertToSampledImagePass::GetStorageClass(
+ const Instruction& variable) const {
+ assert(variable.opcode() == SpvOpVariable);
+ auto* type = context()->get_type_mgr()->GetType(variable.type_id());
+ auto* pointer_type = type->AsPointer();
+ if (!pointer_type) return SpvStorageClassMax;
+
+ return pointer_type->storage_class();
+}
+
+bool ConvertToSampledImagePass::CollectResourcesToConvert(
+ DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_sampler,
+ DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_image)
+ const {
+ for (auto& inst : context()->types_values()) {
+ const auto* variable_type = GetVariableType(inst);
+ if (variable_type == nullptr) continue;
+
+ DescriptorSetAndBinding descriptor_set_binding;
+ if (!GetDescriptorSetBinding(inst, &descriptor_set_binding)) continue;
+
+ if (!ShouldResourceBeConverted(descriptor_set_binding)) {
+ continue;
+ }
+
+ if (variable_type->AsImage()) {
+ if (!descriptor_set_binding_pair_to_image
+ ->insert({descriptor_set_binding, &inst})
+ .second) {
+ return false;
+ }
+ } else if (variable_type->AsSampler()) {
+ if (!descriptor_set_binding_pair_to_sampler
+ ->insert({descriptor_set_binding, &inst})
+ .second) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+Pass::Status ConvertToSampledImagePass::Process() {
+ Status status = Status::SuccessWithoutChange;
+
+ DescriptorSetBindingToInstruction descriptor_set_binding_pair_to_sampler,
+ descriptor_set_binding_pair_to_image;
+ if (!CollectResourcesToConvert(&descriptor_set_binding_pair_to_sampler,
+ &descriptor_set_binding_pair_to_image)) {
+ return Status::Failure;
+ }
+
+ for (auto& image : descriptor_set_binding_pair_to_image) {
+ status = CombineStatus(
+ status, UpdateImageVariableToSampledImage(image.second, image.first));
+ if (status == Status::Failure) {
+ return status;
+ }
+ }
+
+ for (const auto& sampler : descriptor_set_binding_pair_to_sampler) {
+ // Converting only a Sampler to Sampled Image is not allowed. It must have a
+ // corresponding image to combine the sampler with.
+ auto image_itr = descriptor_set_binding_pair_to_image.find(sampler.first);
+ if (image_itr == descriptor_set_binding_pair_to_image.end() ||
+ image_itr->second == nullptr) {
+ return Status::Failure;
+ }
+
+ status = CombineStatus(
+ status, CheckUsesOfSamplerVariable(sampler.second, image_itr->second));
+ if (status == Status::Failure) {
+ return status;
+ }
+ }
+
+ return status;
+}
+
+void ConvertToSampledImagePass::FindUses(const Instruction* inst,
+ std::vector<Instruction*>* uses,
+ uint32_t user_opcode) const {
+ auto* def_use_mgr = context()->get_def_use_mgr();
+ def_use_mgr->ForEachUser(inst, [uses, user_opcode, this](Instruction* user) {
+ if (user->opcode() == user_opcode) {
+ uses->push_back(user);
+ } else if (user->opcode() == SpvOpCopyObject) {
+ FindUses(user, uses, user_opcode);
+ }
+ });
+}
+
+void ConvertToSampledImagePass::FindUsesOfImage(
+ const Instruction* image, std::vector<Instruction*>* uses) const {
+ auto* def_use_mgr = context()->get_def_use_mgr();
+ def_use_mgr->ForEachUser(image, [uses, this](Instruction* user) {
+ switch (user->opcode()) {
+ case SpvOpImageFetch:
+ case SpvOpImageRead:
+ case SpvOpImageWrite:
+ case SpvOpImageQueryFormat:
+ case SpvOpImageQueryOrder:
+ case SpvOpImageQuerySizeLod:
+ case SpvOpImageQuerySize:
+ case SpvOpImageQueryLevels:
+ case SpvOpImageQuerySamples:
+ case SpvOpImageSparseFetch:
+ uses->push_back(user);
+ default:
+ break;
+ }
+ if (user->opcode() == SpvOpCopyObject) {
+ FindUsesOfImage(user, uses);
+ }
+ });
+}
+
+Instruction* ConvertToSampledImagePass::CreateImageExtraction(
+ Instruction* sampled_image) {
+ InstructionBuilder builder(
+ context(), sampled_image->NextNode(),
+ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+ return builder.AddUnaryOp(
+ GetImageTypeOfSampledImage(context()->get_type_mgr(), sampled_image),
+ SpvOpImage, sampled_image->result_id());
+}
+
+uint32_t ConvertToSampledImagePass::GetSampledImageTypeForImage(
+ Instruction* image_variable) {
+ const auto* variable_type = GetVariableType(*image_variable);
+ if (variable_type == nullptr) return 0;
+ const auto* image_type = variable_type->AsImage();
+ if (image_type == nullptr) return 0;
+
+ analysis::Image image_type_for_sampled_image(*image_type);
+ analysis::SampledImage sampled_image_type(&image_type_for_sampled_image);
+ return context()->get_type_mgr()->GetTypeInstruction(&sampled_image_type);
+}
+
+Instruction* ConvertToSampledImagePass::UpdateImageUses(
+ Instruction* sampled_image_load) {
+ std::vector<Instruction*> uses_of_load;
+ FindUsesOfImage(sampled_image_load, &uses_of_load);
+ if (uses_of_load.empty()) return nullptr;
+
+ auto* extracted_image = CreateImageExtraction(sampled_image_load);
+ for (auto* user : uses_of_load) {
+ user->SetInOperand(0, {extracted_image->result_id()});
+ context()->get_def_use_mgr()->AnalyzeInstUse(user);
+ }
+ return extracted_image;
+}
+
+bool ConvertToSampledImagePass::
+ IsSamplerOfSampledImageDecoratedByDescriptorSetBinding(
+ Instruction* sampled_image_inst,
+ const DescriptorSetAndBinding& descriptor_set_binding) {
+ auto* def_use_mgr = context()->get_def_use_mgr();
+ uint32_t sampler_id = sampled_image_inst->GetSingleWordInOperand(1u);
+ auto* sampler_load = def_use_mgr->GetDef(sampler_id);
+ if (sampler_load->opcode() != SpvOpLoad) return false;
+ auto* sampler = def_use_mgr->GetDef(sampler_load->GetSingleWordInOperand(0u));
+ DescriptorSetAndBinding sampler_descriptor_set_binding;
+ return GetDescriptorSetBinding(*sampler, &sampler_descriptor_set_binding) &&
+ sampler_descriptor_set_binding == descriptor_set_binding;
+}
+
+void ConvertToSampledImagePass::UpdateSampledImageUses(
+ Instruction* image_load, Instruction* image_extraction,
+ const DescriptorSetAndBinding& image_descriptor_set_binding) {
+ std::vector<Instruction*> sampled_image_users;
+ FindUses(image_load, &sampled_image_users, SpvOpSampledImage);
+
+ auto* def_use_mgr = context()->get_def_use_mgr();
+ for (auto* sampled_image_inst : sampled_image_users) {
+ if (IsSamplerOfSampledImageDecoratedByDescriptorSetBinding(
+ sampled_image_inst, image_descriptor_set_binding)) {
+ context()->ReplaceAllUsesWith(sampled_image_inst->result_id(),
+ image_load->result_id());
+ def_use_mgr->AnalyzeInstUse(image_load);
+ context()->KillInst(sampled_image_inst);
+ } else {
+ if (!image_extraction)
+ image_extraction = CreateImageExtraction(image_load);
+ sampled_image_inst->SetInOperand(0, {image_extraction->result_id()});
+ def_use_mgr->AnalyzeInstUse(sampled_image_inst);
+ }
+ }
+}
+
+void ConvertToSampledImagePass::MoveInstructionNextToType(Instruction* inst,
+ uint32_t type_id) {
+ auto* type_inst = context()->get_def_use_mgr()->GetDef(type_id);
+ inst->SetResultType(type_id);
+ inst->RemoveFromList();
+ inst->InsertAfter(type_inst);
+}
+
+bool ConvertToSampledImagePass::ConvertImageVariableToSampledImage(
+ Instruction* image_variable, uint32_t sampled_image_type_id) {
+ auto* sampled_image_type =
+ context()->get_type_mgr()->GetType(sampled_image_type_id);
+ if (sampled_image_type == nullptr) return false;
+ auto storage_class = GetStorageClass(*image_variable);
+ if (storage_class == SpvStorageClassMax) return false;
+ analysis::Pointer sampled_image_pointer(sampled_image_type, storage_class);
+
+ // Make sure |image_variable| is behind its type i.e., avoid the forward
+ // reference.
+ uint32_t type_id =
+ context()->get_type_mgr()->GetTypeInstruction(&sampled_image_pointer);
+ MoveInstructionNextToType(image_variable, type_id);
+ return true;
+}
+
+Pass::Status ConvertToSampledImagePass::UpdateImageVariableToSampledImage(
+ Instruction* image_variable,
+ const DescriptorSetAndBinding& descriptor_set_binding) {
+ std::vector<Instruction*> image_variable_loads;
+ FindUses(image_variable, &image_variable_loads, SpvOpLoad);
+ if (image_variable_loads.empty()) return Status::SuccessWithoutChange;
+
+ const uint32_t sampled_image_type_id =
+ GetSampledImageTypeForImage(image_variable);
+ if (!sampled_image_type_id) return Status::Failure;
+
+ for (auto* load : image_variable_loads) {
+ load->SetResultType(sampled_image_type_id);
+ auto* image_extraction = UpdateImageUses(load);
+ UpdateSampledImageUses(load, image_extraction, descriptor_set_binding);
+ }
+
+ return ConvertImageVariableToSampledImage(image_variable,
+ sampled_image_type_id)
+ ? Status::SuccessWithChange
+ : Status::Failure;
+}
+
+bool ConvertToSampledImagePass::DoesSampledImageReferenceImage(
+ Instruction* sampled_image_inst, Instruction* image_variable) {
+ if (sampled_image_inst->opcode() != SpvOpSampledImage) return false;
+ auto* def_use_mgr = context()->get_def_use_mgr();
+ auto* image_load = GetNonCopyObjectDef(
+ def_use_mgr, sampled_image_inst->GetSingleWordInOperand(0u));
+ if (image_load->opcode() != SpvOpLoad) return false;
+ auto* image =
+ GetNonCopyObjectDef(def_use_mgr, image_load->GetSingleWordInOperand(0u));
+ return image->opcode() == SpvOpVariable &&
+ image->result_id() == image_variable->result_id();
+}
+
+Pass::Status ConvertToSampledImagePass::CheckUsesOfSamplerVariable(
+ const Instruction* sampler_variable,
+ Instruction* image_to_be_combined_with) {
+ if (image_to_be_combined_with == nullptr) return Status::Failure;
+
+ std::vector<Instruction*> sampler_variable_loads;
+ FindUses(sampler_variable, &sampler_variable_loads, SpvOpLoad);
+ for (auto* load : sampler_variable_loads) {
+ std::vector<Instruction*> sampled_image_users;
+ FindUses(load, &sampled_image_users, SpvOpSampledImage);
+ for (auto* sampled_image_inst : sampled_image_users) {
+ if (!DoesSampledImageReferenceImage(sampled_image_inst,
+ image_to_be_combined_with)) {
+ return Status::Failure;
+ }
+ }
+ }
+ return Status::SuccessWithoutChange;
+}
+
+std::unique_ptr<VectorOfDescriptorSetAndBindingPairs>
+ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString(
+ const char* str) {
+ if (!str) return nullptr;
+
+ auto descriptor_set_binding_pairs =
+ MakeUnique<VectorOfDescriptorSetAndBindingPairs>();
+
+ while (std::isspace(*str)) str++; // skip leading spaces.
+
+ // The parsing loop, break when points to the end.
+ while (*str) {
+ // Parse the descriptor set.
+ uint32_t descriptor_set = 0;
+ str = ParseNumberUntilSeparator(str, &descriptor_set);
+ if (str == nullptr) return nullptr;
+
+ // Find the ':', spaces between the descriptor set and the ':' are not
+ // allowed.
+ if (*str++ != ':') {
+ // ':' not found
+ return nullptr;
+ }
+
+ // Parse the binding.
+ uint32_t binding = 0;
+ str = ParseNumberUntilSeparator(str, &binding);
+ if (str == nullptr) return nullptr;
+
+ descriptor_set_binding_pairs->push_back({descriptor_set, binding});
+
+ // Skip trailing spaces.
+ while (std::isspace(*str)) str++;
+ }
+
+ return descriptor_set_binding_pairs;
+}
+
+} // namespace opt
+} // namespace spvtools
diff --git a/source/opt/convert_to_sampled_image_pass.h b/source/opt/convert_to_sampled_image_pass.h
new file mode 100644
index 00000000..d3938af7
--- /dev/null
+++ b/source/opt/convert_to_sampled_image_pass.h
@@ -0,0 +1,207 @@
+// Copyright (c) 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_
+#define SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_
+
+#include <memory>
+#include <unordered_set>
+#include <utility>
+
+#include "source/opt/pass.h"
+#include "source/opt/types.h"
+
+namespace spvtools {
+namespace opt {
+
+// A struct for a pair of descriptor set and binding.
+struct DescriptorSetAndBinding {
+ uint32_t descriptor_set;
+ uint32_t binding;
+
+ bool operator==(const DescriptorSetAndBinding& descriptor_set_binding) const {
+ return descriptor_set_binding.descriptor_set == descriptor_set &&
+ descriptor_set_binding.binding == binding;
+ }
+};
+
+// See optimizer.hpp for documentation.
+class ConvertToSampledImagePass : public Pass {
+ public:
+ // Hashing functor for the pair of descriptor set and binding.
+ struct DescriptorSetAndBindingHash {
+ size_t operator()(
+ const DescriptorSetAndBinding& descriptor_set_binding) const {
+ return std::hash<uint32_t>()(descriptor_set_binding.descriptor_set) ^
+ std::hash<uint32_t>()(descriptor_set_binding.binding);
+ }
+ };
+
+ using SetOfDescriptorSetAndBindingPairs =
+ std::unordered_set<DescriptorSetAndBinding, DescriptorSetAndBindingHash>;
+ using DescriptorSetBindingToInstruction =
+ std::unordered_map<DescriptorSetAndBinding, Instruction*,
+ DescriptorSetAndBindingHash>;
+
+ explicit ConvertToSampledImagePass(
+ const std::vector<DescriptorSetAndBinding>& descriptor_set_binding_pairs)
+ : descriptor_set_binding_pairs_(descriptor_set_binding_pairs.begin(),
+ descriptor_set_binding_pairs.end()) {}
+
+ const char* name() const override { return "convert-to-sampled-image"; }
+ Status Process() override;
+
+ // Parses the given null-terminated C string to get a vector of descriptor set
+ // and binding pairs. Returns a unique pointer to the vector of descriptor set
+ // and binding pairs built from the given |str| on success. Returns a nullptr
+ // if the given string is not valid for building the vector of pairs.
+ // A valid string for building the vector of pairs should follow the rule
+ // below:
+ //
+ // "<descriptor set>:<binding> <descriptor set>:<binding> ..."
+ // Example:
+ // "3:5 2:1 0:4"
+ //
+ // Entries are separated with blank spaces (i.e.:' ', '\n', '\r', '\t',
+ // '\f', '\v'). Each entry corresponds to a descriptor set and binding pair.
+ // Multiple spaces between, before or after entries are allowed. However,
+ // spaces are not allowed within a descriptor set or binding.
+ //
+ // In each entry, the descriptor set and binding are separated by ':'.
+ // Missing ':' in any entry is invalid. And it is invalid to have blank
+ // spaces in between the descriptor set and ':' or ':' and the binding.
+ //
+ // <descriptor set>: the descriptor set.
+ // The text must represent a valid uint32_t number.
+ //
+ // <binding>: the binding.
+ // The text must represent a valid uint32_t number.
+ static std::unique_ptr<std::vector<DescriptorSetAndBinding>>
+ ParseDescriptorSetBindingPairsString(const char* str);
+
+ private:
+ // Collects resources to convert to sampled image and saves them in
+ // |descriptor_set_binding_pair_to_sampler| if the resource is a sampler and
+ // saves them in |descriptor_set_binding_pair_to_image| if the resource is an
+ // image. Returns false if two samplers or two images have the same descriptor
+ // set and binding. Otherwise, returns true.
+ bool CollectResourcesToConvert(
+ DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_sampler,
+ DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_image)
+ const;
+
+ // Finds an OpDecorate with DescriptorSet decorating |inst| and another
+ // OpDecorate with Binding decorating |inst|. Stores the descriptor set and
+ // binding in |descriptor_set_binding|. Returns whether it successfully finds
+ // the descriptor set and binding or not.
+ bool GetDescriptorSetBinding(
+ const Instruction& inst,
+ DescriptorSetAndBinding* descriptor_set_binding) const;
+
+ // Returns whether |descriptor_set_binding| is a pair of a descriptor set
+ // and a binding that we have to convert resources with it to a sampled image
+ // or not.
+ bool ShouldResourceBeConverted(
+ const DescriptorSetAndBinding& descriptor_set_binding) const;
+
+ // Returns the pointee type of the type of variable |variable|. If |variable|
+ // is not an OpVariable instruction, just returns nullptr.
+ const analysis::Type* GetVariableType(const Instruction& variable) const;
+
+ // Returns the storage class of |variable|.
+ SpvStorageClass GetStorageClass(const Instruction& variable) const;
+
+ // Finds |inst|'s users whose opcode is |user_opcode| or users of OpCopyObject
+ // instructions of |inst| whose opcode is |user_opcode| and puts them in
+ // |uses|.
+ void FindUses(const Instruction* inst, std::vector<Instruction*>* uses,
+ uint32_t user_opcode) const;
+
+ // Finds OpImage* instructions using |image| or OpCopyObject instructions that
+ // copy |image| and puts them in |uses|.
+ void FindUsesOfImage(const Instruction* image,
+ std::vector<Instruction*>* uses) const;
+
+ // Creates an OpImage instruction that extracts the image from the sampled
+ // image |sampled_image|.
+ Instruction* CreateImageExtraction(Instruction* sampled_image);
+
+ // Converts |image_variable| whose type is an image pointer to sampled image
+ // type. Updates users of |image_variable| accordingly. If some instructions
+ // e.g., OpImageRead use |image_variable| as an Image operand, creates an
+ // image extracted from the sampled image using OpImage and replace the Image
+ // operands of the users with the extracted image. If some OpSampledImage
+ // instructions use |image_variable| and sampler whose descriptor set and
+ // binding are the same with |image_variable|, just combines |image_variable|
+ // and the sampler to a sampled image.
+ Pass::Status UpdateImageVariableToSampledImage(
+ Instruction* image_variable,
+ const DescriptorSetAndBinding& descriptor_set_binding);
+
+ // Returns the id of type sampled image type whose image type is the one of
+ // |image_variable|.
+ uint32_t GetSampledImageTypeForImage(Instruction* image_variable);
+
+ // Moves |inst| next to the OpType* instruction with |type_id|.
+ void MoveInstructionNextToType(Instruction* inst, uint32_t type_id);
+
+ // Converts |image_variable| whose type is an image pointer to sampled image
+ // with the type id |sampled_image_type_id|. Returns whether it successfully
+ // converts the type of |image_variable| or not.
+ bool ConvertImageVariableToSampledImage(Instruction* image_variable,
+ uint32_t sampled_image_type_id);
+
+ // Replaces |sampled_image_load| instruction used by OpImage* with the image
+ // extracted from |sampled_image_load|. Returns the extracted image or nullptr
+ // if it does not have uses.
+ Instruction* UpdateImageUses(Instruction* sampled_image_load);
+
+ // Returns true if the sampler of |sampled_image_inst| is decorated by a
+ // descriptor set and a binding |descriptor_set_binding|.
+ bool IsSamplerOfSampledImageDecoratedByDescriptorSetBinding(
+ Instruction* sampled_image_inst,
+ const DescriptorSetAndBinding& descriptor_set_binding);
+
+ // Replaces OpSampledImage instructions using |image_load| with |image_load|
+ // if the sampler of the OpSampledImage instruction has descriptor set and
+ // binding |image_descriptor_set_binding|. Otherwise, replaces |image_load|
+ // with |image_extraction|.
+ void UpdateSampledImageUses(
+ Instruction* image_load, Instruction* image_extraction,
+ const DescriptorSetAndBinding& image_descriptor_set_binding);
+
+ // Checks the uses of |sampler_variable|. When a sampler is used by
+ // OpSampledImage instruction, the corresponding image must be
+ // |image_to_be_combined_with| that should be already converted to a sampled
+ // image by UpdateImageVariableToSampledImage() method.
+ Pass::Status CheckUsesOfSamplerVariable(
+ const Instruction* sampler_variable,
+ Instruction* image_to_be_combined_with);
+
+ // Returns true if Image operand of |sampled_image_inst| is the image of
+ // |image_variable|.
+ bool DoesSampledImageReferenceImage(Instruction* sampled_image_inst,
+ Instruction* image_variable);
+
+ // A set of pairs of descriptor set and binding. If an image and/or a sampler
+ // have a pair of descriptor set and binding that is an element of
+ // |descriptor_set_binding_pairs_|, they/it will be converted to a sampled
+ // image by this pass.
+ const SetOfDescriptorSetAndBindingPairs descriptor_set_binding_pairs_;
+};
+
+} // namespace opt
+} // namespace spvtools
+
+#endif // SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_
diff --git a/source/opt/copy_prop_arrays.cpp b/source/opt/copy_prop_arrays.cpp
index 67a97b6a..62ed5e77 100644
--- a/source/opt/copy_prop_arrays.cpp
+++ b/source/opt/copy_prop_arrays.cpp
@@ -29,10 +29,10 @@ const uint32_t kCompositeExtractObjectInOperand = 0;
const uint32_t kTypePointerStorageClassInIdx = 0;
const uint32_t kTypePointerPointeeInIdx = 1;
-bool IsOpenCL100DebugDeclareOrValue(Instruction* di) {
- auto dbg_opcode = di->GetOpenCL100DebugOpcode();
- return dbg_opcode == OpenCLDebugInfo100DebugDeclare ||
- dbg_opcode == OpenCLDebugInfo100DebugValue;
+bool IsDebugDeclareOrValue(Instruction* di) {
+ auto dbg_opcode = di->GetCommonDebugOpcode();
+ return dbg_opcode == CommonDebugInfoDebugDeclare ||
+ dbg_opcode == CommonDebugInfoDebugValue;
}
} // namespace
@@ -40,6 +40,10 @@ bool IsOpenCL100DebugDeclareOrValue(Instruction* di) {
Pass::Status CopyPropagateArrays::Process() {
bool modified = false;
for (Function& function : *get_module()) {
+ if (function.IsDeclaration()) {
+ continue;
+ }
+
BasicBlock* entry_bb = &*function.begin();
for (auto var_inst = entry_bb->begin(); var_inst->opcode() == SpvOpVariable;
@@ -194,7 +198,7 @@ bool CopyPropagateArrays::HasValidReferencesOnly(Instruction* ptr_inst,
return ptr_inst->opcode() == SpvOpVariable &&
store_inst->GetSingleWordInOperand(kStorePointerInOperand) ==
ptr_inst->result_id();
- } else if (IsOpenCL100DebugDeclareOrValue(use)) {
+ } else if (IsDebugDeclareOrValue(use)) {
return true;
}
// Some other instruction. Be conservative.
@@ -500,7 +504,7 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst,
const_mgr,
type](Instruction* use,
uint32_t) {
- if (IsOpenCL100DebugDeclareOrValue(use)) return true;
+ if (IsDebugDeclareOrValue(use)) return true;
switch (use->opcode()) {
case SpvOpLoad: {
@@ -592,9 +596,9 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst,
Instruction* use = pair.first;
uint32_t index = pair.second;
- if (use->IsOpenCL100DebugInstr()) {
- switch (use->GetOpenCL100DebugOpcode()) {
- case OpenCLDebugInfo100DebugDeclare: {
+ if (use->IsCommonDebugInstr()) {
+ switch (use->GetCommonDebugOpcode()) {
+ case CommonDebugInfoDebugDeclare: {
if (new_ptr_inst->opcode() == SpvOpVariable ||
new_ptr_inst->opcode() == SpvOpFunctionParameter) {
context()->ForgetUses(use);
@@ -608,9 +612,8 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst,
context()->ForgetUses(use);
// Change DebugDeclare to DebugValue.
- use->SetOperand(
- index - 2,
- {static_cast<uint32_t>(OpenCLDebugInfo100DebugValue)});
+ use->SetOperand(index - 2,
+ {static_cast<uint32_t>(CommonDebugInfoDebugValue)});
use->SetOperand(index, {new_ptr_inst->result_id()});
// Add Deref operation.
@@ -625,7 +628,7 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst,
}
break;
}
- case OpenCLDebugInfo100DebugValue:
+ case CommonDebugInfoDebugValue:
context()->ForgetUses(use);
use->SetOperand(index, {new_ptr_inst->result_id()});
context()->AnalyzeUses(use);
diff --git a/source/opt/dataflow.cpp b/source/opt/dataflow.cpp
new file mode 100644
index 00000000..c91fad08
--- /dev/null
+++ b/source/opt/dataflow.cpp
@@ -0,0 +1,91 @@
+// Copyright (c) 2021 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/opt/dataflow.h"
+
+#include <algorithm>
+#include <cstdint>
+
+namespace spvtools {
+namespace opt {
+
+bool DataFlowAnalysis::Enqueue(Instruction* inst) {
+ bool& is_enqueued = on_worklist_[inst];
+ if (is_enqueued) return false;
+ is_enqueued = true;
+ worklist_.push(inst);
+ return true;
+}
+
+DataFlowAnalysis::VisitResult DataFlowAnalysis::RunOnce(
+ Function* function, bool is_first_iteration) {
+ InitializeWorklist(function, is_first_iteration);
+ VisitResult ret = VisitResult::kResultFixed;
+ while (!worklist_.empty()) {
+ Instruction* top = worklist_.front();
+ worklist_.pop();
+ on_worklist_[top] = false;
+ VisitResult result = Visit(top);
+ if (result == VisitResult::kResultChanged) {
+ EnqueueSuccessors(top);
+ ret = VisitResult::kResultChanged;
+ }
+ }
+ return ret;
+}
+
+void DataFlowAnalysis::Run(Function* function) {
+ VisitResult result = RunOnce(function, true);
+ while (result == VisitResult::kResultChanged) {
+ result = RunOnce(function, false);
+ }
+}
+
+void ForwardDataFlowAnalysis::InitializeWorklist(Function* function,
+ bool /*is_first_iteration*/) {
+ context().cfg()->ForEachBlockInReversePostOrder(
+ function->entry().get(), [this](BasicBlock* bb) {
+ if (label_position_ == LabelPosition::kLabelsOnly) {
+ Enqueue(bb->GetLabelInst());
+ return;
+ }
+ if (label_position_ == LabelPosition::kLabelsAtBeginning) {
+ Enqueue(bb->GetLabelInst());
+ }
+ for (Instruction& inst : *bb) {
+ Enqueue(&inst);
+ }
+ if (label_position_ == LabelPosition::kLabelsAtEnd) {
+ Enqueue(bb->GetLabelInst());
+ }
+ });
+}
+
+void ForwardDataFlowAnalysis::EnqueueUsers(Instruction* inst) {
+ context().get_def_use_mgr()->ForEachUser(
+ inst, [this](Instruction* user) { Enqueue(user); });
+}
+
+void ForwardDataFlowAnalysis::EnqueueBlockSuccessors(Instruction* inst) {
+ if (inst->opcode() != SpvOpLabel) return;
+ context()
+ .cfg()
+ ->block(inst->result_id())
+ ->ForEachSuccessorLabel([this](uint32_t* label) {
+ Enqueue(context().cfg()->block(*label)->GetLabelInst());
+ });
+}
+
+} // namespace opt
+} // namespace spvtools
diff --git a/source/opt/dataflow.h b/source/opt/dataflow.h
new file mode 100644
index 00000000..be07415d
--- /dev/null
+++ b/source/opt/dataflow.h
@@ -0,0 +1,148 @@
+// Copyright (c) 2021 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_OPT_DATAFLOW_H_
+#define SOURCE_OPT_DATAFLOW_H_
+
+#include <queue>
+#include <unordered_map>
+#include <vector>
+
+#include "source/opt/instruction.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace opt {
+
+// Generic data-flow analysis.
+// Maintains a worklist of instructions to process and processes them in a
+// specified order. See also ForwardDataFlowAnalysis, which is specialized for
+// forward data-flow analysis.
+class DataFlowAnalysis {
+ public:
+ // The result of a |Visit| operation on an instruction.
+ // This is used to determine when analysis has reached a fixpoint.
+ enum class VisitResult {
+ // The analysis result for this instruction has changed.
+ // This means that any instructions that depend on it (its successors) must
+ // be recomputed.
+ kResultChanged,
+ // The analysis result for this instruction has not changed.
+ // When all visit operations return |kResultFixed|, the analysis has reached
+ // a fixpoint (converged).
+ kResultFixed,
+ };
+
+ virtual ~DataFlowAnalysis() {}
+
+ // Run this analysis on a given function.
+ // For analyses which work interprocedurally, |function| may be ignored.
+ void Run(Function* function);
+
+ protected:
+ DataFlowAnalysis(IRContext& context) : context_(context) {}
+
+ // Initialize the worklist for a given function.
+ // |is_first_iteration| is true on the first call to |Run| and false
+ // afterwards. All subsequent runs are only necessary to check if the analysis
+ // has converged; if |EnqueueSuccessors| is complete, |InitializeWorklist|
+ // should do nothing after the first iteration.
+ virtual void InitializeWorklist(Function* function,
+ bool is_first_iteration) = 0;
+
+ // Enqueues the successors (instructions which use the analysis result) of
+ // |inst|. This is not required to be complete, but convergence is faster when
+ // it is. This is called whenever |Visit| returns |kResultChanged|.
+ virtual void EnqueueSuccessors(Instruction* inst) = 0;
+
+ // Visits the given instruction, recomputing the analysis result. This is
+ // called once per instruction queued in |InitializeWorklist| and afterward
+ // when a predecessor is changed, through |EnqueueSuccessors|.
+ virtual VisitResult Visit(Instruction* inst) = 0;
+
+ // Enqueues the given instruction to be visited. Ignored if already in the
+ // worklist.
+ bool Enqueue(Instruction* inst);
+
+ IRContext& context() { return context_; }
+
+ private:
+ // Runs one pass, calling |InitializeWorklist| and then iterating through the
+ // worklist until all fixed.
+ VisitResult RunOnce(Function* function, bool is_first_iteration);
+
+ IRContext& context_;
+ std::unordered_map<Instruction*, bool> on_worklist_;
+ // The worklist, which contains the list of instructions to be visited.
+ //
+ // The choice of data structure was influenced by the data in "Iterative
+ // Data-flow Analysis, Revisited" (Cooper et al, 2002).
+ // https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.125.1549&rep=rep1&type=pdf
+ // The paper shows that the overall performance benefit of a priority queue
+ // over a regular queue or stack is relatively small (or negative).
+ //
+ // A queue has the advantage that nodes are visited in the same order they are
+ // enqueued, which relieves the analysis from inserting nodes "backwards", for
+ // example in worklist initialization. Also, as the paper claims that sorting
+ // successors does not improve runtime, we can use a single queue which is
+ // modified during iteration.
+ std::queue<Instruction*> worklist_;
+};
+
+// A generic data flow analysis, specialized for forward analysis.
+class ForwardDataFlowAnalysis : public DataFlowAnalysis {
+ public:
+ // Indicates where labels should be in the worklist RPO ordering.
+ enum class LabelPosition {
+ // Labels should be placed at the beginning of their blocks.
+ kLabelsAtBeginning,
+ // Labels should be placed at the end of their blocks.
+ kLabelsAtEnd,
+ // Labels should not be in the worklist.
+ kNoLabels,
+ // Only labels should be placed in the worklist.
+ kLabelsOnly,
+ };
+
+ ForwardDataFlowAnalysis(IRContext& context, LabelPosition label_position)
+ : DataFlowAnalysis(context), label_position_(label_position) {}
+
+ protected:
+ // Initializes the worklist in reverse postorder, regardless of
+ // |is_first_iteration|. Labels are placed according to the label position
+ // specified in the constructor.
+ void InitializeWorklist(Function* function, bool is_first_iteration) override;
+
+ // Enqueues the users and block successors of the given instruction.
+ // See |EnqueueUsers| and |EnqueueBlockSuccessors|.
+ void EnqueueSuccessors(Instruction* inst) override {
+ EnqueueUsers(inst);
+ EnqueueBlockSuccessors(inst);
+ }
+
+ // Enqueues the users of the given instruction.
+ void EnqueueUsers(Instruction* inst);
+
+ // Enqueues the labels of the successors of the block corresponding to the
+ // given label instruction. Does nothing for other instructions.
+ void EnqueueBlockSuccessors(Instruction* inst);
+
+ private:
+ LabelPosition label_position_;
+};
+
+} // namespace opt
+} // namespace spvtools
+
+#endif // SOURCE_OPT_DATAFLOW_H_
diff --git a/source/opt/dead_branch_elim_pass.cpp b/source/opt/dead_branch_elim_pass.cpp
index 0054f576..356dbcb3 100644
--- a/source/opt/dead_branch_elim_pass.cpp
+++ b/source/opt/dead_branch_elim_pass.cpp
@@ -346,6 +346,7 @@ bool DeadBranchElimPass::FixPhiNodesInLiveBlocks(
if (operands.size() == 4) {
// First input data operands is at index 2.
uint32_t replId = operands[2u].words[0];
+ context()->KillNamesAndDecorates(inst->result_id());
context()->ReplaceAllUsesWith(inst->result_id(), replId);
iter = context()->KillInst(&*inst);
} else {
@@ -419,6 +420,10 @@ bool DeadBranchElimPass::EraseDeadBlocks(
}
bool DeadBranchElimPass::EliminateDeadBranches(Function* func) {
+ if (func->IsDeclaration()) {
+ return false;
+ }
+
bool modified = false;
std::unordered_set<BasicBlock*> live_blocks;
modified |= MarkLiveBlocks(func, &live_blocks);
diff --git a/source/opt/dead_branch_elim_pass.h b/source/opt/dead_branch_elim_pass.h
index 7841bc47..198bad2d 100644
--- a/source/opt/dead_branch_elim_pass.h
+++ b/source/opt/dead_branch_elim_pass.h
@@ -98,7 +98,7 @@ class DeadBranchElimPass : public MemPass {
// Fix phis in reachable blocks so that only live (or unremovable) incoming
// edges are present. If the block now only has a single live incoming edge,
// remove the phi and replace its uses with its data input. If the single
- // remaining incoming edge is from the phi itself, the the phi is in an
+ // remaining incoming edge is from the phi itself, the phi is in an
// unreachable single block loop. Either the block is dead and will be
// removed, or it's reachable from an unreachable continue target. In the
// latter case that continue target block will be collapsed into a block that
@@ -158,7 +158,7 @@ class DeadBranchElimPass : public MemPass {
uint32_t cont_id, uint32_t header_id, uint32_t merge_id,
std::unordered_set<BasicBlock*>* blocks_with_back_edges);
- // Returns true if there is a brach to the merge node of the selection
+ // Returns true if there is a branch to the merge node of the selection
// construct |switch_header_id| that is inside a nested selection construct or
// in the header of the nested selection construct.
bool SwitchHasNestedBreak(uint32_t switch_header_id);
diff --git a/source/opt/dead_insert_elim_pass.cpp b/source/opt/dead_insert_elim_pass.cpp
index 46f4f124..d877f0f9 100644
--- a/source/opt/dead_insert_elim_pass.cpp
+++ b/source/opt/dead_insert_elim_pass.cpp
@@ -196,7 +196,7 @@ bool DeadInsertElimPass::EliminateDeadInsertsOnePass(Function* func) {
}
const uint32_t id = ii->result_id();
get_def_use_mgr()->ForEachUser(id, [&ii, this](Instruction* user) {
- if (user->IsOpenCL100DebugInstr()) return;
+ if (user->IsCommonDebugInstr()) return;
switch (user->opcode()) {
case SpvOpCompositeInsert:
case SpvOpPhi:
@@ -256,7 +256,7 @@ Pass::Status DeadInsertElimPass::Process() {
ProcessFunction pfn = [this](Function* fp) {
return EliminateDeadInserts(fp);
};
- bool modified = context()->ProcessEntryPointCallTree(pfn);
+ bool modified = context()->ProcessReachableCallTree(pfn);
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp
index e782641f..c1df6258 100644
--- a/source/opt/debug_info_manager.cpp
+++ b/source/opt/debug_info_manager.cpp
@@ -18,12 +18,15 @@
#include "source/opt/ir_context.h"
-// Constants for OpenCL.DebugInfo.100 extension instructions.
+// Constants for OpenCL.DebugInfo.100 & NonSemantic.Shader.DebugInfo.100
+// extension instructions.
static const uint32_t kOpLineOperandLineIndex = 1;
static const uint32_t kLineOperandIndexDebugFunction = 7;
static const uint32_t kLineOperandIndexDebugLexicalBlock = 5;
static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
+static const uint32_t kDebugFunctionDefinitionOperandDebugFunctionIndex = 4;
+static const uint32_t kDebugFunctionDefinitionOperandOpFunctionIndex = 5;
static const uint32_t kDebugFunctionOperandParentIndex = 9;
static const uint32_t kDebugTypeCompositeOperandParentIndex = 9;
static const uint32_t kDebugLexicalBlockOperandParentIndex = 7;
@@ -46,8 +49,8 @@ namespace {
void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) {
assert(dbg_inlined_at);
- assert(dbg_inlined_at->GetOpenCL100DebugOpcode() ==
- OpenCLDebugInfo100DebugInlinedAt);
+ assert(dbg_inlined_at->GetCommonDebugOpcode() ==
+ CommonDebugInfoDebugInlinedAt);
if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) {
dbg_inlined_at->AddOperand(
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inlined_operand}});
@@ -59,8 +62,8 @@ void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) {
uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) {
assert(dbg_inlined_at);
- assert(dbg_inlined_at->GetOpenCL100DebugOpcode() ==
- OpenCLDebugInfo100DebugInlinedAt);
+ assert(dbg_inlined_at->GetCommonDebugOpcode() ==
+ CommonDebugInfoDebugInlinedAt);
if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex)
return kNoInlinedAt;
return dbg_inlined_at->GetSingleWordOperand(
@@ -68,8 +71,7 @@ uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) {
}
bool IsEmptyDebugExpression(Instruction* instr) {
- return instr->GetOpenCL100DebugOpcode() ==
- OpenCLDebugInfo100DebugExpression &&
+ return (instr->GetCommonDebugOpcode() == CommonDebugInfoDebugExpression) &&
instr->NumOperands() == kDebugExpressOperandOperationIndex;
}
@@ -79,43 +81,63 @@ DebugInfoManager::DebugInfoManager(IRContext* c) : context_(c) {
AnalyzeDebugInsts(*c->module());
}
+uint32_t DebugInfoManager::GetDbgSetImportId() {
+ uint32_t setId =
+ context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo();
+ if (setId == 0) {
+ setId =
+ context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo();
+ }
+ return setId;
+}
+
Instruction* DebugInfoManager::GetDbgInst(uint32_t id) {
auto dbg_inst_it = id_to_dbg_inst_.find(id);
return dbg_inst_it == id_to_dbg_inst_.end() ? nullptr : dbg_inst_it->second;
}
void DebugInfoManager::RegisterDbgInst(Instruction* inst) {
- assert(
- inst->NumInOperands() != 0 &&
- context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() ==
- inst->GetInOperand(0).words[0] &&
- "Given instruction is not a debug instruction");
+ assert(inst->NumInOperands() != 0 &&
+ (GetDbgSetImportId() == inst->GetInOperand(0).words[0]) &&
+ "Given instruction is not a debug instruction");
id_to_dbg_inst_[inst->result_id()] = inst;
}
void DebugInfoManager::RegisterDbgFunction(Instruction* inst) {
- assert(inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction &&
- "inst is not a DebugFunction");
- auto fn_id = inst->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex);
- // Do not register function that has been optimized away
- auto fn_inst = GetDbgInst(fn_id);
- if (fn_inst != nullptr) {
- assert(GetDbgInst(fn_id)->GetOpenCL100DebugOpcode() ==
- OpenCLDebugInfo100DebugInfoNone);
- return;
+ if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) {
+ auto fn_id = inst->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex);
+ // Do not register function that has been optimized away.
+ auto fn_inst = GetDbgInst(fn_id);
+ if (fn_inst != nullptr) {
+ assert(GetDbgInst(fn_id)->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugInfoNone);
+ return;
+ }
+ assert(
+ fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() &&
+ "Register DebugFunction for a function that already has DebugFunction");
+ fn_id_to_dbg_fn_[fn_id] = inst;
+ } else if (inst->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
+ auto fn_id = inst->GetSingleWordOperand(
+ kDebugFunctionDefinitionOperandOpFunctionIndex);
+ auto fn_inst = GetDbgInst(inst->GetSingleWordOperand(
+ kDebugFunctionDefinitionOperandDebugFunctionIndex));
+ assert(fn_inst && fn_inst->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugFunction);
+ assert(fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() &&
+ "Register DebugFunctionDefinition for a function that already has "
+ "DebugFunctionDefinition");
+ fn_id_to_dbg_fn_[fn_id] = fn_inst;
+ } else {
+ assert(false && "inst is not a DebugFunction");
}
- assert(
- fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() &&
- "Register DebugFunction for a function that already has DebugFunction");
- fn_id_to_dbg_fn_[fn_id] = inst;
}
void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id,
Instruction* dbg_declare) {
- assert(dbg_declare->GetOpenCL100DebugOpcode() ==
- OpenCLDebugInfo100DebugDeclare ||
- dbg_declare->GetOpenCL100DebugOpcode() ==
- OpenCLDebugInfo100DebugValue);
+ assert(dbg_declare->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare ||
+ dbg_declare->GetCommonDebugOpcode() == CommonDebugInfoDebugValue);
auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_id);
if (dbg_decl_itr == var_id_to_dbg_decl_.end()) {
var_id_to_dbg_decl_[var_id] = {dbg_declare};
@@ -124,29 +146,57 @@ void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id,
}
}
+// Create new constant directly into global value area, bypassing the
+// Constant manager. This is used when the DefUse or Constant managers
+// are invalid and cannot be regenerated due to the module being in an
+// inconsistent state e.g. in the middle of significant modification
+// such as inlining. Invalidate Constant and DefUse managers if used.
+uint32_t AddNewConstInGlobals(IRContext* context, uint32_t const_value) {
+ uint32_t id = context->TakeNextId();
+ std::unique_ptr<Instruction> new_const(new Instruction(
+ context, SpvOpConstant, context->get_type_mgr()->GetUIntTypeId(), id,
+ {
+ {spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER,
+ {const_value}},
+ }));
+ context->module()->AddGlobalValue(std::move(new_const));
+ context->InvalidateAnalyses(IRContext::kAnalysisConstants);
+ context->InvalidateAnalyses(IRContext::kAnalysisDefUse);
+ return id;
+}
+
uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
const DebugScope& scope) {
- if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() ==
- 0)
- return kNoInlinedAt;
+ uint32_t setId = GetDbgSetImportId();
+
+ if (setId == 0) return kNoInlinedAt;
+
+ spv_operand_type_t line_number_type =
+ spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER;
+
+ // In NonSemantic.Shader.DebugInfo.100, all constants are IDs of OpConstant,
+ // not literals.
+ if (setId ==
+ context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo())
+ line_number_type = spv_operand_type_t::SPV_OPERAND_TYPE_ID;
uint32_t line_number = 0;
if (line == nullptr) {
auto* lexical_scope_inst = GetDbgInst(scope.GetLexicalScope());
if (lexical_scope_inst == nullptr) return kNoInlinedAt;
- OpenCLDebugInfo100Instructions debug_opcode =
- lexical_scope_inst->GetOpenCL100DebugOpcode();
+ CommonDebugInfoInstructions debug_opcode =
+ lexical_scope_inst->GetCommonDebugOpcode();
switch (debug_opcode) {
- case OpenCLDebugInfo100DebugFunction:
+ case CommonDebugInfoDebugFunction:
line_number = lexical_scope_inst->GetSingleWordOperand(
kLineOperandIndexDebugFunction);
break;
- case OpenCLDebugInfo100DebugLexicalBlock:
+ case CommonDebugInfoDebugLexicalBlock:
line_number = lexical_scope_inst->GetSingleWordOperand(
kLineOperandIndexDebugLexicalBlock);
break;
- case OpenCLDebugInfo100DebugTypeComposite:
- case OpenCLDebugInfo100DebugCompilationUnit:
+ case CommonDebugInfoDebugTypeComposite:
+ case CommonDebugInfoDebugCompilationUnit:
assert(false &&
"DebugTypeComposite and DebugCompilationUnit are lexical "
"scopes, but we inline functions into a function or a block "
@@ -161,6 +211,21 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
}
} else {
line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex);
+
+ // If we need the line number as an ID, generate that constant now.
+ // If Constant or DefUse managers are invalid, generate constant
+ // directly into the global value section of the module; do not
+ // use Constant manager which may attempt to invoke building of the
+ // DefUse manager which cannot be done during inlining. The extra
+ // constants that may be generated here is likely not significant
+ // and will likely be cleaned up in later passes.
+ if (line_number_type == spv_operand_type_t::SPV_OPERAND_TYPE_ID) {
+ if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse) ||
+ !context()->AreAnalysesValid(IRContext::Analysis::kAnalysisConstants))
+ line_number = AddNewConstInGlobals(context(), line_number);
+ else
+ line_number = context()->get_constant_mgr()->GetUIntConst(line_number);
+ }
}
uint32_t result_id = context()->TakeNextId();
@@ -168,13 +233,10 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
result_id,
{
- {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
- {context()
- ->get_feature_mgr()
- ->GetExtInstImportId_OpenCL100DebugInfo()}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {setId}},
{spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
- {static_cast<uint32_t>(OpenCLDebugInfo100DebugInlinedAt)}},
- {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}},
+ {static_cast<uint32_t>(CommonDebugInfoDebugInlinedAt)}},
+ {line_number_type, {line_number}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetLexicalScope()}},
}));
// |scope| already has DebugInlinedAt. We put the existing DebugInlinedAt
@@ -257,19 +319,34 @@ Instruction* DebugInfoManager::GetDebugOperationWithDeref() {
if (deref_operation_ != nullptr) return deref_operation_;
uint32_t result_id = context()->TakeNextId();
- std::unique_ptr<Instruction> deref_operation(new Instruction(
- context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
- result_id,
- {
- {SPV_OPERAND_TYPE_ID,
- {context()
- ->get_feature_mgr()
- ->GetExtInstImportId_OpenCL100DebugInfo()}},
- {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
- {static_cast<uint32_t>(OpenCLDebugInfo100DebugOperation)}},
- {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION,
- {static_cast<uint32_t>(OpenCLDebugInfo100Deref)}},
- }));
+ std::unique_ptr<Instruction> deref_operation;
+
+ if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) {
+ deref_operation = std::unique_ptr<Instruction>(new Instruction(
+ context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+ result_id,
+ {
+ {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}},
+ {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+ {static_cast<uint32_t>(OpenCLDebugInfo100DebugOperation)}},
+ {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION,
+ {static_cast<uint32_t>(OpenCLDebugInfo100Deref)}},
+ }));
+ } else {
+ uint32_t deref_id = context()->get_constant_mgr()->GetUIntConst(
+ NonSemanticShaderDebugInfo100Deref);
+
+ deref_operation = std::unique_ptr<Instruction>(
+ new Instruction(context(), SpvOpExtInst,
+ context()->get_type_mgr()->GetVoidTypeId(), result_id,
+ {
+ {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}},
+ {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+ {static_cast<uint32_t>(
+ NonSemanticShaderDebugInfo100DebugOperation)}},
+ {SPV_OPERAND_TYPE_ID, {deref_id}},
+ }));
+ }
// Add to the front of |ext_inst_debuginfo_|.
deref_operation_ =
@@ -283,8 +360,7 @@ Instruction* DebugInfoManager::GetDebugOperationWithDeref() {
}
Instruction* DebugInfoManager::DerefDebugExpression(Instruction* dbg_expr) {
- assert(dbg_expr->GetOpenCL100DebugOpcode() ==
- OpenCLDebugInfo100DebugExpression);
+ assert(dbg_expr->GetCommonDebugOpcode() == CommonDebugInfoDebugExpression);
std::unique_ptr<Instruction> deref_expr(dbg_expr->Clone(context()));
deref_expr->SetResultId(context()->TakeNextId());
deref_expr->InsertOperand(
@@ -306,12 +382,9 @@ Instruction* DebugInfoManager::GetDebugInfoNone() {
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
result_id,
{
- {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
- {context()
- ->get_feature_mgr()
- ->GetExtInstImportId_OpenCL100DebugInfo()}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}},
{spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
- {static_cast<uint32_t>(OpenCLDebugInfo100DebugInfoNone)}},
+ {static_cast<uint32_t>(CommonDebugInfoDebugInfoNone)}},
}));
// Add to the front of |ext_inst_debuginfo_|.
@@ -333,12 +406,9 @@ Instruction* DebugInfoManager::GetEmptyDebugExpression() {
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
result_id,
{
- {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
- {context()
- ->get_feature_mgr()
- ->GetExtInstImportId_OpenCL100DebugInfo()}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}},
{spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
- {static_cast<uint32_t>(OpenCLDebugInfo100DebugExpression)}},
+ {static_cast<uint32_t>(CommonDebugInfoDebugExpression)}},
}));
// Add to the front of |ext_inst_debuginfo_|.
@@ -355,8 +425,7 @@ Instruction* DebugInfoManager::GetEmptyDebugExpression() {
Instruction* DebugInfoManager::GetDebugInlinedAt(uint32_t dbg_inlined_at_id) {
auto* inlined_at = GetDbgInst(dbg_inlined_at_id);
if (inlined_at == nullptr) return nullptr;
- if (inlined_at->GetOpenCL100DebugOpcode() !=
- OpenCLDebugInfo100DebugInlinedAt) {
+ if (inlined_at->GetCommonDebugOpcode() != CommonDebugInfoDebugInlinedAt) {
return nullptr;
}
return inlined_at;
@@ -403,23 +472,23 @@ bool DebugInfoManager::KillDebugDeclares(uint32_t variable_id) {
uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) {
auto dbg_scope_itr = id_to_dbg_inst_.find(child_scope);
assert(dbg_scope_itr != id_to_dbg_inst_.end());
- OpenCLDebugInfo100Instructions debug_opcode =
- dbg_scope_itr->second->GetOpenCL100DebugOpcode();
+ CommonDebugInfoInstructions debug_opcode =
+ dbg_scope_itr->second->GetCommonDebugOpcode();
uint32_t parent_scope = kNoDebugScope;
switch (debug_opcode) {
- case OpenCLDebugInfo100DebugFunction:
+ case CommonDebugInfoDebugFunction:
parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
kDebugFunctionOperandParentIndex);
break;
- case OpenCLDebugInfo100DebugLexicalBlock:
+ case CommonDebugInfoDebugLexicalBlock:
parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
kDebugLexicalBlockOperandParentIndex);
break;
- case OpenCLDebugInfo100DebugTypeComposite:
+ case CommonDebugInfoDebugTypeComposite:
parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
kDebugTypeCompositeOperandParentIndex);
break;
- case OpenCLDebugInfo100DebugCompilationUnit:
+ case CommonDebugInfoDebugCompilationUnit:
// DebugCompilationUnit does not have a parent scope.
break;
default:
@@ -513,8 +582,7 @@ Instruction* DebugInfoManager::AddDebugValueForDecl(
std::unique_ptr<Instruction> dbg_val(dbg_decl->Clone(context()));
dbg_val->SetResultId(context()->TakeNextId());
- dbg_val->SetInOperand(kExtInstInstructionInIdx,
- {OpenCLDebugInfo100DebugValue});
+ dbg_val->SetInOperand(kExtInstInstructionInIdx, {CommonDebugInfoDebugValue});
dbg_val->SetOperand(kDebugDeclareOperandVariableIndex, {value_id});
dbg_val->SetOperand(kDebugValueOperandExpressionIndex,
{GetEmptyDebugExpression()->result_id()});
@@ -532,9 +600,20 @@ Instruction* DebugInfoManager::AddDebugValueForDecl(
return added_dbg_val;
}
+uint32_t DebugInfoManager::GetVulkanDebugOperation(Instruction* inst) {
+ assert(inst->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugOperation &&
+ "inst must be Vulkan DebugOperation");
+ return context()
+ ->get_constant_mgr()
+ ->GetConstantFromInst(context()->get_def_use_mgr()->GetDef(
+ inst->GetSingleWordOperand(kDebugOperationOperandOperationIndex)))
+ ->GetU32();
+}
+
uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare(
Instruction* inst) {
- if (inst->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugValue) return 0;
+ if (inst->GetCommonDebugOpcode() != CommonDebugInfoDebugValue) return 0;
auto* expr =
GetDbgInst(inst->GetSingleWordOperand(kDebugValueOperandExpressionIndex));
@@ -544,9 +623,19 @@ uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare(
auto* operation = GetDbgInst(
expr->GetSingleWordOperand(kDebugExpressOperandOperationIndex));
if (operation == nullptr) return 0;
- if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) !=
- OpenCLDebugInfo100Deref) {
- return 0;
+
+ // OpenCL.DebugInfo.100 contains a literal for the operation, Vulkan uses an
+ // OpConstant.
+ if (inst->IsOpenCL100DebugInstr()) {
+ if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) !=
+ OpenCLDebugInfo100Deref) {
+ return 0;
+ }
+ } else {
+ uint32_t operation_const = GetVulkanDebugOperation(operation);
+ if (operation_const != NonSemanticShaderDebugInfo100Deref) {
+ return 0;
+ }
}
uint32_t var_id =
@@ -567,8 +656,8 @@ uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare(
}
bool DebugInfoManager::IsDebugDeclare(Instruction* instr) {
- if (!instr->IsOpenCL100DebugInstr()) return false;
- return instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare ||
+ if (!instr->IsCommonDebugInstr()) return false;
+ return instr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare ||
GetVariableIdOfDebugValueUsedForDeclare(instr) != 0;
}
@@ -615,14 +704,13 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) {
users.insert(inst);
}
- if (!inst->IsOpenCL100DebugInstr()) return;
+ if (!inst->IsCommonDebugInstr()) return;
RegisterDbgInst(inst);
- if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) {
- assert(GetDebugFunction(inst->GetSingleWordOperand(
- kDebugFunctionOperandFunctionIndex)) == nullptr &&
- "Two DebugFunction instruction exists for a single OpFunction.");
+ if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction ||
+ inst->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
RegisterDbgFunction(inst);
}
@@ -633,8 +721,17 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) {
deref_operation_ = inst;
}
+ if (deref_operation_ == nullptr &&
+ inst->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugOperation) {
+ uint32_t operation_const = GetVulkanDebugOperation(inst);
+ if (operation_const == NonSemanticShaderDebugInfo100Deref) {
+ deref_operation_ = inst;
+ }
+ }
+
if (debug_info_none_inst_ == nullptr &&
- inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
+ inst->GetCommonDebugOpcode() == CommonDebugInfoDebugInfoNone) {
debug_info_none_inst_ = inst;
}
@@ -642,7 +739,7 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) {
empty_debug_expr_inst_ = inst;
}
- if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) {
+ if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) {
uint32_t var_id =
inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
RegisterDbgDeclare(var_id, inst);
@@ -655,8 +752,8 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) {
void DebugInfoManager::ConvertDebugGlobalToLocalVariable(
Instruction* dbg_global_var, Instruction* local_var) {
- if (dbg_global_var->GetOpenCL100DebugOpcode() !=
- OpenCLDebugInfo100DebugGlobalVariable) {
+ if (dbg_global_var->GetCommonDebugOpcode() !=
+ CommonDebugInfoDebugGlobalVariable) {
return;
}
assert(local_var->opcode() == SpvOpVariable ||
@@ -664,7 +761,7 @@ void DebugInfoManager::ConvertDebugGlobalToLocalVariable(
// Convert |dbg_global_var| to DebugLocalVariable
dbg_global_var->SetInOperand(kExtInstInstructionInIdx,
- {OpenCLDebugInfo100DebugLocalVariable});
+ {CommonDebugInfoDebugLocalVariable});
auto flags = dbg_global_var->GetSingleWordOperand(
kDebugGlobalVariableOperandFlagsIndex);
for (uint32_t i = dbg_global_var->NumInOperands() - 1;
@@ -680,20 +777,20 @@ void DebugInfoManager::ConvertDebugGlobalToLocalVariable(
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
context()->TakeNextId(),
{
- {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
- {context()
- ->get_feature_mgr()
- ->GetExtInstImportId_OpenCL100DebugInfo()}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}},
{spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
- {static_cast<uint32_t>(OpenCLDebugInfo100DebugDeclare)}},
+ {static_cast<uint32_t>(CommonDebugInfoDebugDeclare)}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{dbg_global_var->result_id()}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {local_var->result_id()}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{GetEmptyDebugExpression()->result_id()}},
}));
- auto* added_dbg_decl =
- local_var->NextNode()->InsertBefore(std::move(new_dbg_decl));
+ // Must insert after all OpVariables in block
+ Instruction* insert_before = local_var;
+ while (insert_before->opcode() == SpvOpVariable)
+ insert_before = insert_before->NextNode();
+ auto* added_dbg_decl = insert_before->InsertBefore(std::move(new_dbg_decl));
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_decl);
if (context()->AreAnalysesValid(
@@ -713,7 +810,7 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
// list.
if (empty_debug_expr_inst_ != nullptr &&
empty_debug_expr_inst_->PreviousNode() != nullptr &&
- empty_debug_expr_inst_->PreviousNode()->IsOpenCL100DebugInstr()) {
+ empty_debug_expr_inst_->PreviousNode()->IsCommonDebugInstr()) {
empty_debug_expr_inst_->InsertBefore(
&*context()->module()->ext_inst_debuginfo_begin());
}
@@ -722,7 +819,7 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
// list.
if (debug_info_none_inst_ != nullptr &&
debug_info_none_inst_->PreviousNode() != nullptr &&
- debug_info_none_inst_->PreviousNode()->IsOpenCL100DebugInstr()) {
+ debug_info_none_inst_->PreviousNode()->IsCommonDebugInstr()) {
debug_info_none_inst_->InsertBefore(
&*context()->module()->ext_inst_debuginfo_begin());
}
@@ -740,7 +837,7 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
inlinedat_id_to_users_itr->second.erase(instr);
}
- if (instr == nullptr || !instr->IsOpenCL100DebugInstr()) {
+ if (instr == nullptr || !instr->IsCommonDebugInstr()) {
return;
}
@@ -751,9 +848,15 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex);
fn_id_to_dbg_fn_.erase(fn_id);
}
+ if (instr->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugFunction) {
+ auto fn_id = instr->GetSingleWordOperand(
+ kDebugFunctionDefinitionOperandOpFunctionIndex);
+ fn_id_to_dbg_fn_.erase(fn_id);
+ }
- if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare ||
- instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) {
+ if (instr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare ||
+ instr->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) {
auto var_or_value_id =
instr->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_or_value_id);
@@ -767,6 +870,8 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
++dbg_instr_itr) {
+ // OpenCL.DebugInfo.100 contains the operation as a literal operand, in
+ // Vulkan it's referenced as an OpConstant.
if (instr != &*dbg_instr_itr &&
dbg_instr_itr->GetOpenCL100DebugOpcode() ==
OpenCLDebugInfo100DebugOperation &&
@@ -775,6 +880,14 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
OpenCLDebugInfo100Deref) {
deref_operation_ = &*dbg_instr_itr;
break;
+ } else if (instr != &*dbg_instr_itr &&
+ dbg_instr_itr->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugOperation) {
+ uint32_t operation_const = GetVulkanDebugOperation(&*dbg_instr_itr);
+ if (operation_const == NonSemanticShaderDebugInfo100Deref) {
+ deref_operation_ = &*dbg_instr_itr;
+ break;
+ }
}
}
}
@@ -784,9 +897,8 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
++dbg_instr_itr) {
- if (instr != &*dbg_instr_itr &&
- dbg_instr_itr->GetOpenCL100DebugOpcode() ==
- OpenCLDebugInfo100DebugInfoNone) {
+ if (instr != &*dbg_instr_itr && dbg_instr_itr->GetCommonDebugOpcode() ==
+ CommonDebugInfoDebugInfoNone) {
debug_info_none_inst_ = &*dbg_instr_itr;
break;
}
diff --git a/source/opt/debug_info_manager.h b/source/opt/debug_info_manager.h
index 776e9baa..df34b30f 100644
--- a/source/opt/debug_info_manager.h
+++ b/source/opt/debug_info_manager.h
@@ -67,8 +67,8 @@ class DebugInlinedAtContext {
std::unordered_map<uint32_t, uint32_t> callee_inlined_at2chain_;
};
-// A class for analyzing, managing, and creating OpenCL.DebugInfo.100 extension
-// instructions.
+// A class for analyzing, managing, and creating OpenCL.DebugInfo.100 and
+// NonSemantic.Shader.DebugInfo.100 extension instructions.
class DebugInfoManager {
public:
// Constructs a debug information manager from the given |context|.
@@ -85,7 +85,7 @@ class DebugInfoManager {
return !(lhs == rhs);
}
- // Analyzes OpenCL.DebugInfo.100 instruction |dbg_inst|.
+ // Analyzes DebugInfo instruction |dbg_inst|.
void AnalyzeDebugInst(Instruction* dbg_inst);
// Creates new DebugInlinedAt and returns its id. Its line operand is the
@@ -164,6 +164,9 @@ class DebugInfoManager {
// Erases |instr| from data structures of this class.
void ClearDebugInfo(Instruction* instr);
+ // Return the opcode for the Vulkan DebugOperation inst
+ uint32_t GetVulkanDebugOperation(Instruction* inst);
+
// Returns the id of Value operand if |inst| is DebugValue who has Deref
// operation and its Value operand is a result id of OpVariable with
// Function storage class. Otherwise, returns 0.
@@ -190,10 +193,13 @@ class DebugInfoManager {
private:
IRContext* context() { return context_; }
- // Analyzes OpenCL.DebugInfo.100 instructions in the given |module| and
+ // Analyzes DebugInfo instructions in the given |module| and
// populates data structures in this class.
void AnalyzeDebugInsts(Module& module);
+ // Get the DebugInfo ExtInstImport Id, or 0 if no DebugInfo is available.
+ uint32_t GetDbgSetImportId();
+
// Returns the debug instruction whose id is |id|. Returns |nullptr| if one
// does not exists.
Instruction* GetDbgInst(uint32_t id);
@@ -230,7 +236,7 @@ class DebugInfoManager {
IRContext* context_;
- // Mapping from ids of OpenCL.DebugInfo.100 extension instructions
+ // Mapping from ids of DebugInfo extension instructions.
// to their Instruction instances.
std::unordered_map<uint32_t, Instruction*> id_to_dbg_inst_;
diff --git a/source/opt/decoration_manager.cpp b/source/opt/decoration_manager.cpp
index 4bf026ef..2146c359 100644
--- a/source/opt/decoration_manager.cpp
+++ b/source/opt/decoration_manager.cpp
@@ -490,6 +490,14 @@ void DecorationManager::ForEachDecoration(
});
}
+bool DecorationManager::HasDecoration(uint32_t id, uint32_t decoration) {
+ bool has_decoration = false;
+ ForEachDecoration(id, decoration, [&has_decoration](const Instruction&) {
+ has_decoration = true;
+ });
+ return has_decoration;
+}
+
bool DecorationManager::FindDecoration(
uint32_t id, uint32_t decoration,
std::function<bool(const Instruction&)> f) {
diff --git a/source/opt/decoration_manager.h b/source/opt/decoration_manager.h
index b753e6be..fe78f2ce 100644
--- a/source/opt/decoration_manager.h
+++ b/source/opt/decoration_manager.h
@@ -90,6 +90,10 @@ class DecorationManager {
bool AreDecorationsTheSame(const Instruction* inst1, const Instruction* inst2,
bool ignore_target) const;
+ // Returns whether a decoration instruction for |id| with decoration
+ // |decoration| exists or not.
+ bool HasDecoration(uint32_t id, uint32_t decoration);
+
// |f| is run on each decoration instruction for |id| with decoration
// |decoration|. Processed are all decorations which target |id| either
// directly or indirectly by Decoration Groups.
diff --git a/source/opt/def_use_manager.cpp b/source/opt/def_use_manager.cpp
index 0ec98cae..d54fdb65 100644
--- a/source/opt/def_use_manager.cpp
+++ b/source/opt/def_use_manager.cpp
@@ -14,11 +14,6 @@
#include "source/opt/def_use_manager.h"
-#include <iostream>
-
-#include "source/opt/log.h"
-#include "source/opt/reflect.h"
-
namespace spvtools {
namespace opt {
namespace analysis {
@@ -59,7 +54,7 @@ void DefUseManager::AnalyzeInstUse(Instruction* inst) {
uint32_t use_id = inst->GetSingleWordOperand(i);
Instruction* def = GetDef(use_id);
assert(def && "Definition is not registered.");
- id_to_users_.insert(UserEntry(def, inst));
+ id_to_users_.insert(UserEntry{def, inst});
used_ids->push_back(use_id);
} break;
default:
@@ -71,6 +66,9 @@ void DefUseManager::AnalyzeInstUse(Instruction* inst) {
void DefUseManager::AnalyzeInstDefUse(Instruction* inst) {
AnalyzeInstDef(inst);
AnalyzeInstUse(inst);
+ // Analyze lines last otherwise they will be cleared when inst is
+ // cleared by preceding two calls
+ for (auto& l_inst : inst->dbg_line_insts()) AnalyzeInstDefUse(&l_inst);
}
void DefUseManager::UpdateDefUse(Instruction* inst) {
@@ -99,13 +97,13 @@ const Instruction* DefUseManager::GetDef(uint32_t id) const {
DefUseManager::IdToUsersMap::const_iterator DefUseManager::UsersBegin(
const Instruction* def) const {
return id_to_users_.lower_bound(
- UserEntry(const_cast<Instruction*>(def), nullptr));
+ UserEntry{const_cast<Instruction*>(def), nullptr});
}
bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter,
const IdToUsersMap::const_iterator& cached_end,
const Instruction* inst) const {
- return (iter != cached_end && iter->first == inst);
+ return (iter != cached_end && iter->def == inst);
}
bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter,
@@ -122,7 +120,7 @@ bool DefUseManager::WhileEachUser(
auto end = id_to_users_.end();
for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) {
- if (!f(iter->second)) return false;
+ if (!f(iter->user)) return false;
}
return true;
}
@@ -155,7 +153,7 @@ bool DefUseManager::WhileEachUse(
auto end = id_to_users_.end();
for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) {
- Instruction* user = iter->second;
+ Instruction* user = iter->user;
for (uint32_t idx = 0; idx != user->NumOperands(); ++idx) {
const Operand& op = user->GetOperand(idx);
if (op.type != SPV_OPERAND_TYPE_RESULT_ID && spvIsIdType(op.type)) {
@@ -224,9 +222,11 @@ void DefUseManager::AnalyzeDefUse(Module* module) {
if (!module) return;
// Analyze all the defs before any uses to catch forward references.
module->ForEachInst(
- std::bind(&DefUseManager::AnalyzeInstDef, this, std::placeholders::_1));
+ std::bind(&DefUseManager::AnalyzeInstDef, this, std::placeholders::_1),
+ true);
module->ForEachInst(
- std::bind(&DefUseManager::AnalyzeInstUse, this, std::placeholders::_1));
+ std::bind(&DefUseManager::AnalyzeInstUse, this, std::placeholders::_1),
+ true);
}
void DefUseManager::ClearInst(Instruction* inst) {
@@ -253,45 +253,59 @@ void DefUseManager::EraseUseRecordsOfOperandIds(const Instruction* inst) {
if (iter != inst_to_used_ids_.end()) {
for (auto use_id : iter->second) {
id_to_users_.erase(
- UserEntry(GetDef(use_id), const_cast<Instruction*>(inst)));
+ UserEntry{GetDef(use_id), const_cast<Instruction*>(inst)});
}
- inst_to_used_ids_.erase(inst);
+ inst_to_used_ids_.erase(iter);
}
}
-bool operator==(const DefUseManager& lhs, const DefUseManager& rhs) {
+bool CompareAndPrintDifferences(const DefUseManager& lhs,
+ const DefUseManager& rhs) {
+ bool same = true;
+
if (lhs.id_to_def_ != rhs.id_to_def_) {
- return false;
+ for (auto p : lhs.id_to_def_) {
+ if (rhs.id_to_def_.find(p.first) == rhs.id_to_def_.end()) {
+ printf("Diff in id_to_def: missing value in rhs\n");
+ }
+ }
+ for (auto p : rhs.id_to_def_) {
+ if (lhs.id_to_def_.find(p.first) == lhs.id_to_def_.end()) {
+ printf("Diff in id_to_def: missing value in lhs\n");
+ }
+ }
+ same = false;
}
if (lhs.id_to_users_ != rhs.id_to_users_) {
for (auto p : lhs.id_to_users_) {
if (rhs.id_to_users_.count(p) == 0) {
- return false;
+ printf("Diff in id_to_users: missing value in rhs\n");
}
}
for (auto p : rhs.id_to_users_) {
if (lhs.id_to_users_.count(p) == 0) {
- return false;
+ printf("Diff in id_to_users: missing value in lhs\n");
}
}
- return false;
+ same = false;
}
if (lhs.inst_to_used_ids_ != rhs.inst_to_used_ids_) {
for (auto p : lhs.inst_to_used_ids_) {
if (rhs.inst_to_used_ids_.count(p.first) == 0) {
- return false;
+ printf("Diff in inst_to_used_ids: missing value in rhs\n");
}
}
for (auto p : rhs.inst_to_used_ids_) {
if (lhs.inst_to_used_ids_.count(p.first) == 0) {
- return false;
+ printf("Diff in inst_to_used_ids: missing value in lhs\n");
}
}
- return false;
+ same = false;
}
- return true;
+
+ return same;
}
} // namespace analysis
diff --git a/source/opt/def_use_manager.h b/source/opt/def_use_manager.h
index 0499e82b..a8dbbc60 100644
--- a/source/opt/def_use_manager.h
+++ b/source/opt/def_use_manager.h
@@ -15,10 +15,8 @@
#ifndef SOURCE_OPT_DEF_USE_MANAGER_H_
#define SOURCE_OPT_DEF_USE_MANAGER_H_
-#include <list>
#include <set>
#include <unordered_map>
-#include <utility>
#include <vector>
#include "source/opt/instruction.h"
@@ -51,15 +49,17 @@ inline bool operator<(const Use& lhs, const Use& rhs) {
return lhs.operand_index < rhs.operand_index;
}
-// Definition and user pair.
-//
-// The first element of the pair is the definition.
-// The second element of the pair is the user.
-//
// Definition should never be null. User can be null, however, such an entry
// should be used only for searching (e.g. all users of a particular definition)
// and never stored in a container.
-using UserEntry = std::pair<Instruction*, Instruction*>;
+struct UserEntry {
+ Instruction* def;
+ Instruction* user;
+};
+
+inline bool operator==(const UserEntry& lhs, const UserEntry& rhs) {
+ return lhs.def == rhs.def && lhs.user == rhs.user;
+}
// Orders UserEntry for use in associative containers (i.e. less than ordering).
//
@@ -72,24 +72,24 @@ using UserEntry = std::pair<Instruction*, Instruction*>;
// definition (i.e. using {def, nullptr}).
struct UserEntryLess {
bool operator()(const UserEntry& lhs, const UserEntry& rhs) const {
- // If lhs.first and rhs.first are both null, fall through to checking the
+ // If lhs.def and rhs.def are both null, fall through to checking the
// second entries.
- if (!lhs.first && rhs.first) return true;
- if (lhs.first && !rhs.first) return false;
+ if (!lhs.def && rhs.def) return true;
+ if (lhs.def && !rhs.def) return false;
// If neither definition is null, then compare unique ids.
- if (lhs.first && rhs.first) {
- if (lhs.first->unique_id() < rhs.first->unique_id()) return true;
- if (rhs.first->unique_id() < lhs.first->unique_id()) return false;
+ if (lhs.def && rhs.def) {
+ if (lhs.def->unique_id() < rhs.def->unique_id()) return true;
+ if (rhs.def->unique_id() < lhs.def->unique_id()) return false;
}
// Return false on equality.
- if (!lhs.second && !rhs.second) return false;
- if (!lhs.second) return true;
- if (!rhs.second) return false;
+ if (!lhs.user && !rhs.user) return false;
+ if (!lhs.user) return true;
+ if (!rhs.user) return false;
// If neither user is null then compare unique ids.
- return lhs.second->unique_id() < rhs.second->unique_id();
+ return lhs.user->unique_id() < rhs.user->unique_id();
}
};
@@ -97,7 +97,6 @@ struct UserEntryLess {
class DefUseManager {
public:
using IdToDefMap = std::unordered_map<uint32_t, Instruction*>;
- using IdToUsersMap = std::set<UserEntry, UserEntryLess>;
// Constructs a def-use manager from the given |module|. All internal messages
// will be communicated to the outside via the given message |consumer|. This
@@ -191,14 +190,12 @@ class DefUseManager {
// Returns the annotation instrunctions which are a direct use of the given
// |id|. This means when the decorations are applied through decoration
// group(s), this function will just return the OpGroupDecorate
- // instrcution(s) which refer to the given id as an operand. The OpDecorate
+ // instruction(s) which refer to the given id as an operand. The OpDecorate
// instructions which decorate the decoration group will not be returned.
std::vector<Instruction*> GetAnnotations(uint32_t id) const;
// Returns the map from ids to their def instructions.
const IdToDefMap& id_to_defs() const { return id_to_def_; }
- // Returns the map from instructions to their users.
- const IdToUsersMap& id_to_users() const { return id_to_users_; }
// Clear the internal def-use record of the given instruction |inst|. This
// method will update the use information of the operand ids of |inst|. The
@@ -210,16 +207,15 @@ class DefUseManager {
// Erases the records that a given instruction uses its operand ids.
void EraseUseRecordsOfOperandIds(const Instruction* inst);
- friend bool operator==(const DefUseManager&, const DefUseManager&);
- friend bool operator!=(const DefUseManager& lhs, const DefUseManager& rhs) {
- return !(lhs == rhs);
- }
+ friend bool CompareAndPrintDifferences(const DefUseManager&,
+ const DefUseManager&);
- // If |inst| has not already been analysed, then analyses its defintion and
+ // If |inst| has not already been analysed, then analyses its definition and
// uses.
void UpdateDefUse(Instruction* inst);
private:
+ using IdToUsersMap = std::set<UserEntry, UserEntryLess>;
using InstToUsedIdsMap =
std::unordered_map<const Instruction*, std::vector<uint32_t>>;
diff --git a/source/opt/desc_sroa.cpp b/source/opt/desc_sroa.cpp
index 5e950069..b130ca80 100644
--- a/source/opt/desc_sroa.cpp
+++ b/source/opt/desc_sroa.cpp
@@ -14,10 +14,19 @@
#include "source/opt/desc_sroa.h"
+#include "source/opt/desc_sroa_util.h"
#include "source/util/string_utils.h"
namespace spvtools {
namespace opt {
+namespace {
+
+bool IsDecorationBinding(Instruction* inst) {
+ if (inst->opcode() != SpvOpDecorate) return false;
+ return inst->GetSingleWordInOperand(1u) == SpvDecorationBinding;
+}
+
+} // namespace
Pass::Status DescriptorScalarReplacement::Process() {
bool modified = false;
@@ -25,7 +34,7 @@ Pass::Status DescriptorScalarReplacement::Process() {
std::vector<Instruction*> vars_to_kill;
for (Instruction& var : context()->types_values()) {
- if (IsCandidate(&var)) {
+ if (descsroautil::IsDescriptorArray(context(), &var)) {
modified = true;
if (!ReplaceCandidate(&var)) {
return Status::Failure;
@@ -41,72 +50,6 @@ Pass::Status DescriptorScalarReplacement::Process() {
return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
}
-bool DescriptorScalarReplacement::IsCandidate(Instruction* var) {
- if (var->opcode() != SpvOpVariable) {
- return false;
- }
-
- uint32_t ptr_type_id = var->type_id();
- Instruction* ptr_type_inst =
- context()->get_def_use_mgr()->GetDef(ptr_type_id);
- if (ptr_type_inst->opcode() != SpvOpTypePointer) {
- return false;
- }
-
- uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1);
- Instruction* var_type_inst =
- context()->get_def_use_mgr()->GetDef(var_type_id);
- if (var_type_inst->opcode() != SpvOpTypeArray &&
- var_type_inst->opcode() != SpvOpTypeStruct) {
- return false;
- }
-
- // All structures with descriptor assignments must be replaced by variables,
- // one for each of their members - with the exceptions of buffers.
- if (IsTypeOfStructuredBuffer(var_type_inst)) {
- return false;
- }
-
- bool has_desc_set_decoration = false;
- context()->get_decoration_mgr()->ForEachDecoration(
- var->result_id(), SpvDecorationDescriptorSet,
- [&has_desc_set_decoration](const Instruction&) {
- has_desc_set_decoration = true;
- });
- if (!has_desc_set_decoration) {
- return false;
- }
-
- bool has_binding_decoration = false;
- context()->get_decoration_mgr()->ForEachDecoration(
- var->result_id(), SpvDecorationBinding,
- [&has_binding_decoration](const Instruction&) {
- has_binding_decoration = true;
- });
- if (!has_binding_decoration) {
- return false;
- }
-
- return true;
-}
-
-bool DescriptorScalarReplacement::IsTypeOfStructuredBuffer(
- const Instruction* type) const {
- if (type->opcode() != SpvOpTypeStruct) {
- return false;
- }
-
- // All buffers have offset decorations for members of their structure types.
- // This is how we distinguish it from a structure of descriptors.
- bool has_offset_decoration = false;
- context()->get_decoration_mgr()->ForEachDecoration(
- type->result_id(), SpvDecorationOffset,
- [&has_offset_decoration](const Instruction&) {
- has_offset_decoration = true;
- });
- return has_offset_decoration;
-}
-
bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) {
std::vector<Instruction*> access_chain_work_list;
std::vector<Instruction*> load_work_list;
@@ -162,21 +105,20 @@ bool DescriptorScalarReplacement::ReplaceAccessChain(Instruction* var,
return false;
}
- uint32_t idx_id = use->GetSingleWordInOperand(1);
- const analysis::Constant* idx_const =
- context()->get_constant_mgr()->FindDeclaredConstant(idx_id);
- if (idx_const == nullptr) {
+ const analysis::Constant* const_index =
+ descsroautil::GetAccessChainIndexAsConst(context(), use);
+ if (const_index == nullptr) {
context()->EmitErrorMessage("Variable cannot be replaced: invalid index",
use);
return false;
}
- uint32_t idx = idx_const->GetU32();
+ uint32_t idx = const_index->GetU32();
uint32_t replacement_var = GetReplacementVariable(var, idx);
if (use->NumInOperands() == 2) {
// We are not indexing into the replacement variable. We can replaces the
- // access chain with the replacement varibale itself.
+ // access chain with the replacement variable itself.
context()->ReplaceAllUsesWith(use->result_id(), replacement_var);
context()->KillInst(use);
return true;
@@ -193,8 +135,8 @@ bool DescriptorScalarReplacement::ReplaceAccessChain(Instruction* var,
// Use the replacement variable as the base address.
new_operands.push_back({SPV_OPERAND_TYPE_ID, {replacement_var}});
- // Drop the first index because it is consumed by the replacment, and copy the
- // rest.
+ // Drop the first index because it is consumed by the replacement, and copy
+ // the rest.
for (uint32_t i = 4; i < use->NumOperands(); i++) {
new_operands.emplace_back(use->GetOperand(i));
}
@@ -208,39 +150,12 @@ uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var,
uint32_t idx) {
auto replacement_vars = replacement_variables_.find(var);
if (replacement_vars == replacement_variables_.end()) {
- uint32_t ptr_type_id = var->type_id();
- Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
- assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
- "Variable should be a pointer to an array or structure.");
- uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
- Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id);
- const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray;
- const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct;
- assert((is_array || is_struct) &&
- "Variable should be a pointer to an array or structure.");
-
- // For arrays, each array element should be replaced with a new replacement
- // variable
- if (is_array) {
- uint32_t array_len_id = pointee_type_inst->GetSingleWordInOperand(1);
- const analysis::Constant* array_len_const =
- context()->get_constant_mgr()->FindDeclaredConstant(array_len_id);
- assert(array_len_const != nullptr && "Array length must be a constant.");
- uint32_t array_len = array_len_const->GetU32();
-
- replacement_vars = replacement_variables_
- .insert({var, std::vector<uint32_t>(array_len, 0)})
- .first;
- }
- // For structures, each member should be replaced with a new replacement
- // variable
- if (is_struct) {
- const uint32_t num_members = pointee_type_inst->NumInOperands();
- replacement_vars =
- replacement_variables_
- .insert({var, std::vector<uint32_t>(num_members, 0)})
- .first;
- }
+ uint32_t number_of_elements =
+ descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var);
+ replacement_vars =
+ replacement_variables_
+ .insert({var, std::vector<uint32_t>(number_of_elements, 0)})
+ .first;
}
if (replacement_vars->second[idx] == 0) {
@@ -250,6 +165,75 @@ uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var,
return replacement_vars->second[idx];
}
+void DescriptorScalarReplacement::CopyDecorationsForNewVariable(
+ Instruction* old_var, uint32_t index, uint32_t new_var_id,
+ uint32_t new_var_ptr_type_id, const bool is_old_var_array,
+ const bool is_old_var_struct, Instruction* old_var_type) {
+ // Handle OpDecorate and OpDecorateString instructions.
+ for (auto old_decoration :
+ get_decoration_mgr()->GetDecorationsFor(old_var->result_id(), true)) {
+ uint32_t new_binding = 0;
+ if (IsDecorationBinding(old_decoration)) {
+ new_binding = GetNewBindingForElement(
+ old_decoration->GetSingleWordInOperand(2), index, new_var_ptr_type_id,
+ is_old_var_array, is_old_var_struct, old_var_type);
+ }
+ CreateNewDecorationForNewVariable(old_decoration, new_var_id, new_binding);
+ }
+
+ // Handle OpMemberDecorate instructions.
+ for (auto old_decoration : get_decoration_mgr()->GetDecorationsFor(
+ old_var_type->result_id(), true)) {
+ assert(old_decoration->opcode() == SpvOpMemberDecorate);
+ if (old_decoration->GetSingleWordInOperand(1u) != index) continue;
+ CreateNewDecorationForMemberDecorate(old_decoration, new_var_id);
+ }
+}
+
+uint32_t DescriptorScalarReplacement::GetNewBindingForElement(
+ uint32_t old_binding, uint32_t index, uint32_t new_var_ptr_type_id,
+ const bool is_old_var_array, const bool is_old_var_struct,
+ Instruction* old_var_type) {
+ if (is_old_var_array) {
+ return old_binding + index * GetNumBindingsUsedByType(new_var_ptr_type_id);
+ }
+ if (is_old_var_struct) {
+ // The binding offset that should be added is the sum of binding
+ // numbers used by previous members of the current struct.
+ uint32_t new_binding = old_binding;
+ for (uint32_t i = 0; i < index; ++i) {
+ new_binding +=
+ GetNumBindingsUsedByType(old_var_type->GetSingleWordInOperand(i));
+ }
+ return new_binding;
+ }
+ return old_binding;
+}
+
+void DescriptorScalarReplacement::CreateNewDecorationForNewVariable(
+ Instruction* old_decoration, uint32_t new_var_id, uint32_t new_binding) {
+ assert(old_decoration->opcode() == SpvOpDecorate ||
+ old_decoration->opcode() == SpvOpDecorateString);
+ std::unique_ptr<Instruction> new_decoration(old_decoration->Clone(context()));
+ new_decoration->SetInOperand(0, {new_var_id});
+
+ if (IsDecorationBinding(new_decoration.get())) {
+ new_decoration->SetInOperand(2, {new_binding});
+ }
+ context()->AddAnnotationInst(std::move(new_decoration));
+}
+
+void DescriptorScalarReplacement::CreateNewDecorationForMemberDecorate(
+ Instruction* old_member_decoration, uint32_t new_var_id) {
+ std::vector<Operand> operands(
+ {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {new_var_id}}});
+ auto new_decorate_operand_begin = old_member_decoration->begin() + 2u;
+ auto new_decorate_operand_end = old_member_decoration->end();
+ operands.insert(operands.end(), new_decorate_operand_begin,
+ new_decorate_operand_end);
+ get_decoration_mgr()->AddDecoration(SpvOpDecorate, std::move(operands));
+}
+
uint32_t DescriptorScalarReplacement::CreateReplacementVariable(
Instruction* var, uint32_t idx) {
// The storage class for the new variable is the same as the original.
@@ -285,33 +269,8 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable(
{static_cast<uint32_t>(storage_class)}}}));
context()->AddGlobalValue(std::move(variable));
- // Copy all of the decorations to the new variable. The only difference is
- // the Binding decoration needs to be adjusted.
- for (auto old_decoration :
- get_decoration_mgr()->GetDecorationsFor(var->result_id(), true)) {
- assert(old_decoration->opcode() == SpvOpDecorate);
- std::unique_ptr<Instruction> new_decoration(
- old_decoration->Clone(context()));
- new_decoration->SetInOperand(0, {id});
-
- uint32_t decoration = new_decoration->GetSingleWordInOperand(1u);
- if (decoration == SpvDecorationBinding) {
- uint32_t new_binding = new_decoration->GetSingleWordInOperand(2);
- if (is_array) {
- new_binding += idx * GetNumBindingsUsedByType(ptr_element_type_id);
- }
- if (is_struct) {
- // The binding offset that should be added is the sum of binding numbers
- // used by previous members of the current struct.
- for (uint32_t i = 0; i < idx; ++i) {
- new_binding += GetNumBindingsUsedByType(
- pointee_type_inst->GetSingleWordInOperand(i));
- }
- }
- new_decoration->SetInOperand(2, {new_binding});
- }
- context()->AddAnnotationInst(std::move(new_decoration));
- }
+ CopyDecorationsForNewVariable(var, idx, id, ptr_element_type_id, is_array,
+ is_struct, pointee_type_inst);
// Create a new OpName for the replacement variable.
std::vector<std::unique_ptr<Instruction>> names_to_add;
@@ -377,7 +336,7 @@ uint32_t DescriptorScalarReplacement::GetNumBindingsUsedByType(
// The number of bindings consumed by a structure is the sum of the bindings
// used by its members.
if (type_inst->opcode() == SpvOpTypeStruct &&
- !IsTypeOfStructuredBuffer(type_inst)) {
+ !descsroautil::IsTypeOfStructuredBuffer(context(), type_inst)) {
uint32_t sum = 0;
for (uint32_t i = 0; i < type_inst->NumInOperands(); i++)
sum += GetNumBindingsUsedByType(type_inst->GetSingleWordInOperand(i));
diff --git a/source/opt/desc_sroa.h b/source/opt/desc_sroa.h
index cd72fd30..6a24fd87 100644
--- a/source/opt/desc_sroa.h
+++ b/source/opt/desc_sroa.h
@@ -46,10 +46,6 @@ class DescriptorScalarReplacement : public Pass {
}
private:
- // Returns true if |var| is an OpVariable instruction that represents a
- // descriptor array. These are the variables that we want to replace.
- bool IsCandidate(Instruction* var);
-
// Replaces all references to |var| by new variables, one for each element of
// the array |var|. The binding for the new variables corresponding to
// element i will be the binding of |var| plus i. Returns true if successful.
@@ -93,14 +89,50 @@ class DescriptorScalarReplacement : public Pass {
// bindings used by its members.
uint32_t GetNumBindingsUsedByType(uint32_t type_id);
- // Returns true if |type| is a type that could be used for a structured buffer
- // as opposed to a type that would be used for a structure of resource
- // descriptors.
- bool IsTypeOfStructuredBuffer(const Instruction* type) const;
+ // Copy all of the decorations of variable |old_var| and make them as
+ // decorations for the new variable whose id is |new_var_id|. The new variable
+ // is supposed to replace |index|th element of |old_var|.
+ // |new_var_ptr_type_id| is the id of the pointer to the type of the new
+ // variable. |is_old_var_array| is true if |old_var| has an array type.
+ // |is_old_var_struct| is true if |old_var| has a structure type.
+ // |old_var_type| is the pointee type of |old_var|.
+ void CopyDecorationsForNewVariable(Instruction* old_var, uint32_t index,
+ uint32_t new_var_id,
+ uint32_t new_var_ptr_type_id,
+ const bool is_old_var_array,
+ const bool is_old_var_struct,
+ Instruction* old_var_type);
+
+ // Get the new binding number for a new variable that will be replaced with an
+ // |index|th element of an old variable. The old variable has |old_binding|
+ // as its binding number. |ptr_elem_type_id| the id of the pointer to the
+ // element type. |is_old_var_array| is true if the old variable has an array
+ // type. |is_old_var_struct| is true if the old variable has a structure type.
+ // |old_var_type| is the pointee type of the old variable.
+ uint32_t GetNewBindingForElement(uint32_t old_binding, uint32_t index,
+ uint32_t ptr_elem_type_id,
+ const bool is_old_var_array,
+ const bool is_old_var_struct,
+ Instruction* old_var_type);
+
+ // Create a new OpDecorate(String) instruction by cloning |old_decoration|.
+ // The new OpDecorate(String) instruction will be used for a variable whose id
+ // is |new_var_ptr_type_id|. If |old_decoration| is a decoration for a
+ // binding, the new OpDecorate(String) instruction will have |new_binding| as
+ // its binding.
+ void CreateNewDecorationForNewVariable(Instruction* old_decoration,
+ uint32_t new_var_id,
+ uint32_t new_binding);
+
+ // Create a new OpDecorate instruction whose operand is the same as an
+ // OpMemberDecorate instruction |old_member_decoration| except Target operand.
+ // The Target operand of the new OpDecorate instruction will be |new_var_id|.
+ void CreateNewDecorationForMemberDecorate(Instruction* old_decoration,
+ uint32_t new_var_id);
// A map from an OpVariable instruction to the set of variables that will be
// used to replace it. The entry |replacement_variables_[var][i]| is the id of
- // a variable that will be used in the place of the the ith element of the
+ // a variable that will be used in the place of the ith element of the
// array |var|. If the entry is |0|, then the variable has not been
// created yet.
std::map<Instruction*, std::vector<uint32_t>> replacement_variables_;
diff --git a/source/opt/desc_sroa_util.cpp b/source/opt/desc_sroa_util.cpp
new file mode 100644
index 00000000..1954e2cc
--- /dev/null
+++ b/source/opt/desc_sroa_util.cpp
@@ -0,0 +1,117 @@
+// Copyright (c) 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/opt/desc_sroa_util.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+const uint32_t kOpAccessChainInOperandIndexes = 1;
+
+// Returns the length of array type |type|.
+uint32_t GetLengthOfArrayType(IRContext* context, Instruction* type) {
+ assert(type->opcode() == SpvOpTypeArray && "type must be array");
+ uint32_t length_id = type->GetSingleWordInOperand(1);
+ const analysis::Constant* length_const =
+ context->get_constant_mgr()->FindDeclaredConstant(length_id);
+ assert(length_const != nullptr);
+ return length_const->GetU32();
+}
+
+} // namespace
+
+namespace descsroautil {
+
+bool IsDescriptorArray(IRContext* context, Instruction* var) {
+ if (var->opcode() != SpvOpVariable) {
+ return false;
+ }
+
+ uint32_t ptr_type_id = var->type_id();
+ Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id);
+ if (ptr_type_inst->opcode() != SpvOpTypePointer) {
+ return false;
+ }
+
+ uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1);
+ Instruction* var_type_inst = context->get_def_use_mgr()->GetDef(var_type_id);
+ if (var_type_inst->opcode() != SpvOpTypeArray &&
+ var_type_inst->opcode() != SpvOpTypeStruct) {
+ return false;
+ }
+
+ // All structures with descriptor assignments must be replaced by variables,
+ // one for each of their members - with the exceptions of buffers.
+ if (IsTypeOfStructuredBuffer(context, var_type_inst)) {
+ return false;
+ }
+
+ if (!context->get_decoration_mgr()->HasDecoration(
+ var->result_id(), SpvDecorationDescriptorSet)) {
+ return false;
+ }
+
+ return context->get_decoration_mgr()->HasDecoration(var->result_id(),
+ SpvDecorationBinding);
+}
+
+bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type) {
+ if (type->opcode() != SpvOpTypeStruct) {
+ return false;
+ }
+
+ // All buffers have offset decorations for members of their structure types.
+ // This is how we distinguish it from a structure of descriptors.
+ return context->get_decoration_mgr()->HasDecoration(type->result_id(),
+ SpvDecorationOffset);
+}
+
+const analysis::Constant* GetAccessChainIndexAsConst(
+ IRContext* context, Instruction* access_chain) {
+ if (access_chain->NumInOperands() <= 1) {
+ return nullptr;
+ }
+ uint32_t idx_id = GetFirstIndexOfAccessChain(access_chain);
+ const analysis::Constant* idx_const =
+ context->get_constant_mgr()->FindDeclaredConstant(idx_id);
+ return idx_const;
+}
+
+uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain) {
+ assert(access_chain->NumInOperands() > 1 &&
+ "OpAccessChain does not have Indexes operand");
+ return access_chain->GetSingleWordInOperand(kOpAccessChainInOperandIndexes);
+}
+
+uint32_t GetNumberOfElementsForArrayOrStruct(IRContext* context,
+ Instruction* var) {
+ uint32_t ptr_type_id = var->type_id();
+ Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id);
+ assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
+ "Variable should be a pointer to an array or structure.");
+ uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
+ Instruction* pointee_type_inst =
+ context->get_def_use_mgr()->GetDef(pointee_type_id);
+ if (pointee_type_inst->opcode() == SpvOpTypeArray) {
+ return GetLengthOfArrayType(context, pointee_type_inst);
+ }
+ assert(pointee_type_inst->opcode() == SpvOpTypeStruct &&
+ "Variable should be a pointer to an array or structure.");
+ return pointee_type_inst->NumInOperands();
+}
+
+} // namespace descsroautil
+} // namespace opt
+} // namespace spvtools
diff --git a/source/opt/desc_sroa_util.h b/source/opt/desc_sroa_util.h
new file mode 100644
index 00000000..2f45c0c2
--- /dev/null
+++ b/source/opt/desc_sroa_util.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_OPT_DESC_SROA_UTIL_H_
+#define SOURCE_OPT_DESC_SROA_UTIL_H_
+
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace opt {
+
+// Provides functions for the descriptor array SROA.
+namespace descsroautil {
+
+// Returns true if |var| is an OpVariable instruction that represents a
+// descriptor array.
+bool IsDescriptorArray(IRContext* context, Instruction* var);
+
+// Returns true if |type| is a type that could be used for a structured buffer
+// as opposed to a type that would be used for a structure of resource
+// descriptors.
+bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type);
+
+// Returns the first index of the OpAccessChain instruction |access_chain| as
+// a constant. Returns nullptr if it is not a constant.
+const analysis::Constant* GetAccessChainIndexAsConst(IRContext* context,
+ Instruction* access_chain);
+
+// Returns the number of elements of an OpVariable instruction |var| whose type
+// must be a pointer to an array or a struct.
+uint32_t GetNumberOfElementsForArrayOrStruct(IRContext* context,
+ Instruction* var);
+
+// Returns the first Indexes operand id of the OpAccessChain or
+// OpInBoundsAccessChain instruction |access_chain|. The access chain must have
+// at least 1 index.
+uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain);
+
+} // namespace descsroautil
+} // namespace opt
+} // namespace spvtools
+
+#endif // SOURCE_OPT_DESC_SROA_UTIL_H_
diff --git a/source/opt/dominator_tree.cpp b/source/opt/dominator_tree.cpp
index 55287f44..d86de151 100644
--- a/source/opt/dominator_tree.cpp
+++ b/source/opt/dominator_tree.cpp
@@ -48,7 +48,7 @@ namespace {
// BBType - BasicBlock type. Will either be BasicBlock or DominatorTreeNode
// SuccessorLambda - Lamdba matching the signature of 'const
// std::vector<BBType>*(const BBType *A)'. Will return a vector of the nodes
-// succeding BasicBlock A.
+// succeeding BasicBlock A.
// PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be
// called on each node traversed AFTER their children.
// PreLambda - Lamdba matching the signature of 'void (const BBType*)' will be
@@ -69,7 +69,7 @@ static void DepthFirstSearch(const BBType* bb, SuccessorLambda successors,
// BBType - BasicBlock type. Will either be BasicBlock or DominatorTreeNode
// SuccessorLambda - Lamdba matching the signature of 'const
// std::vector<BBType>*(const BBType *A)'. Will return a vector of the nodes
-// succeding BasicBlock A.
+// succeeding BasicBlock A.
// PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be
// called on each node traversed after their children.
template <typename BBType, typename SuccessorLambda, typename PostLambda>
diff --git a/source/opt/dominator_tree.h b/source/opt/dominator_tree.h
index 0024bc50..1674b228 100644
--- a/source/opt/dominator_tree.h
+++ b/source/opt/dominator_tree.h
@@ -278,7 +278,7 @@ class DominatorTree {
private:
// Wrapper function which gets the list of pairs of each BasicBlocks to its
- // immediately dominating BasicBlock and stores the result in the the edges
+ // immediately dominating BasicBlock and stores the result in the edges
// parameter.
//
// The |edges| vector will contain the dominator tree as pairs of nodes.
diff --git a/source/opt/eliminate_dead_members_pass.cpp b/source/opt/eliminate_dead_members_pass.cpp
index 173df620..52aca525 100644
--- a/source/opt/eliminate_dead_members_pass.cpp
+++ b/source/opt/eliminate_dead_members_pass.cpp
@@ -20,6 +20,7 @@
namespace {
const uint32_t kRemovedMember = 0xFFFFFFFF;
const uint32_t kSpecConstOpOpcodeIdx = 0;
+constexpr uint32_t kArrayElementTypeIdx = 0;
} // namespace
namespace spvtools {
@@ -37,7 +38,7 @@ Pass::Status EliminateDeadMembersPass::Process() {
}
void EliminateDeadMembersPass::FindLiveMembers() {
- // Until we have implemented the rewritting of OpSpecConsantOp instructions,
+ // Until we have implemented the rewriting of OpSpecConsantOp instructions,
// we have to mark them as fully used just to be safe.
for (auto& inst : get_module()->types_values()) {
if (inst.opcode() == SpvOpSpecConstantOp) {
@@ -64,6 +65,10 @@ void EliminateDeadMembersPass::FindLiveMembers() {
MarkPointeeTypeAsFullUsed(inst.type_id());
break;
default:
+ // Ignore structured buffers as layout(offset) qualifiers cannot be
+ // applied to structure fields
+ if (inst.IsVulkanStorageBufferVariable())
+ MarkPointeeTypeAsFullUsed(inst.type_id());
break;
}
}
@@ -136,18 +141,22 @@ void EliminateDeadMembersPass::MarkMembersAsLiveForStore(
void EliminateDeadMembersPass::MarkTypeAsFullyUsed(uint32_t type_id) {
Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
assert(type_inst != nullptr);
- if (type_inst->opcode() != SpvOpTypeStruct) {
- return;
- }
-
- // Mark every member of the current struct as used.
- for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) {
- used_members_[type_id].insert(i);
- }
- // Mark any sub struct as fully used.
- for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) {
- MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i));
+ switch (type_inst->opcode()) {
+ case SpvOpTypeStruct:
+ // Mark every member and its type as fully used.
+ for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) {
+ used_members_[type_id].insert(i);
+ MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i));
+ }
+ break;
+ case SpvOpTypeArray:
+ case SpvOpTypeRuntimeArray:
+ MarkTypeAsFullyUsed(
+ type_inst->GetSingleWordInOperand(kArrayElementTypeIdx));
+ break;
+ default:
+ break;
}
}
@@ -561,7 +570,7 @@ bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) {
Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
switch (type_inst->opcode()) {
case SpvOpTypeStruct:
- // The type will have already been rewriten, so use the new member
+ // The type will have already been rewritten, so use the new member
// index.
type_id = type_inst->GetSingleWordInOperand(new_member_idx);
break;
diff --git a/source/opt/feature_manager.cpp b/source/opt/feature_manager.cpp
index ad70c1e4..a5902716 100644
--- a/source/opt/feature_manager.cpp
+++ b/source/opt/feature_manager.cpp
@@ -39,8 +39,7 @@ void FeatureManager::AddExtension(Instruction* ext) {
assert(ext->opcode() == SpvOpExtension &&
"Expecting an extension instruction.");
- const std::string name =
- reinterpret_cast<const char*>(ext->GetInOperand(0u).words.data());
+ const std::string name = ext->GetInOperand(0u).AsString();
Extension extension;
if (GetExtensionFromString(name.c_str(), &extension)) {
extensions_.Add(extension);
@@ -80,6 +79,8 @@ void FeatureManager::AddExtInstImportIds(Module* module) {
extinst_importid_GLSLstd450_ = module->GetExtInstImportId("GLSL.std.450");
extinst_importid_OpenCL100DebugInfo_ =
module->GetExtInstImportId("OpenCL.DebugInfo.100");
+ extinst_importid_Shader100DebugInfo_ =
+ module->GetExtInstImportId("NonSemantic.Shader.DebugInfo.100");
}
bool operator==(const FeatureManager& a, const FeatureManager& b) {
@@ -107,6 +108,11 @@ bool operator==(const FeatureManager& a, const FeatureManager& b) {
return false;
}
+ if (a.extinst_importid_Shader100DebugInfo_ !=
+ b.extinst_importid_Shader100DebugInfo_) {
+ return false;
+ }
+
return true;
}
} // namespace opt
diff --git a/source/opt/feature_manager.h b/source/opt/feature_manager.h
index 66d1cbac..68c8e9a2 100644
--- a/source/opt/feature_manager.h
+++ b/source/opt/feature_manager.h
@@ -55,6 +55,10 @@ class FeatureManager {
return extinst_importid_OpenCL100DebugInfo_;
}
+ uint32_t GetExtInstImportId_Shader100DebugInfo() const {
+ return extinst_importid_Shader100DebugInfo_;
+ }
+
friend bool operator==(const FeatureManager& a, const FeatureManager& b);
friend bool operator!=(const FeatureManager& a, const FeatureManager& b) {
return !(a == b);
@@ -92,6 +96,10 @@ class FeatureManager {
// Common OpenCL100DebugInfo external instruction import ids, cached
// for performance.
uint32_t extinst_importid_OpenCL100DebugInfo_ = 0;
+
+ // Common NonSemanticShader100DebugInfo external instruction import ids,
+ // cached for performance.
+ uint32_t extinst_importid_Shader100DebugInfo_ = 0;
};
} // namespace opt
diff --git a/source/opt/fold.cpp b/source/opt/fold.cpp
index 6550fb4f..b903da6a 100644
--- a/source/opt/fold.cpp
+++ b/source/opt/fold.cpp
@@ -540,7 +540,7 @@ std::vector<uint32_t> InstructionFolder::FoldVectors(
// in 32-bit words here. The reason of not using FoldScalars() here
// is that we do not create temporary null constants as components
// when the vector operand is a NullConstant because Constant creation
- // may need extra checks for the validity and that is not manageed in
+ // may need extra checks for the validity and that is not managed in
// here.
if (const analysis::ScalarConstant* scalar_component =
vector_operand->GetComponents().at(d)->AsScalarConstant()) {
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 8ab717ea..85f11fde 100644
--- a/source/opt/fold_spec_constant_op_and_composite_pass.cpp
+++ b/source/opt/fold_spec_constant_op_and_composite_pass.cpp
@@ -115,7 +115,7 @@ bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
Instruction* folded_inst = nullptr;
assert(inst->GetInOperand(0).type ==
SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER &&
- "The first in-operand of OpSpecContantOp instruction must be of "
+ "The first in-operand of OpSpecConstantOp instruction must be of "
"SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER type");
switch (static_cast<SpvOp>(inst->GetSingleWordInOperand(0))) {
diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp
index e3e926c3..4904f186 100644
--- a/source/opt/folding_rules.cpp
+++ b/source/opt/folding_rules.cpp
@@ -14,6 +14,7 @@
#include "source/opt/folding_rules.h"
+#include <climits>
#include <limits>
#include <memory>
#include <utility>
@@ -523,7 +524,8 @@ uint32_t PerformFloatingPointOperation(analysis::ConstantManager* const_mgr,
float fval = val.getAsFloat(); \
if (!IsValidResult(fval)) return 0; \
words = val.GetWords(); \
- } static_assert(true, "require extra semicolon")
+ } \
+ static_assert(true, "require extra semicolon")
switch (opcode) {
case SpvOpFMul:
FOLD_OP(*);
@@ -558,24 +560,19 @@ uint32_t PerformIntegerOperation(analysis::ConstantManager* const_mgr,
uint32_t width = type->AsInteger()->width();
assert(width == 32 || width == 64);
std::vector<uint32_t> words;
-#define FOLD_OP(op) \
- if (width == 64) { \
- if (type->IsSigned()) { \
- int64_t val = input1->GetS64() op input2->GetS64(); \
- words = ExtractInts(static_cast<uint64_t>(val)); \
- } else { \
- uint64_t val = input1->GetU64() op input2->GetU64(); \
- words = ExtractInts(val); \
- } \
- } else { \
- if (type->IsSigned()) { \
- int32_t val = input1->GetS32() op input2->GetS32(); \
- words.push_back(static_cast<uint32_t>(val)); \
- } else { \
- uint32_t val = input1->GetU32() op input2->GetU32(); \
- words.push_back(val); \
- } \
- } static_assert(true, "require extra semicalon")
+ // Regardless of the sign of the constant, folding is performed on an unsigned
+ // interpretation of the constant data. This avoids signed integer overflow
+ // while folding, and works because sign is irrelevant for the IAdd, ISub and
+ // IMul instructions.
+#define FOLD_OP(op) \
+ if (width == 64) { \
+ uint64_t val = input1->GetU64() op input2->GetU64(); \
+ words = ExtractInts(val); \
+ } else { \
+ uint32_t val = input1->GetU32() op input2->GetU32(); \
+ words.push_back(val); \
+ } \
+ static_assert(true, "require extra semicolon")
switch (opcode) {
case SpvOpIMul:
FOLD_OP(*);
@@ -967,30 +964,21 @@ FoldingRule MergeDivMulArithmetic() {
// Fold divides of a constant and a negation.
// Cases:
// (-x) / 2 = x / -2
-// 2 / (-x) = 2 / -x
+// 2 / (-x) = -2 / x
FoldingRule MergeDivNegateArithmetic() {
return [](IRContext* context, Instruction* inst,
const std::vector<const analysis::Constant*>& constants) {
- assert(inst->opcode() == SpvOpFDiv || inst->opcode() == SpvOpSDiv ||
- inst->opcode() == SpvOpUDiv);
+ assert(inst->opcode() == SpvOpFDiv);
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
- const analysis::Type* type =
- context->get_type_mgr()->GetType(inst->type_id());
- bool uses_float = HasFloatingPoint(type);
- if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false;
-
- uint32_t width = ElementWidth(type);
- if (width != 32 && width != 64) return false;
+ if (!inst->IsFloatingPointFoldingAllowed()) return false;
const analysis::Constant* const_input1 = ConstInput(constants);
if (!const_input1) return false;
Instruction* other_inst = NonConstInput(context, constants[0], inst);
- if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
- return false;
+ if (!other_inst->IsFloatingPointFoldingAllowed()) return false;
bool first_is_variable = constants[0] == nullptr;
- if (other_inst->opcode() == SpvOpFNegate ||
- other_inst->opcode() == SpvOpSNegate) {
+ if (other_inst->opcode() == SpvOpFNegate) {
uint32_t neg_id = NegateConstant(const_mgr, const_input1);
if (first_is_variable) {
@@ -1468,90 +1456,121 @@ FoldingRule IntMultipleBy1() {
};
}
-FoldingRule CompositeConstructFeedingExtract() {
- return [](IRContext* context, Instruction* inst,
- const std::vector<const analysis::Constant*>&) {
- // If the input to an OpCompositeExtract is an OpCompositeConstruct,
- // then we can simply use the appropriate element in the construction.
- assert(inst->opcode() == SpvOpCompositeExtract &&
- "Wrong opcode. Should be OpCompositeExtract.");
- analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
- analysis::TypeManager* type_mgr = context->get_type_mgr();
-
- // If there are no index operands, then this rule cannot do anything.
- if (inst->NumInOperands() <= 1) {
- return false;
- }
+// Returns the number of elements that the |index|th in operand in |inst|
+// contributes to the result of |inst|. |inst| must be an
+// OpCompositeConstructInstruction.
+uint32_t GetNumOfElementsContributedByOperand(IRContext* context,
+ const Instruction* inst,
+ uint32_t index) {
+ assert(inst->opcode() == SpvOpCompositeConstruct);
+ analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+ analysis::TypeManager* type_mgr = context->get_type_mgr();
+
+ analysis::Vector* result_type =
+ type_mgr->GetType(inst->type_id())->AsVector();
+ if (result_type == nullptr) {
+ // If the result of the OpCompositeConstruct is not a vector then every
+ // operands corresponds to a single element in the result.
+ return 1;
+ }
- uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
- Instruction* cinst = def_use_mgr->GetDef(cid);
+ // If the result type is a vector then the operands are either scalars or
+ // vectors. If it is a scalar, then it corresponds to a single element. If it
+ // is a vector, then each element in the vector will be an element in the
+ // result.
+ uint32_t id = inst->GetSingleWordInOperand(index);
+ Instruction* def = def_use_mgr->GetDef(id);
+ analysis::Vector* type = type_mgr->GetType(def->type_id())->AsVector();
+ if (type == nullptr) {
+ return 1;
+ }
+ return type->element_count();
+}
- if (cinst->opcode() != SpvOpCompositeConstruct) {
- return false;
- }
+// Returns the in-operands for an OpCompositeExtract instruction that are needed
+// to extract the |result_index|th element in the result of |inst| without using
+// the result of |inst|. Returns the empty vector if |result_index| is
+// out-of-bounds. |inst| must be an |OpCompositeConstruct| instruction.
+std::vector<Operand> GetExtractOperandsForElementOfCompositeConstruct(
+ IRContext* context, const Instruction* inst, uint32_t result_index) {
+ assert(inst->opcode() == SpvOpCompositeConstruct);
+ analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+ analysis::TypeManager* type_mgr = context->get_type_mgr();
- std::vector<Operand> operands;
- analysis::Type* composite_type = type_mgr->GetType(cinst->type_id());
- if (composite_type->AsVector() == nullptr) {
- // Get the element being extracted from the OpCompositeConstruct
- // Since it is not a vector, it is simple to extract the single element.
- uint32_t element_index = inst->GetSingleWordInOperand(1);
- uint32_t element_id = cinst->GetSingleWordInOperand(element_index);
- operands.push_back({SPV_OPERAND_TYPE_ID, {element_id}});
-
- // Add the remaining indices for extraction.
- for (uint32_t i = 2; i < inst->NumInOperands(); ++i) {
- operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER,
- {inst->GetSingleWordInOperand(i)}});
- }
+ analysis::Type* result_type = type_mgr->GetType(inst->type_id());
+ if (result_type->AsVector() == nullptr) {
+ uint32_t id = inst->GetSingleWordInOperand(result_index);
+ return {Operand(SPV_OPERAND_TYPE_ID, {id})};
+ }
- } else {
- // With vectors we have to handle the case where it is concatenating
- // vectors.
- assert(inst->NumInOperands() == 2 &&
- "Expecting a vector of scalar values.");
-
- uint32_t element_index = inst->GetSingleWordInOperand(1);
- for (uint32_t construct_index = 0;
- construct_index < cinst->NumInOperands(); ++construct_index) {
- uint32_t element_id = cinst->GetSingleWordInOperand(construct_index);
- Instruction* element_def = def_use_mgr->GetDef(element_id);
- analysis::Vector* element_type =
- type_mgr->GetType(element_def->type_id())->AsVector();
- if (element_type) {
- uint32_t vector_size = element_type->element_count();
- if (vector_size <= element_index) {
- // The element we want comes after this vector.
- element_index -= vector_size;
- } else {
- // We want an element of this vector.
- operands.push_back({SPV_OPERAND_TYPE_ID, {element_id}});
- operands.push_back(
- {SPV_OPERAND_TYPE_LITERAL_INTEGER, {element_index}});
- break;
- }
- } else {
- if (element_index == 0) {
- // This is a scalar, and we this is the element we are extracting.
- operands.push_back({SPV_OPERAND_TYPE_ID, {element_id}});
- break;
- } else {
- // Skip over this scalar value.
- --element_index;
- }
- }
+ // If the result type is a vector, then vector operands are concatenated.
+ uint32_t total_element_count = 0;
+ for (uint32_t idx = 0; idx < inst->NumInOperands(); ++idx) {
+ uint32_t element_count =
+ GetNumOfElementsContributedByOperand(context, inst, idx);
+ total_element_count += element_count;
+ if (result_index < total_element_count) {
+ std::vector<Operand> operands;
+ uint32_t id = inst->GetSingleWordInOperand(idx);
+ Instruction* operand_def = def_use_mgr->GetDef(id);
+ analysis::Type* operand_type = type_mgr->GetType(operand_def->type_id());
+
+ operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
+ if (operand_type->AsVector()) {
+ uint32_t start_index_of_id = total_element_count - element_count;
+ uint32_t index_into_id = result_index - start_index_of_id;
+ operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index_into_id}});
}
+ return operands;
}
+ }
+ return {};
+}
+
+bool CompositeConstructFeedingExtract(
+ IRContext* context, Instruction* inst,
+ const std::vector<const analysis::Constant*>&) {
+ // If the input to an OpCompositeExtract is an OpCompositeConstruct,
+ // then we can simply use the appropriate element in the construction.
+ assert(inst->opcode() == SpvOpCompositeExtract &&
+ "Wrong opcode. Should be OpCompositeExtract.");
+ analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+ // If there are no index operands, then this rule cannot do anything.
+ if (inst->NumInOperands() <= 1) {
+ return false;
+ }
+
+ uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
+ Instruction* cinst = def_use_mgr->GetDef(cid);
+
+ if (cinst->opcode() != SpvOpCompositeConstruct) {
+ return false;
+ }
+
+ uint32_t index_into_result = inst->GetSingleWordInOperand(1);
+ std::vector<Operand> operands =
+ GetExtractOperandsForElementOfCompositeConstruct(context, cinst,
+ index_into_result);
+
+ if (operands.empty()) {
+ return false;
+ }
+
+ // Add the remaining indices for extraction.
+ for (uint32_t i = 2; i < inst->NumInOperands(); ++i) {
+ operands.push_back(
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {inst->GetSingleWordInOperand(i)}});
+ }
+
+ if (operands.size() == 1) {
// If there were no extra indices, then we have the final object. No need
- // to extract even more.
- if (operands.size() == 1) {
- inst->SetOpcode(SpvOpCopyObject);
- }
+ // to extract any more.
+ inst->SetOpcode(SpvOpCopyObject);
+ }
- inst->SetInOperands(std::move(operands));
- return true;
- };
+ inst->SetInOperands(std::move(operands));
+ return true;
}
// If the OpCompositeConstruct is simply putting back together elements that
@@ -2510,7 +2529,7 @@ void FoldingRules::AddFoldingRules() {
rules_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct);
rules_[SpvOpCompositeExtract].push_back(InsertFeedingExtract());
- rules_[SpvOpCompositeExtract].push_back(CompositeConstructFeedingExtract());
+ rules_[SpvOpCompositeExtract].push_back(CompositeConstructFeedingExtract);
rules_[SpvOpCompositeExtract].push_back(VectorShuffleFeedingExtract());
rules_[SpvOpCompositeExtract].push_back(FMixFeedingExtract());
@@ -2562,8 +2581,6 @@ void FoldingRules::AddFoldingRules() {
rules_[SpvOpPhi].push_back(RedundantPhi());
- rules_[SpvOpSDiv].push_back(MergeDivNegateArithmetic());
-
rules_[SpvOpSNegate].push_back(MergeNegateArithmetic());
rules_[SpvOpSNegate].push_back(MergeNegateMulDivArithmetic());
rules_[SpvOpSNegate].push_back(MergeNegateAddSubArithmetic());
@@ -2572,8 +2589,6 @@ void FoldingRules::AddFoldingRules() {
rules_[SpvOpStore].push_back(StoringUndef());
- rules_[SpvOpUDiv].push_back(MergeDivNegateArithmetic());
-
rules_[SpvOpVectorShuffle].push_back(VectorShuffleFeedingShuffle());
rules_[SpvOpImageSampleImplicitLod].push_back(UpdateImageOperands());
diff --git a/source/opt/function.h b/source/opt/function.h
index 9e1c7274..917bf584 100644
--- a/source/opt/function.h
+++ b/source/opt/function.h
@@ -177,6 +177,9 @@ class Function {
// debuggers.
void Dump() const;
+ // Returns true is a function declaration and not a function definition.
+ bool IsDeclaration() { return begin() == end(); }
+
private:
// The OpFunction instruction that begins the definition of this function.
std::unique_ptr<Instruction> def_inst_;
diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp
index 1b28f9b5..4652d72d 100644
--- a/source/opt/graphics_robust_access_pass.cpp
+++ b/source/opt/graphics_robust_access_pass.cpp
@@ -13,7 +13,7 @@
// limitations under the License.
// This pass injects code in a graphics shader to implement guarantees
-// satisfying Vulkan's robustBufferAcces rules. Robust access rules permit
+// satisfying Vulkan's robustBufferAccess rules. Robust access rules permit
// an out-of-bounds access to be redirected to an access of the same type
// (load, store, etc.) but within the same root object.
//
@@ -74,7 +74,7 @@
// Pointers are always (correctly) typed and so the address and number of
// consecutive locations are fully determined by the pointer.
//
-// - A pointer value orginates as one of few cases:
+// - A pointer value originates as one of few cases:
//
// - OpVariable for an interface object or an array of them: image,
// buffer (UBO or SSBO), sampler, sampled-image, push-constant, input
@@ -559,21 +559,17 @@ uint32_t GraphicsRobustAccessPass::GetGlslInsts() {
if (module_status_.glsl_insts_id == 0) {
// This string serves double-duty as raw data for a string and for a vector
// of 32-bit words
- const char glsl[] = "GLSL.std.450\0\0\0\0";
- const size_t glsl_str_byte_len = 16;
+ const char glsl[] = "GLSL.std.450";
// Use an existing import if we can.
for (auto& inst : context()->module()->ext_inst_imports()) {
- const auto& name_words = inst.GetInOperand(0).words;
- if (0 == std::strncmp(reinterpret_cast<const char*>(name_words.data()),
- glsl, glsl_str_byte_len)) {
+ if (inst.GetInOperand(0).AsString() == glsl) {
module_status_.glsl_insts_id = inst.result_id();
}
}
if (module_status_.glsl_insts_id == 0) {
// Make a new import instruction.
module_status_.glsl_insts_id = TakeNextId();
- std::vector<uint32_t> words(glsl_str_byte_len / sizeof(uint32_t));
- std::memcpy(words.data(), glsl, glsl_str_byte_len);
+ std::vector<uint32_t> words = spvtools::utils::MakeVector(glsl);
auto import_inst = MakeUnique<Instruction>(
context(), SpvOpExtInstImport, 0, module_status_.glsl_insts_id,
std::initializer_list<Operand>{
@@ -962,7 +958,7 @@ spv_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer(
constant_mgr->GetDefiningInstruction(component_0)->result_id();
// If the image is a cube array, then the last component of the queried
- // size is the layer count. In the query, we have to accomodate folding
+ // size is the layer count. In the query, we have to accommodate folding
// in the face index ranging from 0 through 5. The inclusive upper bound
// on the third coordinate therefore is multiplied by 6.
auto* query_size_including_faces = query_size;
diff --git a/source/opt/graphics_robust_access_pass.h b/source/opt/graphics_robust_access_pass.h
index 6fc692c1..8f4c9dc7 100644
--- a/source/opt/graphics_robust_access_pass.h
+++ b/source/opt/graphics_robust_access_pass.h
@@ -111,7 +111,7 @@ class GraphicsRobustAccessPass : public Pass {
Instruction* max, Instruction* where);
// Returns a new instruction which evaluates to the length the runtime array
- // referenced by the access chain at the specfied index. The instruction is
+ // referenced by the access chain at the specified index. The instruction is
// inserted before the access chain instruction. Returns a null pointer in
// some cases if assumptions are violated (rather than asserting out).
opt::Instruction* MakeRuntimeArrayLengthInst(Instruction* access_chain,
diff --git a/source/opt/if_conversion.cpp b/source/opt/if_conversion.cpp
index 4284069a..49206617 100644
--- a/source/opt/if_conversion.cpp
+++ b/source/opt/if_conversion.cpp
@@ -129,6 +129,7 @@ Pass::Status IfConversion::Process() {
Instruction* select = builder.AddSelect(phi->type_id(), condition,
true_value->result_id(),
false_value->result_id());
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(select);
select->UpdateDebugInfoFrom(phi);
context()->ReplaceAllUsesWith(phi->result_id(), select->result_id());
to_kill.push_back(phi);
diff --git a/source/opt/inline_exhaustive_pass.cpp b/source/opt/inline_exhaustive_pass.cpp
index f24f744d..bef45017 100644
--- a/source/opt/inline_exhaustive_pass.cpp
+++ b/source/opt/inline_exhaustive_pass.cpp
@@ -65,7 +65,7 @@ Pass::Status InlineExhaustivePass::ProcessImpl() {
status = CombineStatus(status, InlineExhaustive(fp));
return false;
};
- context()->ProcessEntryPointCallTree(pfn);
+ context()->ProcessReachableCallTree(pfn);
return status;
}
diff --git a/source/opt/inline_opaque_pass.cpp b/source/opt/inline_opaque_pass.cpp
index 6ccaf908..fe9c6799 100644
--- a/source/opt/inline_opaque_pass.cpp
+++ b/source/opt/inline_opaque_pass.cpp
@@ -105,7 +105,7 @@ Pass::Status InlineOpaquePass::ProcessImpl() {
status = CombineStatus(status, InlineOpaque(fp));
return false;
};
- context()->ProcessEntryPointCallTree(pfn);
+ context()->ProcessReachableCallTree(pfn);
return status;
}
diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp
index 8159ebf7..2cc31258 100644
--- a/source/opt/inline_pass.cpp
+++ b/source/opt/inline_pass.cpp
@@ -92,7 +92,7 @@ void InlinePass::AddStore(uint32_t ptr_id, uint32_t val_id,
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {val_id}}}));
if (line_inst != nullptr) {
- newStore->dbg_line_insts().push_back(*line_inst);
+ newStore->AddDebugLine(line_inst);
}
newStore->SetDebugScope(dbg_scope);
(*block_ptr)->AddInstruction(std::move(newStore));
@@ -106,7 +106,7 @@ void InlinePass::AddLoad(uint32_t type_id, uint32_t resultId, uint32_t ptr_id,
new Instruction(context(), SpvOpLoad, type_id, resultId,
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}}));
if (line_inst != nullptr) {
- newLoad->dbg_line_insts().push_back(*line_inst);
+ newLoad->AddDebugLine(line_inst);
}
newLoad->SetDebugScope(dbg_scope);
(*block_ptr)->AddInstruction(std::move(newLoad));
@@ -158,8 +158,8 @@ bool InlinePass::CloneAndMapLocals(
auto callee_block_itr = calleeFn->begin();
auto callee_var_itr = callee_block_itr->begin();
while (callee_var_itr->opcode() == SpvOp::SpvOpVariable ||
- callee_var_itr->GetOpenCL100DebugOpcode() ==
- OpenCLDebugInfo100DebugDeclare) {
+ callee_var_itr->GetCommonDebugOpcode() ==
+ CommonDebugInfoDebugDeclare) {
if (callee_var_itr->opcode() != SpvOp::SpvOpVariable) {
++callee_var_itr;
continue;
@@ -300,8 +300,7 @@ InstructionList::iterator InlinePass::AddStoresForVariableInitializers(
UptrVectorIterator<BasicBlock> callee_first_block_itr) {
auto callee_itr = callee_first_block_itr->begin();
while (callee_itr->opcode() == SpvOp::SpvOpVariable ||
- callee_itr->GetOpenCL100DebugOpcode() ==
- OpenCLDebugInfo100DebugDeclare) {
+ callee_itr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) {
if (callee_itr->opcode() == SpvOp::SpvOpVariable &&
callee_itr->NumInOperands() == 2) {
assert(callee2caller.count(callee_itr->result_id()) &&
@@ -315,8 +314,7 @@ InstructionList::iterator InlinePass::AddStoresForVariableInitializers(
context()->get_debug_info_mgr()->BuildDebugScope(
callee_itr->GetDebugScope(), inlined_at_ctx));
}
- if (callee_itr->GetOpenCL100DebugOpcode() ==
- OpenCLDebugInfo100DebugDeclare) {
+ if (callee_itr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) {
InlineSingleInstruction(
callee2caller, new_blk_ptr->get(), &*callee_itr,
context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
@@ -405,6 +403,14 @@ bool InlinePass::InlineEntryBlock(
callee2caller, inlined_at_ctx, new_blk_ptr, callee_first_block);
while (callee_inst_itr != callee_first_block->end()) {
+ // Don't inline function definition links, the calling function is not a
+ // definition.
+ if (callee_inst_itr->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
+ ++callee_inst_itr;
+ continue;
+ }
+
if (!InlineSingleInstruction(
callee2caller, new_blk_ptr->get(), &*callee_inst_itr,
context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
@@ -435,6 +441,11 @@ std::unique_ptr<BasicBlock> InlinePass::InlineBasicBlocks(
auto tail_inst_itr = callee_block_itr->end();
for (auto inst_itr = callee_block_itr->begin(); inst_itr != tail_inst_itr;
++inst_itr) {
+ // Don't inline function definition links, the calling function is not a
+ // definition
+ if (inst_itr->GetShader100DebugOpcode() ==
+ NonSemanticShaderDebugInfo100DebugFunctionDefinition)
+ continue;
if (!InlineSingleInstruction(
callee2caller, new_blk_ptr.get(), &*inst_itr,
context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
diff --git a/source/opt/inst_bindless_check_pass.h b/source/opt/inst_bindless_check_pass.h
index cd961805..e6e6ef4f 100644
--- a/source/opt/inst_bindless_check_pass.h
+++ b/source/opt/inst_bindless_check_pass.h
@@ -147,11 +147,11 @@ class InstBindlessCheckPass : public InstrumentPass {
uint32_t GenLastByteIdx(RefAnalysis* ref, InstructionBuilder* builder);
// Clone original image computation starting at |image_id| into |builder|.
- // This may generate more than one instruction if neccessary.
+ // This may generate more than one instruction if necessary.
uint32_t CloneOriginalImage(uint32_t image_id, InstructionBuilder* builder);
// Clone original original reference encapsulated by |ref| into |builder|.
- // This may generate more than one instruction if neccessary.
+ // This may generate more than one instruction if necessary.
uint32_t CloneOriginalReference(RefAnalysis* ref,
InstructionBuilder* builder);
diff --git a/source/opt/inst_buff_addr_check_pass.cpp b/source/opt/inst_buff_addr_check_pass.cpp
index 06acc7ea..e2336d36 100644
--- a/source/opt/inst_buff_addr_check_pass.cpp
+++ b/source/opt/inst_buff_addr_check_pass.cpp
@@ -130,13 +130,48 @@ void InstBuffAddrCheckPass::GenCheckCode(
context()->KillInst(ref_inst);
}
+uint32_t InstBuffAddrCheckPass::GetTypeAlignment(uint32_t type_id) {
+ Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
+ switch (type_inst->opcode()) {
+ case SpvOpTypeFloat:
+ case SpvOpTypeInt:
+ case SpvOpTypeVector:
+ return GetTypeLength(type_id);
+ case SpvOpTypeMatrix:
+ return GetTypeAlignment(type_inst->GetSingleWordInOperand(0));
+ case SpvOpTypeArray:
+ case SpvOpTypeRuntimeArray:
+ return GetTypeAlignment(type_inst->GetSingleWordInOperand(0));
+ case SpvOpTypeStruct: {
+ uint32_t max = 0;
+ type_inst->ForEachInId([&max, this](const uint32_t* iid) {
+ uint32_t alignment = GetTypeAlignment(*iid);
+ max = (alignment > max) ? alignment : max;
+ });
+ return max;
+ }
+ case SpvOpTypePointer:
+ assert(type_inst->GetSingleWordInOperand(0) ==
+ SpvStorageClassPhysicalStorageBufferEXT &&
+ "unexpected pointer type");
+ return 8u;
+ default:
+ assert(false && "unexpected type");
+ return 0;
+ }
+}
+
uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) {
Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
switch (type_inst->opcode()) {
case SpvOpTypeFloat:
case SpvOpTypeInt:
return type_inst->GetSingleWordInOperand(0) / 8u;
- case SpvOpTypeVector:
+ case SpvOpTypeVector: {
+ uint32_t raw_cnt = type_inst->GetSingleWordInOperand(1);
+ uint32_t adj_cnt = (raw_cnt == 3u) ? 4u : raw_cnt;
+ return adj_cnt * GetTypeLength(type_inst->GetSingleWordInOperand(0));
+ }
case SpvOpTypeMatrix:
return type_inst->GetSingleWordInOperand(1) *
GetTypeLength(type_inst->GetSingleWordInOperand(0));
@@ -145,8 +180,29 @@ uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) {
SpvStorageClassPhysicalStorageBufferEXT &&
"unexpected pointer type");
return 8u;
+ case SpvOpTypeArray: {
+ uint32_t const_id = type_inst->GetSingleWordInOperand(1);
+ Instruction* const_inst = get_def_use_mgr()->GetDef(const_id);
+ uint32_t cnt = const_inst->GetSingleWordInOperand(0);
+ return cnt * GetTypeLength(type_inst->GetSingleWordInOperand(0));
+ }
+ case SpvOpTypeStruct: {
+ uint32_t len = 0;
+ type_inst->ForEachInId([&len, this](const uint32_t* iid) {
+ // Align struct length
+ uint32_t alignment = GetTypeAlignment(*iid);
+ uint32_t mod = len % alignment;
+ uint32_t diff = (mod != 0) ? alignment - mod : 0;
+ len += diff;
+ // Increment struct length by component length
+ uint32_t comp_len = GetTypeLength(*iid);
+ len += comp_len;
+ });
+ return len;
+ }
+ case SpvOpTypeRuntimeArray:
default:
- assert(false && "unexpected buffer reference type");
+ assert(false && "unexpected type");
return 0;
}
}
diff --git a/source/opt/inst_buff_addr_check_pass.h b/source/opt/inst_buff_addr_check_pass.h
index ec7bb684..a8232239 100644
--- a/source/opt/inst_buff_addr_check_pass.h
+++ b/source/opt/inst_buff_addr_check_pass.h
@@ -28,7 +28,9 @@ namespace opt {
// external design of this class may change as the layer evolves.
class InstBuffAddrCheckPass : public InstrumentPass {
public:
- // Preferred interface
+ // For test harness only
+ InstBuffAddrCheckPass() : InstrumentPass(7, 23, kInstValidationIdBuffAddr) {}
+ // For all other interfaces
InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id)
: InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr) {}
@@ -40,8 +42,12 @@ class InstBuffAddrCheckPass : public InstrumentPass {
const char* name() const override { return "inst-bindless-check-pass"; }
private:
- // Return byte length of type |type_id|. Must be int, float, vector, matrix
- // or physical pointer.
+ // Return byte alignment of type |type_id|. Must be int, float, vector,
+ // matrix, struct, array or physical pointer. Uses std430 alignment.
+ uint32_t GetTypeAlignment(uint32_t type_id);
+
+ // Return byte length of type |type_id|. Must be int, float, vector, matrix,
+ // struct, array or physical pointer. Uses std430 alignment and sizes.
uint32_t GetTypeLength(uint32_t type_id);
// Add |type_id| param to |input_func| and add id to |param_vec|.
diff --git a/source/opt/inst_debug_printf_pass.cpp b/source/opt/inst_debug_printf_pass.cpp
index c0e6bc3f..4218138f 100644
--- a/source/opt/inst_debug_printf_pass.cpp
+++ b/source/opt/inst_debug_printf_pass.cpp
@@ -16,6 +16,7 @@
#include "inst_debug_printf_pass.h"
+#include "source/util/string_utils.h"
#include "spirv/unified1/NonSemanticDebugPrintf.h"
namespace spvtools {
@@ -231,10 +232,8 @@ Pass::Status InstDebugPrintfPass::ProcessImpl() {
bool non_sem_set_seen = false;
for (auto c_itr = context()->module()->ext_inst_import_begin();
c_itr != context()->module()->ext_inst_import_end(); ++c_itr) {
- const char* set_name =
- reinterpret_cast<const char*>(&c_itr->GetInOperand(0).words[0]);
- const char* non_sem_str = "NonSemantic.";
- if (!strncmp(set_name, non_sem_str, strlen(non_sem_str))) {
+ const std::string set_name = c_itr->GetInOperand(0).AsString();
+ if (spvtools::utils::starts_with(set_name, "NonSemantic.")) {
non_sem_set_seen = true;
break;
}
@@ -242,9 +241,8 @@ Pass::Status InstDebugPrintfPass::ProcessImpl() {
if (!non_sem_set_seen) {
for (auto c_itr = context()->module()->extension_begin();
c_itr != context()->module()->extension_end(); ++c_itr) {
- const char* ext_name =
- reinterpret_cast<const char*>(&c_itr->GetInOperand(0).words[0]);
- if (!strcmp(ext_name, "SPV_KHR_non_semantic_info")) {
+ const std::string ext_name = c_itr->GetInOperand(0).AsString();
+ if (ext_name == "SPV_KHR_non_semantic_info") {
context()->KillInst(&*c_itr);
break;
}
diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp
index 1054a203..2461e41e 100644
--- a/source/opt/instruction.cpp
+++ b/source/opt/instruction.cpp
@@ -30,9 +30,11 @@ namespace {
const uint32_t kTypeImageDimIndex = 1;
const uint32_t kLoadBaseIndex = 0;
const uint32_t kPointerTypeStorageClassIndex = 0;
+const uint32_t kVariableStorageClassIndex = 0;
const uint32_t kTypeImageSampledIndex = 5;
-// Constants for OpenCL.DebugInfo.100 extension instructions.
+// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100
+// extension instructions.
const uint32_t kExtInstSetIdInIdx = 0;
const uint32_t kExtInstInstructionInIdx = 1;
const uint32_t kDebugScopeNumWords = 7;
@@ -64,15 +66,14 @@ Instruction::Instruction(IRContext* c, SpvOp op)
Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
std::vector<Instruction>&& dbg_line)
- : context_(c),
+ : utils::IntrusiveNodeBase<Instruction>(),
+ context_(c),
opcode_(static_cast<SpvOp>(inst.opcode)),
has_type_id_(inst.type_id != 0),
has_result_id_(inst.result_id != 0),
unique_id_(c->TakeNextUniqueId()),
dbg_line_insts_(std::move(dbg_line)),
dbg_scope_(kNoDebugScope, kNoInlinedAt) {
- assert((!IsDebugLineInst(opcode_) || dbg_line.empty()) &&
- "Op(No)Line attaching to Op(No)Line found");
for (uint32_t i = 0; i < inst.num_operands; ++i) {
const auto& current_payload = inst.operands[i];
std::vector<uint32_t> words(
@@ -80,11 +81,14 @@ Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
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");
}
Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
const DebugScope& dbg_scope)
- : context_(c),
+ : utils::IntrusiveNodeBase<Instruction>(),
+ context_(c),
opcode_(static_cast<SpvOp>(inst.opcode)),
has_type_id_(inst.type_id != 0),
has_result_id_(inst.result_id != 0),
@@ -122,6 +126,7 @@ Instruction::Instruction(IRContext* c, SpvOp op, uint32_t ty_id,
Instruction::Instruction(Instruction&& that)
: utils::IntrusiveNodeBase<Instruction>(),
+ context_(that.context_),
opcode_(that.opcode_),
has_type_id_(that.has_type_id_),
has_result_id_(that.has_result_id_),
@@ -135,6 +140,7 @@ Instruction::Instruction(Instruction&& that)
}
Instruction& Instruction::operator=(Instruction&& that) {
+ context_ = that.context_;
opcode_ = that.opcode_;
has_type_id_ = that.has_type_id_;
has_result_id_ = that.has_result_id_;
@@ -153,6 +159,10 @@ Instruction* Instruction::Clone(IRContext* c) const {
clone->unique_id_ = c->TakeNextUniqueId();
clone->operands_ = operands_;
clone->dbg_line_insts_ = dbg_line_insts_;
+ for (auto& i : clone->dbg_line_insts_) {
+ i.unique_id_ = c->TakeNextUniqueId();
+ if (i.IsDebugLineInst()) i.SetResultId(c->TakeNextId());
+ }
clone->dbg_scope_ = dbg_scope_;
return clone;
}
@@ -394,6 +404,21 @@ bool Instruction::IsVulkanStorageBuffer() const {
return false;
}
+bool Instruction::IsVulkanStorageBufferVariable() const {
+ if (opcode() != SpvOpVariable) {
+ return false;
+ }
+
+ uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
+ if (storage_class == SpvStorageClassStorageBuffer ||
+ storage_class == SpvStorageClassUniform) {
+ Instruction* var_type = context()->get_def_use_mgr()->GetDef(type_id());
+ return var_type != nullptr && var_type->IsVulkanStorageBuffer();
+ }
+
+ return false;
+}
+
bool Instruction::IsVulkanUniformBuffer() const {
if (opcode() != SpvOpTypePointer) {
return false;
@@ -506,7 +531,7 @@ void Instruction::UpdateLexicalScope(uint32_t scope) {
for (auto& i : dbg_line_insts_) {
i.dbg_scope_.SetLexicalScope(scope);
}
- if (!IsDebugLineInst(opcode()) &&
+ if (!IsLineInst() &&
context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) {
context()->get_debug_info_mgr()->AnalyzeDebugInst(this);
}
@@ -517,24 +542,61 @@ void Instruction::UpdateDebugInlinedAt(uint32_t new_inlined_at) {
for (auto& i : dbg_line_insts_) {
i.dbg_scope_.SetInlinedAt(new_inlined_at);
}
- if (!IsDebugLineInst(opcode()) &&
+ if (!IsLineInst() &&
context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) {
context()->get_debug_info_mgr()->AnalyzeDebugInst(this);
}
}
+void Instruction::ClearDbgLineInsts() {
+ if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) {
+ auto def_use_mgr = context()->get_def_use_mgr();
+ for (auto& l_inst : dbg_line_insts_) def_use_mgr->ClearInst(&l_inst);
+ }
+ clear_dbg_line_insts();
+}
+
void Instruction::UpdateDebugInfoFrom(const Instruction* from) {
if (from == nullptr) return;
- clear_dbg_line_insts();
+ ClearDbgLineInsts();
if (!from->dbg_line_insts().empty())
- dbg_line_insts().push_back(from->dbg_line_insts().back());
+ AddDebugLine(&from->dbg_line_insts().back());
SetDebugScope(from->GetDebugScope());
- if (!IsDebugLineInst(opcode()) &&
+ if (!IsLineInst() &&
context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) {
context()->get_debug_info_mgr()->AnalyzeDebugInst(this);
}
}
+void Instruction::AddDebugLine(const Instruction* inst) {
+ dbg_line_insts_.push_back(*inst);
+ dbg_line_insts_.back().unique_id_ = context()->TakeNextUniqueId();
+ if (inst->IsDebugLineInst())
+ dbg_line_insts_.back().SetResultId(context_->TakeNextId());
+ if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(&dbg_line_insts_.back());
+}
+
+bool Instruction::IsDebugLineInst() const {
+ NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode();
+ return ((ext_opt == NonSemanticShaderDebugInfo100DebugLine) ||
+ (ext_opt == NonSemanticShaderDebugInfo100DebugNoLine));
+}
+
+bool Instruction::IsLineInst() const { return IsLine() || IsNoLine(); }
+
+bool Instruction::IsLine() const {
+ if (opcode() == SpvOpLine) return true;
+ NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode();
+ return ext_opt == NonSemanticShaderDebugInfo100DebugLine;
+}
+
+bool Instruction::IsNoLine() const {
+ if (opcode() == SpvOpNoLine) return true;
+ NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode();
+ return ext_opt == NonSemanticShaderDebugInfo100DebugNoLine;
+}
+
Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& inst) {
inst.get()->InsertBefore(this);
return inst.release();
@@ -618,6 +680,49 @@ OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const {
GetSingleWordInOperand(kExtInstInstructionInIdx));
}
+NonSemanticShaderDebugInfo100Instructions Instruction::GetShader100DebugOpcode()
+ const {
+ if (opcode() != SpvOpExtInst) {
+ return NonSemanticShaderDebugInfo100InstructionsMax;
+ }
+
+ if (!context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) {
+ return NonSemanticShaderDebugInfo100InstructionsMax;
+ }
+
+ if (GetSingleWordInOperand(kExtInstSetIdInIdx) !=
+ context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) {
+ return NonSemanticShaderDebugInfo100InstructionsMax;
+ }
+
+ return NonSemanticShaderDebugInfo100Instructions(
+ GetSingleWordInOperand(kExtInstInstructionInIdx));
+}
+
+CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const {
+ if (opcode() != SpvOpExtInst) {
+ return CommonDebugInfoInstructionsMax;
+ }
+
+ const uint32_t opencl_set_id =
+ context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo();
+ const uint32_t shader_set_id =
+ context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo();
+
+ if (!opencl_set_id && !shader_set_id) {
+ return CommonDebugInfoInstructionsMax;
+ }
+
+ const uint32_t used_set_id = GetSingleWordInOperand(kExtInstSetIdInIdx);
+
+ if (used_set_id != opencl_set_id && used_set_id != shader_set_id) {
+ return CommonDebugInfoInstructionsMax;
+ }
+
+ return CommonDebugInfoInstructions(
+ GetSingleWordInOperand(kExtInstInstructionInIdx));
+}
+
bool Instruction::IsValidBaseImage() const {
uint32_t tid = type_id();
if (tid == 0) {
@@ -940,10 +1045,10 @@ void DebugScope::ToBinary(uint32_t type_id, uint32_t result_id,
uint32_t ext_set,
std::vector<uint32_t>* binary) const {
uint32_t num_words = kDebugScopeNumWords;
- OpenCLDebugInfo100Instructions dbg_opcode = OpenCLDebugInfo100DebugScope;
+ CommonDebugInfoInstructions dbg_opcode = CommonDebugInfoDebugScope;
if (GetLexicalScope() == kNoDebugScope) {
num_words = kDebugNoScopeNumWords;
- dbg_opcode = OpenCLDebugInfo100DebugNoScope;
+ dbg_opcode = CommonDebugInfoDebugNoScope;
} else if (GetInlinedAt() == kNoInlinedAt) {
num_words = kDebugScopeNumWordsWithoutInlinedAt;
}
diff --git a/source/opt/instruction.h b/source/opt/instruction.h
index 3e557dd7..f87f563a 100644
--- a/source/opt/instruction.h
+++ b/source/opt/instruction.h
@@ -22,7 +22,10 @@
#include <utility>
#include <vector>
+#include "NonSemanticShaderDebugInfo100.h"
#include "OpenCLDebugInfo100.h"
+#include "source/binary.h"
+#include "source/common_debug_info.h"
#include "source/latest_version_glsl_std_450_header.h"
#include "source/latest_version_spirv_header.h"
#include "source/opcode.h"
@@ -30,6 +33,7 @@
#include "source/opt/reflect.h"
#include "source/util/ilist_node.h"
#include "source/util/small_vector.h"
+#include "source/util/string_utils.h"
#include "spirv-tools/libspirv.h"
const uint32_t kNoDebugScope = 0;
@@ -83,15 +87,12 @@ struct Operand {
spv_operand_type_t type; // Type of this logical operand.
OperandData words; // Binary segments of this logical operand.
- // Returns a string operand as a C-style string.
- const char* AsCString() const {
+ // Returns a string operand as a std::string.
+ std::string AsString() const {
assert(type == SPV_OPERAND_TYPE_LITERAL_STRING);
- return reinterpret_cast<const char*>(words.data());
+ return spvtools::utils::MakeString(words);
}
- // Returns a string operand as a std::string.
- std::string AsString() const { return AsCString(); }
-
// Returns a literal integer operand as a uint64_t
uint64_t AsLiteralUint64() const {
assert(type == SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER);
@@ -121,7 +122,7 @@ inline bool operator!=(const Operand& o1, const Operand& o2) {
}
// This structure is used to represent a DebugScope instruction from
-// the OpenCL.100.DebugInfo extened instruction set. Note that we can
+// the OpenCL.100.DebugInfo extended instruction set. Note that we can
// ignore the result id of DebugScope instruction because it is not
// used for anything. We do not keep it to reduce the size of
// structure.
@@ -250,11 +251,6 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// Clear line-related debug instructions attached to this instruction.
void clear_dbg_line_insts() { dbg_line_insts_.clear(); }
- // Set line-related debug instructions.
- void set_dbg_line_insts(const std::vector<Instruction>& lines) {
- dbg_line_insts_ = lines;
- }
-
// Same semantics as in the base class except the list the InstructionList
// containing |pos| will now assume ownership of |this|.
// inline void MoveBefore(Instruction* pos);
@@ -304,8 +300,21 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// Sets DebugScope.
inline void SetDebugScope(const DebugScope& scope);
inline const DebugScope& GetDebugScope() const { return dbg_scope_; }
+ // Add debug line inst. Renew result id if Debug[No]Line
+ void AddDebugLine(const Instruction* inst);
// Updates DebugInlinedAt of DebugScope and OpLine.
void UpdateDebugInlinedAt(uint32_t new_inlined_at);
+ // Clear line-related debug instructions attached to this instruction
+ // along with def-use entries.
+ void ClearDbgLineInsts();
+ // Return true if Shader100:Debug[No]Line
+ bool IsDebugLineInst() const;
+ // Return true if Op[No]Line or Shader100:Debug[No]Line
+ bool IsLineInst() const;
+ // Return true if OpLine or Shader100:DebugLine
+ bool IsLine() const;
+ // Return true if OpNoLine or Shader100:DebugNoLine
+ bool IsNoLine() const;
inline uint32_t GetDebugInlinedAt() const {
return dbg_scope_.GetInlinedAt();
}
@@ -454,6 +463,10 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// storage buffer.
bool IsVulkanStorageBuffer() const;
+ // Returns true if the instruction defines a variable in StorageBuffer or
+ // Uniform storage class with a pointer type that points to a storage buffer.
+ bool IsVulkanStorageBufferVariable() const;
+
// Returns true if the instruction defines a pointer type that points to a
// uniform buffer.
bool IsVulkanUniformBuffer() const;
@@ -550,11 +563,30 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// OpenCLDebugInfo100InstructionsMax.
OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const;
+ // Returns debug opcode of an NonSemantic.Shader.DebugInfo.100 instruction. If
+ // it is not an NonSemantic.Shader.DebugInfo.100 instruction, just return
+ // NonSemanticShaderDebugInfo100InstructionsMax.
+ NonSemanticShaderDebugInfo100Instructions GetShader100DebugOpcode() const;
+
+ // Returns debug opcode of an OpenCL.100.DebugInfo or
+ // NonSemantic.Shader.DebugInfo.100 instruction. Since these overlap, we
+ // return the OpenCLDebugInfo code
+ CommonDebugInfoInstructions GetCommonDebugOpcode() const;
+
// Returns true if it is an OpenCL.DebugInfo.100 instruction.
bool IsOpenCL100DebugInstr() const {
return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax;
}
+ // Returns true if it is an NonSemantic.Shader.DebugInfo.100 instruction.
+ bool IsShader100DebugInstr() const {
+ return GetShader100DebugOpcode() !=
+ NonSemanticShaderDebugInfo100InstructionsMax;
+ }
+ bool IsCommonDebugInstr() const {
+ return GetCommonDebugOpcode() != CommonDebugInfoInstructionsMax;
+ }
+
// Returns true if this instructions a non-semantic instruction.
bool IsNonSemanticInstruction() const;
@@ -588,9 +620,9 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
uint32_t unique_id_; // Unique instruction id
// All logical operands, including result type id and result id.
OperandList operands_;
- // Opline and OpNoLine instructions preceding this instruction. Note that for
- // Instructions representing OpLine or OpNonLine itself, this field should be
- // empty.
+ // Op[No]Line or Debug[No]Line instructions preceding this instruction. Note
+ // that for Instructions representing Op[No]Line or Debug[No]Line themselves,
+ // this field should be empty.
std::vector<Instruction> dbg_line_insts_;
// DebugScope that wraps this instruction.
diff --git a/source/opt/instrument_pass.h b/source/opt/instrument_pass.h
index 12b939d4..90c1dd47 100644
--- a/source/opt/instrument_pass.h
+++ b/source/opt/instrument_pass.h
@@ -50,7 +50,7 @@
// A validation pass may read or write multiple buffers. All such buffers
// are located in a single debug descriptor set whose index is passed at the
// creation of the instrumentation pass. The bindings of the buffers used by
-// a validation pass are permanantly assigned and fixed and documented by
+// a validation pass are permanently assigned and fixed and documented by
// the kDebugOutput* static consts.
namespace spvtools {
@@ -179,8 +179,8 @@ class InstrumentPass : public Pass {
// the error. Every stage will write a fixed number of words. Vertex shaders
// will write the Vertex and Instance ID. Fragment shaders will write
// FragCoord.xy. Compute shaders will write the GlobalInvocation ID.
- // The tesselation eval shader will write the Primitive ID and TessCoords.uv.
- // The tesselation control shader and geometry shader will write the
+ // The tessellation eval shader will write the Primitive ID and TessCoords.uv.
+ // The tessellation control shader and geometry shader will write the
// Primitive ID and Invocation ID.
//
// The Validation Error Code specifies the exact error which has occurred.
diff --git a/source/opt/ir_builder.h b/source/opt/ir_builder.h
index fe5feff5..4433cf0d 100644
--- a/source/opt/ir_builder.h
+++ b/source/opt/ir_builder.h
@@ -359,8 +359,9 @@ class InstructionBuilder {
return AddInstruction(std::move(select));
}
- // Adds a signed int32 constant to the binary.
- // The |value| parameter is the constant value to be added.
+ // Returns a pointer to the definition of a signed 32-bit integer constant
+ // with the given value. Returns |nullptr| if the constant does not exist and
+ // cannot be created.
Instruction* GetSintConstant(int32_t value) {
return GetIntConstant<int32_t>(value, true);
}
@@ -381,21 +382,24 @@ class InstructionBuilder {
GetContext()->TakeNextId(), ops));
return AddInstruction(std::move(construct));
}
- // Adds an unsigned int32 constant to the binary.
- // The |value| parameter is the constant value to be added.
+
+ // Returns a pointer to the definition of an unsigned 32-bit integer constant
+ // with the given value. Returns |nullptr| if the constant does not exist and
+ // cannot be created.
Instruction* GetUintConstant(uint32_t value) {
return GetIntConstant<uint32_t>(value, false);
}
uint32_t GetUintConstantId(uint32_t value) {
Instruction* uint_inst = GetUintConstant(value);
- return uint_inst->result_id();
+ return (uint_inst != nullptr ? uint_inst->result_id() : 0);
}
// Adds either a signed or unsigned 32 bit integer constant to the binary
- // depedning on the |sign|. If |sign| is true then the value is added as a
+ // depending on the |sign|. If |sign| is true then the value is added as a
// signed constant otherwise as an unsigned constant. If |sign| is false the
- // value must not be a negative number.
+ // value must not be a negative number. Returns false if the constant does
+ // not exists and could be be created.
template <typename T>
Instruction* GetIntConstant(T value, bool sign) {
// Assert that we are not trying to store a negative number in an unsigned
@@ -411,6 +415,10 @@ class InstructionBuilder {
uint32_t type_id =
GetContext()->get_type_mgr()->GetTypeInstruction(&int_type);
+ if (type_id == 0) {
+ return nullptr;
+ }
+
// Get the memory managed type so that it is safe to be stored by
// GetConstant.
analysis::Type* rebuilt_type =
diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp
index 03afe6e8..5b0beeb2 100644
--- a/source/opt/ir_context.cpp
+++ b/source/opt/ir_context.cpp
@@ -30,7 +30,8 @@ static const int kSpvDecorateBuiltinInIdx = 2;
static const int kEntryPointInterfaceInIdx = 3;
static const int kEntryPointFunctionIdInIdx = 1;
-// Constants for OpenCL.DebugInfo.100 extension instructions.
+// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100
+// extension instructions.
static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
static const uint32_t kDebugGlobalVariableOperandVariableIndex = 11;
@@ -105,7 +106,7 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
analyses_to_invalidate |= kAnalysisDebugInfo;
}
- // The dominator analysis hold the psuedo entry and exit nodes from the CFG.
+ // The dominator analysis hold the pseudo entry and exit nodes from the CFG.
// Also if the CFG change the dominators many changed as well, so the
// dominator analysis should be invalidated as well.
if (analyses_to_invalidate & kAnalysisCFG) {
@@ -170,7 +171,9 @@ Instruction* IRContext::KillInst(Instruction* inst) {
KillOperandFromDebugInstructions(inst);
if (AreAnalysesValid(kAnalysisDefUse)) {
- get_def_use_mgr()->ClearInst(inst);
+ analysis::DefUseManager* def_use_mgr = get_def_use_mgr();
+ def_use_mgr->ClearInst(inst);
+ for (auto& l_inst : inst->dbg_line_insts()) def_use_mgr->ClearInst(&l_inst);
}
if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) {
instr_to_block_.erase(inst);
@@ -217,6 +220,8 @@ Instruction* IRContext::KillInst(Instruction* inst) {
void IRContext::CollectNonSemanticTree(
Instruction* inst, std::unordered_set<Instruction*>* to_kill) {
if (!inst->HasResultId()) return;
+ // Debug[No]Line result id is not used, so we are done
+ if (inst->IsDebugLineInst()) return;
std::vector<Instruction*> work_list;
std::unordered_set<Instruction*> seen;
work_list.push_back(inst);
@@ -312,7 +317,7 @@ bool IRContext::IsConsistent() {
#else
if (AreAnalysesValid(kAnalysisDefUse)) {
analysis::DefUseManager new_def_use(module());
- if (*get_def_use_mgr() != new_def_use) {
+ if (!CompareAndPrintDifferences(*get_def_use_mgr(), new_def_use)) {
return false;
}
}
@@ -437,8 +442,7 @@ void IRContext::KillOperandFromDebugInstructions(Instruction* inst) {
if (opcode == SpvOpVariable || IsConstantInst(opcode)) {
for (auto it = module()->ext_inst_debuginfo_begin();
it != module()->ext_inst_debuginfo_end(); ++it) {
- if (it->GetOpenCL100DebugOpcode() !=
- OpenCLDebugInfo100DebugGlobalVariable)
+ if (it->GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable)
continue;
auto& operand = it->GetOperand(kDebugGlobalVariableOperandVariableIndex);
if (operand.words[0] == id) {
@@ -619,9 +623,8 @@ void IRContext::AddCombinatorsForCapability(uint32_t capability) {
void IRContext::AddCombinatorsForExtension(Instruction* extension) {
assert(extension->opcode() == SpvOpExtInstImport &&
"Expecting an import of an extension's instruction set.");
- const char* extension_name =
- reinterpret_cast<const char*>(&extension->GetInOperand(0).words[0]);
- if (!strcmp(extension_name, "GLSL.std.450")) {
+ const std::string extension_name = extension->GetInOperand(0).AsString();
+ if (extension_name == "GLSL.std.450") {
combinator_ops_[extension->result_id()] = {GLSLstd450Round,
GLSLstd450RoundEven,
GLSLstd450Trunc,
@@ -930,7 +933,7 @@ void IRContext::EmitErrorMessage(std::string message, Instruction* inst) {
while (line_inst != nullptr) { // Stop at the beginning of the basic block.
if (!line_inst->dbg_line_insts().empty()) {
line_inst = &line_inst->dbg_line_insts().back();
- if (line_inst->opcode() == SpvOpNoLine) {
+ if (line_inst->IsNoLine()) {
line_inst = nullptr;
}
break;
@@ -940,11 +943,11 @@ void IRContext::EmitErrorMessage(std::string message, Instruction* inst) {
uint32_t line_number = 0;
uint32_t col_number = 0;
- char* source = nullptr;
+ std::string source;
if (line_inst != nullptr) {
Instruction* file_name =
get_def_use_mgr()->GetDef(line_inst->GetSingleWordInOperand(0));
- source = reinterpret_cast<char*>(&file_name->GetInOperand(0).words[0]);
+ source = file_name->GetInOperand(0).AsString();
// Get the line number and column number.
line_number = line_inst->GetSingleWordInOperand(1);
@@ -953,7 +956,7 @@ void IRContext::EmitErrorMessage(std::string message, Instruction* inst) {
message +=
"\n " + inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
- consumer()(SPV_MSG_ERROR, source, {line_number, col_number, 0},
+ consumer()(SPV_MSG_ERROR, source.c_str(), {line_number, col_number, 0},
message.c_str());
}
@@ -1034,5 +1037,11 @@ bool IRContext::CheckCFG() {
return true;
}
+
+bool IRContext::IsReachable(const opt::BasicBlock& bb) {
+ auto enclosing_function = bb.GetParent();
+ return GetDominatorAnalysis(enclosing_function)
+ ->Dominates(enclosing_function->entry().get(), &bb);
+}
} // namespace opt
} // namespace spvtools
diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h
index aab35162..274dd14e 100644
--- a/source/opt/ir_context.h
+++ b/source/opt/ir_context.h
@@ -43,6 +43,7 @@
#include "source/opt/type_manager.h"
#include "source/opt/value_number_table.h"
#include "source/util/make_unique.h"
+#include "source/util/string_utils.h"
namespace spvtools {
namespace opt {
@@ -98,6 +99,7 @@ class IRContext {
module_(new Module()),
consumer_(std::move(c)),
def_use_mgr_(nullptr),
+ feature_mgr_(nullptr),
valid_analyses_(kAnalysisNone),
constant_mgr_(nullptr),
type_mgr_(nullptr),
@@ -116,6 +118,7 @@ class IRContext {
module_(std::move(m)),
consumer_(std::move(c)),
def_use_mgr_(nullptr),
+ feature_mgr_(nullptr),
valid_analyses_(kAnalysisNone),
type_mgr_(nullptr),
id_to_name_(nullptr),
@@ -190,8 +193,8 @@ class IRContext {
inline IteratorRange<Module::const_inst_iterator> debugs3() const;
// Iterators for debug info instructions (excluding OpLine & OpNoLine)
- // contained in this module. These are OpExtInst for OpenCL.DebugInfo.100
- // or DebugInfo extension placed between section 9 and 10.
+ // contained in this module. These are OpExtInst for DebugInfo extension
+ // placed between section 9 and 10.
inline Module::inst_iterator ext_inst_debuginfo_begin();
inline Module::inst_iterator ext_inst_debuginfo_end();
inline IteratorRange<Module::inst_iterator> ext_inst_debuginfo();
@@ -299,7 +302,7 @@ class IRContext {
}
}
- // Returns a pointer the decoration manager. If the decoration manger is
+ // Returns a pointer the decoration manager. If the decoration manager is
// invalid, it is rebuilt first.
analysis::DecorationManager* get_decoration_mgr() {
if (!AreAnalysesValid(kAnalysisDecorations)) {
@@ -382,7 +385,7 @@ class IRContext {
// Deletes the instruction defining the given |id|. Returns true on
// success, false if the given |id| is not defined at all. This method also
- // erases the name, decorations, and defintion of |id|.
+ // erases the name, decorations, and definition of |id|.
//
// Pointers and iterators pointing to the deleted instructions become invalid.
// However other pointers and iterators are still valid.
@@ -516,6 +519,18 @@ class IRContext {
std::string message = "ID overflow. Try running compact-ids.";
consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str());
}
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ // If TakeNextId returns 0, it is very likely that execution will
+ // subsequently fail. Such failures are false alarms from a fuzzing point
+ // of view: they are due to the fact that too many ids were used, rather
+ // than being due to an actual bug. Thus, during a fuzzing build, it is
+ // preferable to bail out when ID overflow occurs.
+ //
+ // A zero exit code is returned here because a non-zero code would cause
+ // ClusterFuzz/OSS-Fuzz to regard the termination as a crash, and spurious
+ // crash reports is what this guard aims to avoid.
+ exit(0);
+#endif
}
return next_id;
}
@@ -598,10 +613,14 @@ class IRContext {
bool ProcessCallTreeFromRoots(ProcessFunction& pfn,
std::queue<uint32_t>* roots);
- // Emmits a error message to the message consumer indicating the error
+ // Emits a error message to the message consumer indicating the error
// described by |message| occurred in |inst|.
void EmitErrorMessage(std::string message, Instruction* inst);
+ // Returns true if and only if there is a path to |bb| from the entry block of
+ // the function that contains |bb|.
+ bool IsReachable(const opt::BasicBlock& bb);
+
private:
// Builds the def-use manager from scratch, even if it was already valid.
void BuildDefUseManager() {
@@ -765,6 +784,8 @@ class IRContext {
// The instruction decoration manager for |module_|.
std::unique_ptr<analysis::DecorationManager> decoration_mgr_;
+
+ // The feature manager for |module_|.
std::unique_ptr<FeatureManager> feature_mgr_;
// A map from instructions to the basic block they belong to. This mapping is
@@ -781,7 +802,7 @@ class IRContext {
// iterators to traverse instructions.
std::unordered_map<uint32_t, Function*> id_to_func_;
- // A bitset indicating which analyes are currently valid.
+ // A bitset indicating which analyzes are currently valid.
Analysis valid_analyses_;
// Opcodes of shader capability core executable instructions
@@ -1012,11 +1033,7 @@ void IRContext::AddCapability(std::unique_ptr<Instruction>&& c) {
}
void IRContext::AddExtension(const std::string& ext_name) {
- const auto num_chars = ext_name.size();
- // Compute num words, accommodate the terminating null character.
- const auto num_words = (num_chars + 1 + 3) / 4;
- std::vector<uint32_t> ext_words(num_words, 0u);
- std::memcpy(ext_words.data(), ext_name.data(), num_chars);
+ std::vector<uint32_t> ext_words = spvtools::utils::MakeVector(ext_name);
AddExtension(std::unique_ptr<Instruction>(
new Instruction(this, SpvOpExtension, 0u, 0u,
{{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}})));
@@ -1033,11 +1050,7 @@ void IRContext::AddExtension(std::unique_ptr<Instruction>&& e) {
}
void IRContext::AddExtInstImport(const std::string& name) {
- const auto num_chars = name.size();
- // Compute num words, accommodate the terminating null character.
- const auto num_words = (num_chars + 1 + 3) / 4;
- std::vector<uint32_t> ext_words(num_words, 0u);
- std::memcpy(ext_words.data(), name.data(), num_chars);
+ std::vector<uint32_t> ext_words = spvtools::utils::MakeVector(name);
AddExtInstImport(std::unique_ptr<Instruction>(
new Instruction(this, SpvOpExtInstImport, 0u, TakeNextId(),
{{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}})));
diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp
index e443ebb5..a82b530e 100644
--- a/source/opt/ir_loader.cpp
+++ b/source/opt/ir_loader.cpp
@@ -19,6 +19,7 @@
#include "DebugInfo.h"
#include "OpenCLDebugInfo100.h"
#include "source/ext_inst.h"
+#include "source/opt/ir_context.h"
#include "source/opt/log.h"
#include "source/opt/reflect.h"
#include "source/util/make_unique.h"
@@ -37,26 +38,40 @@ IrLoader::IrLoader(const MessageConsumer& consumer, Module* m)
inst_index_(0),
last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
+bool IsLineInst(const spv_parsed_instruction_t* inst) {
+ const auto opcode = static_cast<SpvOp>(inst->opcode);
+ if (IsOpLineInst(opcode)) return true;
+ if (opcode != SpvOpExtInst) return false;
+ if (inst->ext_inst_type != SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100)
+ return false;
+ const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
+ const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
+ NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
+ return ext_inst_key == NonSemanticShaderDebugInfo100DebugLine ||
+ ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine;
+}
+
bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
++inst_index_;
- const auto opcode = static_cast<SpvOp>(inst->opcode);
- if (IsDebugLineInst(opcode)) {
+ if (IsLineInst(inst)) {
module()->SetContainsDebugInfo();
last_line_inst_.reset();
- dbg_line_info_.push_back(
- Instruction(module()->context(), *inst, last_dbg_scope_));
+ dbg_line_info_.emplace_back(module()->context(), *inst, last_dbg_scope_);
return true;
}
// If it is a DebugScope or DebugNoScope of debug extension, we do not
// create a new instruction, but simply keep the information in
// struct DebugScope.
+ const auto opcode = static_cast<SpvOp>(inst->opcode);
if (opcode == SpvOpExtInst && spvExtInstIsDebugInfo(inst->ext_inst_type)) {
const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
- if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
- const OpenCLDebugInfo100Instructions ext_inst_key =
- OpenCLDebugInfo100Instructions(ext_inst_index);
- if (ext_inst_key == OpenCLDebugInfo100DebugScope) {
+ if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
+ inst->ext_inst_type ==
+ SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
+ const CommonDebugInfoInstructions ext_inst_key =
+ CommonDebugInfoInstructions(ext_inst_index);
+ if (ext_inst_key == CommonDebugInfoDebugScope) {
uint32_t inlined_at = 0;
if (inst->num_words > kInlinedAtIndex)
inlined_at = inst->words[kInlinedAtIndex];
@@ -65,7 +80,7 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
module()->SetContainsDebugInfo();
return true;
}
- if (ext_inst_key == OpenCLDebugInfo100DebugNoScope) {
+ if (ext_inst_key == CommonDebugInfoDebugNoScope) {
last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
module()->SetContainsDebugInfo();
return true;
@@ -94,14 +109,20 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
new Instruction(module()->context(), *inst, std::move(dbg_line_info_)));
if (!spv_inst->dbg_line_insts().empty()) {
if (extra_line_tracking_ &&
- (spv_inst->dbg_line_insts().back().opcode() != SpvOpNoLine)) {
+ (!spv_inst->dbg_line_insts().back().IsNoLine())) {
last_line_inst_ = std::unique_ptr<Instruction>(
spv_inst->dbg_line_insts().back().Clone(module()->context()));
+ if (last_line_inst_->IsDebugLineInst())
+ last_line_inst_->SetResultId(module()->context()->TakeNextId());
}
dbg_line_info_.clear();
} else if (last_line_inst_ != nullptr) {
last_line_inst_->SetDebugScope(last_dbg_scope_);
spv_inst->dbg_line_insts().push_back(*last_line_inst_);
+ last_line_inst_ = std::unique_ptr<Instruction>(
+ spv_inst->dbg_line_insts().back().Clone(module()->context()));
+ if (last_line_inst_->IsDebugLineInst())
+ last_line_inst_->SetResultId(module()->context()->TakeNextId());
}
const char* src = source_.c_str();
@@ -233,6 +254,35 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
default: {
Errorf(consumer_, src, loc,
"Debug info extension instruction other than DebugScope, "
+ "DebugNoScope, DebugFunctionDefinition, DebugDeclare, and "
+ "DebugValue found inside function",
+ opcode);
+ return false;
+ }
+ }
+ } else if (inst->ext_inst_type ==
+ SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
+ const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
+ NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
+ switch (ext_inst_key) {
+ case NonSemanticShaderDebugInfo100DebugDeclare:
+ case NonSemanticShaderDebugInfo100DebugValue:
+ case NonSemanticShaderDebugInfo100DebugScope:
+ case NonSemanticShaderDebugInfo100DebugNoScope:
+ case NonSemanticShaderDebugInfo100DebugFunctionDefinition: {
+ if (block_ == nullptr) { // Inside function but outside blocks
+ Errorf(consumer_, src, loc,
+ "Debug info extension instruction found inside function "
+ "but outside block",
+ opcode);
+ } else {
+ block_->AddInstruction(std::move(spv_inst));
+ }
+ break;
+ }
+ default: {
+ Errorf(consumer_, src, loc,
+ "Debug info extension instruction other than DebugScope, "
"DebugNoScope, DebugDeclare, and DebugValue found inside "
"function",
opcode);
diff --git a/source/opt/iterator.h b/source/opt/iterator.h
index 2280582d..847e1bbd 100644
--- a/source/opt/iterator.h
+++ b/source/opt/iterator.h
@@ -142,7 +142,7 @@ inline IteratorRange<IteratorType> make_range(const IteratorType& begin,
template <typename IteratorType>
inline IteratorRange<IteratorType> make_range(IteratorType&& begin,
IteratorType&& end) {
- return {std::move(begin), std::move(end)};
+ return {std::forward<IteratorType>(begin), std::forward<IteratorType>(end)};
}
// Returns a (begin, end) iterator pair for the given container.
diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp
index 5c10ecec..d2059f5c 100644
--- a/source/opt/local_access_chain_convert_pass.cpp
+++ b/source/opt/local_access_chain_convert_pass.cpp
@@ -19,6 +19,7 @@
#include "ir_builder.h"
#include "ir_context.h"
#include "iterator.h"
+#include "source/util/string_utils.h"
namespace spvtools {
namespace opt {
@@ -184,8 +185,8 @@ bool LocalAccessChainConvertPass::IsConstantIndexAccessChain(
bool LocalAccessChainConvertPass::HasOnlySupportedRefs(uint32_t ptrId) {
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true;
if (get_def_use_mgr()->WhileEachUser(ptrId, [this](Instruction* user) {
- if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue ||
- user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) {
+ if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue ||
+ user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) {
return true;
}
SpvOp op = user->opcode();
@@ -328,11 +329,22 @@ bool LocalAccessChainConvertPass::AllExtensionsSupported() const {
return false;
// If any extension not in allowlist, return false
for (auto& ei : get_module()->extensions()) {
- const char* extName =
- reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
+ const std::string extName = ei.GetInOperand(0).AsString();
if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
return false;
}
+ // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise
+ // around unknown extended
+ // instruction sets even if they are non-semantic
+ for (auto& inst : context()->module()->ext_inst_imports()) {
+ assert(inst.opcode() == SpvOpExtInstImport &&
+ "Expecting an import of an extension's instruction set.");
+ const std::string extension_name = inst.GetInOperand(0).AsString();
+ if (spvtools::utils::starts_with(extension_name, "NonSemantic.") &&
+ extension_name != "NonSemantic.Shader.DebugInfo.100") {
+ return false;
+ }
+ }
return true;
}
@@ -420,6 +432,8 @@ void LocalAccessChainConvertPass::InitExtensions() {
"SPV_KHR_terminate_invocation",
"SPV_KHR_subgroup_uniform_control_flow",
"SPV_KHR_integer_dot_product",
+ "SPV_EXT_shader_image_int64",
+ "SPV_KHR_non_semantic_info",
});
}
diff --git a/source/opt/local_access_chain_convert_pass.h b/source/opt/local_access_chain_convert_pass.h
index 552062e5..a51660f1 100644
--- a/source/opt/local_access_chain_convert_pass.h
+++ b/source/opt/local_access_chain_convert_pass.h
@@ -81,7 +81,7 @@ class LocalAccessChainConvertPass : public MemPass {
std::vector<Operand>* in_opnds);
// Create a load/insert/store equivalent to a store of
- // |valId| through (constant index) access chaing |ptrInst|.
+ // |valId| through (constant index) access chain |ptrInst|.
// Append to |newInsts|. Returns true if successful.
bool GenAccessChainStoreReplacement(
const Instruction* ptrInst, uint32_t valId,
diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp
index 05ed28ae..f48c56aa 100644
--- a/source/opt/local_single_block_elim_pass.cpp
+++ b/source/opt/local_single_block_elim_pass.cpp
@@ -19,6 +19,7 @@
#include <vector>
#include "source/opt/iterator.h"
+#include "source/util/string_utils.h"
namespace spvtools {
namespace opt {
@@ -31,9 +32,9 @@ const uint32_t kStoreValIdInIdx = 1;
bool LocalSingleBlockLoadStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) {
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true;
if (get_def_use_mgr()->WhileEachUser(ptrId, [this](Instruction* user) {
- auto dbg_op = user->GetOpenCL100DebugOpcode();
- if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
- dbg_op == OpenCLDebugInfo100DebugValue) {
+ auto dbg_op = user->GetCommonDebugOpcode();
+ if (dbg_op == CommonDebugInfoDebugDeclare ||
+ dbg_op == CommonDebugInfoDebugValue) {
return true;
}
SpvOp op = user->opcode();
@@ -183,11 +184,22 @@ void LocalSingleBlockLoadStoreElimPass::Initialize() {
bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const {
// If any extension not in allowlist, return false
for (auto& ei : get_module()->extensions()) {
- const char* extName =
- reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
+ const std::string extName = ei.GetInOperand(0).AsString();
if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
return false;
}
+ // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise
+ // around unknown extended
+ // instruction sets even if they are non-semantic
+ for (auto& inst : context()->module()->ext_inst_imports()) {
+ assert(inst.opcode() == SpvOpExtInstImport &&
+ "Expecting an import of an extension's instruction set.");
+ const std::string extension_name = inst.GetInOperand(0).AsString();
+ if (spvtools::utils::starts_with(extension_name, "NonSemantic.") &&
+ extension_name != "NonSemantic.Shader.DebugInfo.100") {
+ return false;
+ }
+ }
return true;
}
@@ -209,7 +221,7 @@ Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() {
return LocalSingleBlockLoadStoreElim(fp);
};
- bool modified = context()->ProcessEntryPointCallTree(pfn);
+ bool modified = context()->ProcessReachableCallTree(pfn);
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
@@ -272,6 +284,8 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
"SPV_KHR_terminate_invocation",
"SPV_KHR_subgroup_uniform_control_flow",
"SPV_KHR_integer_dot_product",
+ "SPV_EXT_shader_image_int64",
+ "SPV_KHR_non_semantic_info",
});
}
diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp
index 7eb4b1fd..123d03bf 100644
--- a/source/opt/local_single_store_elim_pass.cpp
+++ b/source/opt/local_single_store_elim_pass.cpp
@@ -19,6 +19,7 @@
#include "source/cfa.h"
#include "source/latest_version_glsl_std_450_header.h"
#include "source/opt/iterator.h"
+#include "source/util/string_utils.h"
namespace spvtools {
namespace opt {
@@ -48,11 +49,22 @@ bool LocalSingleStoreElimPass::LocalSingleStoreElim(Function* func) {
bool LocalSingleStoreElimPass::AllExtensionsSupported() const {
// If any extension not in allowlist, return false
for (auto& ei : get_module()->extensions()) {
- const char* extName =
- reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
+ const std::string extName = ei.GetInOperand(0).AsString();
if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
return false;
}
+ // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise
+ // around unknown extended
+ // instruction sets even if they are non-semantic
+ for (auto& inst : context()->module()->ext_inst_imports()) {
+ assert(inst.opcode() == SpvOpExtInstImport &&
+ "Expecting an import of an extension's instruction set.");
+ const std::string extension_name = inst.GetInOperand(0).AsString();
+ if (spvtools::utils::starts_with(extension_name, "NonSemantic.") &&
+ extension_name != "NonSemantic.Shader.DebugInfo.100") {
+ return false;
+ }
+ }
return true;
}
@@ -67,7 +79,7 @@ Pass::Status LocalSingleStoreElimPass::ProcessImpl() {
ProcessFunction pfn = [this](Function* fp) {
return LocalSingleStoreElim(fp);
};
- bool modified = context()->ProcessEntryPointCallTree(pfn);
+ bool modified = context()->ProcessReachableCallTree(pfn);
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
@@ -125,6 +137,8 @@ void LocalSingleStoreElimPass::InitExtensionAllowList() {
"SPV_KHR_terminate_invocation",
"SPV_KHR_subgroup_uniform_control_flow",
"SPV_KHR_integer_dot_product",
+ "SPV_EXT_shader_image_int64",
+ "SPV_KHR_non_semantic_info",
});
}
bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
@@ -223,9 +237,9 @@ Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses(
case SpvOpCopyObject:
break;
case SpvOpExtInst: {
- auto dbg_op = user->GetOpenCL100DebugOpcode();
- if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
- dbg_op == OpenCLDebugInfo100DebugValue) {
+ auto dbg_op = user->GetCommonDebugOpcode();
+ if (dbg_op == CommonDebugInfoDebugDeclare ||
+ dbg_op == CommonDebugInfoDebugValue) {
break;
}
return nullptr;
@@ -292,9 +306,9 @@ bool LocalSingleStoreElimPass::RewriteLoads(
bool modified = false;
for (Instruction* use : uses) {
if (use->opcode() == SpvOpStore) continue;
- auto dbg_op = use->GetOpenCL100DebugOpcode();
- if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
- dbg_op == OpenCLDebugInfo100DebugValue)
+ auto dbg_op = use->GetCommonDebugOpcode();
+ if (dbg_op == CommonDebugInfoDebugDeclare ||
+ dbg_op == CommonDebugInfoDebugValue)
continue;
if (use->opcode() == SpvOpLoad &&
dominator_analysis->Dominates(store_inst, use)) {
diff --git a/source/opt/loop_descriptor.cpp b/source/opt/loop_descriptor.cpp
index b5b56309..9bc495e5 100644
--- a/source/opt/loop_descriptor.cpp
+++ b/source/opt/loop_descriptor.cpp
@@ -719,7 +719,7 @@ bool Loop::FindNumberOfIterations(const Instruction* induction,
step_value = -step_value;
}
- // Find the inital value of the loop and make sure it is a constant integer.
+ // Find the initial value of the loop and make sure it is a constant integer.
int64_t init_value = 0;
if (!GetInductionInitValue(induction, &init_value)) return false;
@@ -751,7 +751,7 @@ bool Loop::FindNumberOfIterations(const Instruction* induction,
// We retrieve the number of iterations using the following formula, diff /
// |step_value| where diff is calculated differently according to the
// |condition| and uses the |condition_value| and |init_value|. If diff /
-// |step_value| is NOT cleanly divisable then we add one to the sum.
+// |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 {
int64_t diff = 0;
@@ -795,7 +795,7 @@ int64_t Loop::GetIterations(SpvOp condition, int64_t condition_value,
// If the condition is not met to begin with the loop will never iterate.
if (!(init_value >= condition_value)) return 0;
- // We subract one to make it the same as SpvOpGreaterThan as it is
+ // We subtract one to make it the same as SpvOpGreaterThan as it is
// functionally equivalent.
diff = init_value - (condition_value - 1);
diff --git a/source/opt/loop_descriptor.h b/source/opt/loop_descriptor.h
index 4b4f8bc7..e88ff936 100644
--- a/source/opt/loop_descriptor.h
+++ b/source/opt/loop_descriptor.h
@@ -395,7 +395,7 @@ class Loop {
// Sets |merge| as the loop merge block. No checks are performed here.
inline void SetMergeBlockImpl(BasicBlock* merge) { loop_merge_ = merge; }
- // Each differnt loop |condition| affects how we calculate the number of
+ // 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|.
diff --git a/source/opt/loop_fission.cpp b/source/opt/loop_fission.cpp
index 0678113c..b4df8c62 100644
--- a/source/opt/loop_fission.cpp
+++ b/source/opt/loop_fission.cpp
@@ -29,7 +29,7 @@
// 2 - For each loop in the list, group each instruction into a set of related
// instructions by traversing each instructions users and operands recursively.
// We stop if we encounter an instruction we have seen before or an instruction
-// which we don't consider relevent (i.e OpLoopMerge). We then group these
+// which we don't consider relevant (i.e OpLoopMerge). We then group these
// groups into two different sets, one for the first loop and one for the
// second.
//
@@ -453,7 +453,7 @@ Pass::Status LoopFissionPass::Process() {
for (Function& f : *context()->module()) {
// We collect all the inner most loops in the function and run the loop
// splitting util on each. The reason we do this is to allow us to iterate
- // over each, as creating new loops will invalidate the the loop iterator.
+ // over each, as creating new loops will invalidate the loop iterator.
std::vector<Loop*> inner_most_loops{};
LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(&f);
for (Loop& loop : loop_descriptor) {
diff --git a/source/opt/loop_fission.h b/source/opt/loop_fission.h
index e7a59c18..9bc12c0f 100644
--- a/source/opt/loop_fission.h
+++ b/source/opt/loop_fission.h
@@ -33,7 +33,7 @@ namespace opt {
class LoopFissionPass : public Pass {
public:
- // Fuction used to determine if a given loop should be split. Takes register
+ // Function used to determine if a given loop should be split. Takes register
// pressure region for that loop as a parameter and returns true if the loop
// should be split.
using FissionCriteriaFunction =
diff --git a/source/opt/loop_fusion.cpp b/source/opt/loop_fusion.cpp
index 07d171a0..f3aab283 100644
--- a/source/opt/loop_fusion.cpp
+++ b/source/opt/loop_fusion.cpp
@@ -165,7 +165,7 @@ bool LoopFusion::AreCompatible() {
// Check adjacency, |loop_0_| should come just before |loop_1_|.
// There is always at least one block between loops, even if it's empty.
- // We'll check at most 2 preceeding blocks.
+ // We'll check at most 2 preceding blocks.
auto pre_header_1 = loop_1_->GetPreHeaderBlock();
@@ -712,7 +712,7 @@ void LoopFusion::Fuse() {
ld->RemoveLoop(loop_1_);
- // Kill unnessecary instructions and remove all empty blocks.
+ // Kill unnecessary instructions and remove all empty blocks.
for (auto inst : instr_to_delete) {
context_->KillInst(inst);
}
diff --git a/source/opt/loop_fusion.h b/source/opt/loop_fusion.h
index d61d6783..769da5f1 100644
--- a/source/opt/loop_fusion.h
+++ b/source/opt/loop_fusion.h
@@ -40,7 +40,7 @@ class LoopFusion {
// That means:
// * they both have one induction variable
// * they have the same upper and lower bounds
- // - same inital value
+ // - same initial value
// - same condition
// * they have the same update step
// * they are adjacent, with |loop_0| appearing before |loop_1|
diff --git a/source/opt/loop_fusion_pass.h b/source/opt/loop_fusion_pass.h
index 3a0be600..9d5b7ccd 100644
--- a/source/opt/loop_fusion_pass.h
+++ b/source/opt/loop_fusion_pass.h
@@ -33,7 +33,7 @@ class LoopFusionPass : public Pass {
// Processes the given |module|. Returns Status::Failure if errors occur when
// processing. Returns the corresponding Status::Success if processing is
- // succesful to indicate whether changes have been made to the modue.
+ // successful to indicate whether changes have been made to the module.
Status Process() override;
private:
diff --git a/source/opt/loop_peeling.h b/source/opt/loop_peeling.h
index 413f896f..2a55fe44 100644
--- a/source/opt/loop_peeling.h
+++ b/source/opt/loop_peeling.h
@@ -261,7 +261,7 @@ class LoopPeelingPass : public Pass {
// Processes the given |module|. Returns Status::Failure if errors occur when
// processing. Returns the corresponding Status::Success if processing is
- // succesful to indicate whether changes have been made to the modue.
+ // successful to indicate whether changes have been made to the module.
Pass::Status Process() override;
private:
diff --git a/source/opt/loop_unroller.cpp b/source/opt/loop_unroller.cpp
index df68bd20..28ff0729 100644
--- a/source/opt/loop_unroller.cpp
+++ b/source/opt/loop_unroller.cpp
@@ -163,7 +163,7 @@ struct LoopUnrollState {
};
// This class implements the actual unrolling. It uses a LoopUnrollState to
-// maintain the state of the unrolling inbetween steps.
+// maintain the state of the unrolling in between steps.
class LoopUnrollerUtilsImpl {
public:
using BasicBlockListTy = std::vector<std::unique_ptr<BasicBlock>>;
@@ -209,7 +209,7 @@ class LoopUnrollerUtilsImpl {
// Add all blocks_to_add_ to function_ at the |insert_point|.
void AddBlocksToFunction(const BasicBlock* insert_point);
- // Duplicates the |old_loop|, cloning each body and remaping the ids without
+ // Duplicates the |old_loop|, cloning each body and remapping the ids without
// removing instructions or changing relative structure. Result will be stored
// in |new_loop|.
void DuplicateLoop(Loop* old_loop, Loop* new_loop);
@@ -241,7 +241,7 @@ class LoopUnrollerUtilsImpl {
// Remap all the in |basic_block| to new IDs and keep the mapping of new ids
// to old
// ids. |loop| is used to identify special loop blocks (header, continue,
- // ect).
+ // etc).
void AssignNewResultIds(BasicBlock* basic_block);
// Using the map built by AssignNewResultIds, replace the uses in |inst|
@@ -320,7 +320,7 @@ class LoopUnrollerUtilsImpl {
// and then be remapped at the end.
std::vector<Instruction*> loop_phi_instructions_;
- // The number of loop iterations that the loop would preform pre-unroll.
+ // The number of loop iterations that the loop would perform pre-unroll.
size_t number_of_loop_iterations_;
// The amount that the loop steps each iteration.
@@ -760,7 +760,7 @@ void LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block,
IRContext::Analysis::kAnalysisInstrToBlockMapping);
Instruction* new_branch = builder.AddBranch(new_target);
- new_branch->set_dbg_line_insts(lines);
+ if (!lines.empty()) new_branch->AddDebugLine(&lines.back());
new_branch->SetDebugScope(scope);
}
@@ -839,7 +839,7 @@ void LoopUnrollerUtilsImpl::DuplicateLoop(Loop* old_loop, Loop* new_loop) {
new_loop->SetMergeBlock(new_merge);
}
-// Whenever the utility copies a block it stores it in a tempory buffer, this
+// Whenever the utility copies a block it stores it in a temporary buffer, this
// function adds the buffer into the Function. The blocks will be inserted
// after the block |insert_point|.
void LoopUnrollerUtilsImpl::AddBlocksToFunction(
@@ -873,6 +873,10 @@ void LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) {
def_use_mgr->AnalyzeInstDefUse(basic_block->GetLabelInst());
for (Instruction& inst : *basic_block) {
+ // Do def/use analysis on new lines
+ for (auto& line : inst.dbg_line_insts())
+ def_use_mgr->AnalyzeInstDefUse(&line);
+
uint32_t old_id = inst.result_id();
// Ignore stores etc.
@@ -1098,6 +1102,10 @@ void LoopUtils::Finalize() {
Pass::Status LoopUnroller::Process() {
bool changed = false;
for (Function& f : *context()->module()) {
+ if (f.IsDeclaration()) {
+ continue;
+ }
+
LoopDescriptor* LD = context()->GetLoopDescriptor(&f);
for (Loop& loop : *LD) {
LoopUtils loop_utils{context(), &loop};
diff --git a/source/opt/loop_unswitch_pass.cpp b/source/opt/loop_unswitch_pass.cpp
index d805ecf3..1ee7e5e2 100644
--- a/source/opt/loop_unswitch_pass.cpp
+++ b/source/opt/loop_unswitch_pass.cpp
@@ -118,7 +118,7 @@ class LoopUnswitch {
// Find a value that can be used to select the default path.
// If none are possible, then it will just use 0. The value does not matter
- // because this path will never be taken becaues the new switch outside of
+ // because this path will never be taken because the new switch outside of
// the loop cannot select this path either.
std::vector<uint32_t> existing_values;
for (uint32_t i = 2; i < switch_inst->NumInOperands(); i += 2) {
diff --git a/source/opt/loop_unswitch_pass.h b/source/opt/loop_unswitch_pass.h
index 3ecdd611..4f7295d4 100644
--- a/source/opt/loop_unswitch_pass.h
+++ b/source/opt/loop_unswitch_pass.h
@@ -30,7 +30,7 @@ class LoopUnswitchPass : public Pass {
// Processes the given |module|. Returns Status::Failure if errors occur when
// processing. Returns the corresponding Status::Success if processing is
- // succesful to indicate whether changes have been made to the modue.
+ // successful to indicate whether changes have been made to the module.
Pass::Status Process() override;
private:
diff --git a/source/opt/loop_utils.h b/source/opt/loop_utils.h
index a4e61900..70060fc4 100644
--- a/source/opt/loop_utils.h
+++ b/source/opt/loop_utils.h
@@ -123,7 +123,7 @@ class LoopUtils {
// Clone the |loop_| and make the new loop branch to the second loop on exit.
Loop* CloneAndAttachLoopToHeader(LoopCloningResult* cloning_result);
- // Perfom a partial unroll of |loop| by given |factor|. This will copy the
+ // Perform a partial unroll of |loop| by given |factor|. This will copy the
// body of the loop |factor| times. So a |factor| of one would give a new loop
// with the original body plus one unrolled copy body.
bool PartiallyUnroll(size_t factor);
@@ -139,7 +139,7 @@ class LoopUtils {
// 1. That the loop is in structured order.
// 2. That the continue block is a branch to the header.
// 3. That the only phi used in the loop is the induction variable.
- // TODO(stephen@codeplay.com): This is a temporary mesure, after the loop is
+ // TODO(stephen@codeplay.com): This is a temporary measure, after the loop is
// converted into LCSAA form and has a single entry and exit we can rewrite
// the other phis.
// 4. That this is an inner most loop, or that loops contained within this
diff --git a/source/opt/mem_pass.cpp b/source/opt/mem_pass.cpp
index 57387984..ca4889b7 100644
--- a/source/opt/mem_pass.cpp
+++ b/source/opt/mem_pass.cpp
@@ -20,7 +20,6 @@
#include <set>
#include <vector>
-#include "OpenCLDebugInfo100.h"
#include "source/cfa.h"
#include "source/opt/basic_block.h"
#include "source/opt/dominator_analysis.h"
@@ -87,8 +86,8 @@ bool MemPass::IsPtr(uint32_t ptrId) {
}
const SpvOp op = ptrInst->opcode();
if (op == SpvOpVariable || IsNonPtrAccessChain(op)) return true;
- if (op != SpvOpFunctionParameter) return false;
const uint32_t varTypeId = ptrInst->type_id();
+ if (varTypeId == 0) return false;
const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
return varTypeInst->opcode() == SpvOpTypePointer;
}
@@ -226,9 +225,9 @@ MemPass::MemPass() {}
bool MemPass::HasOnlySupportedRefs(uint32_t varId) {
return get_def_use_mgr()->WhileEachUser(varId, [this](Instruction* user) {
- auto dbg_op = user->GetOpenCL100DebugOpcode();
- if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
- dbg_op == OpenCLDebugInfo100DebugValue) {
+ auto dbg_op = user->GetCommonDebugOpcode();
+ if (dbg_op == CommonDebugInfoDebugDeclare ||
+ dbg_op == CommonDebugInfoDebugValue) {
return true;
}
SpvOp op = user->opcode();
diff --git a/source/opt/merge_return_pass.cpp b/source/opt/merge_return_pass.cpp
index f1601049..a962a7cc 100644
--- a/source/opt/merge_return_pass.cpp
+++ b/source/opt/merge_return_pass.cpp
@@ -111,7 +111,9 @@ bool MergeReturnPass::ProcessStructured(
}
RecordImmediateDominators(function);
- AddSingleCaseSwitchAroundFunction();
+ if (!AddSingleCaseSwitchAroundFunction()) {
+ return false;
+ }
std::list<BasicBlock*> order;
cfg()->ComputeStructuredOrder(function, &*function->begin(), &order);
@@ -770,7 +772,7 @@ void MergeReturnPass::InsertAfterElement(BasicBlock* element,
list->insert(pos, new_element);
}
-void MergeReturnPass::AddSingleCaseSwitchAroundFunction() {
+bool MergeReturnPass::AddSingleCaseSwitchAroundFunction() {
CreateReturnBlock();
CreateReturn(final_return_block_);
@@ -778,7 +780,10 @@ void MergeReturnPass::AddSingleCaseSwitchAroundFunction() {
cfg()->RegisterBlock(final_return_block_);
}
- CreateSingleCaseSwitch(final_return_block_);
+ if (!CreateSingleCaseSwitch(final_return_block_)) {
+ return false;
+ }
+ return true;
}
BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) {
@@ -813,7 +818,7 @@ BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) {
return new_block;
}
-void MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) {
+bool MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) {
// Insert the switch before any code is run. We have to split the entry
// block to make sure the OpVariable instructions remain in the entry block.
BasicBlock* start_block = &*function_->begin();
@@ -830,13 +835,17 @@ void MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) {
context(), start_block,
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
- builder.AddSwitch(builder.GetUintConstantId(0u), old_block->id(), {},
- merge_target->id());
+ uint32_t const_zero_id = builder.GetUintConstantId(0u);
+ if (const_zero_id == 0) {
+ return false;
+ }
+ builder.AddSwitch(const_zero_id, old_block->id(), {}, merge_target->id());
if (context()->AreAnalysesValid(IRContext::kAnalysisCFG)) {
cfg()->RegisterBlock(old_block);
cfg()->AddEdges(start_block);
}
+ return true;
}
bool MergeReturnPass::HasNontrivialUnreachableBlocks(Function* function) {
diff --git a/source/opt/merge_return_pass.h b/source/opt/merge_return_pass.h
index 06a3e7b5..a35cf269 100644
--- a/source/opt/merge_return_pass.h
+++ b/source/opt/merge_return_pass.h
@@ -247,7 +247,7 @@ class MergeReturnPass : public MemPass {
// Add new phi nodes for any id that no longer dominate all of it uses. A phi
// node is added to a block |bb| for an id if the id is defined between the
- // original immediate dominator of |bb| and its new immidiate dominator. It
+ // original immediate dominator of |bb| and its new immediate dominator. It
// is assumed that at this point there are no unreachable blocks in the
// control flow graph.
void AddNewPhiNodes();
@@ -273,11 +273,11 @@ class MergeReturnPass : public MemPass {
void InsertAfterElement(BasicBlock* element, BasicBlock* new_element,
std::list<BasicBlock*>* list);
- // Creates a single case switch around all of the exectuable code of the
+ // Creates a single case switch around all of the executable code of the
// current function where the switch and case value are both zero and the
// default is the merge block. Returns after the switch is executed. Sets
// |final_return_block_|.
- void AddSingleCaseSwitchAroundFunction();
+ bool AddSingleCaseSwitchAroundFunction();
// Creates a new basic block that branches to |header_label_id|. Returns the
// new basic block. The block will be the second last basic block in the
@@ -286,7 +286,7 @@ class MergeReturnPass : public MemPass {
// Creates a one case switch around the executable code of the function with
// |merge_target| as the merge node.
- void CreateSingleCaseSwitch(BasicBlock* merge_target);
+ bool CreateSingleCaseSwitch(BasicBlock* merge_target);
// Returns true if |function| has an unreachable block that is not a continue
// target that simply branches back to the header, or a merge block containing
diff --git a/source/opt/module.cpp b/source/opt/module.cpp
index 0c886010..5983abb1 100644
--- a/source/opt/module.cpp
+++ b/source/opt/module.cpp
@@ -139,26 +139,27 @@ void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const {
// TODO(antiagainst): should we change the generator number?
binary->push_back(header_.generator);
binary->push_back(header_.bound);
- binary->push_back(header_.reserved);
+ binary->push_back(header_.schema);
size_t bound_idx = binary->size() - 2;
DebugScope last_scope(kNoDebugScope, kNoInlinedAt);
const Instruction* last_line_inst = nullptr;
bool between_merge_and_branch = false;
+ bool between_label_and_phi_var = false;
auto write_inst = [binary, skip_nop, &last_scope, &last_line_inst,
- &between_merge_and_branch, this](const Instruction* i) {
+ &between_merge_and_branch, &between_label_and_phi_var,
+ this](const Instruction* i) {
// Skip emitting line instructions between merge and branch instructions.
auto opcode = i->opcode();
- if (between_merge_and_branch &&
- (opcode == SpvOpLine || opcode == SpvOpNoLine)) {
+ if (between_merge_and_branch && i->IsLineInst()) {
return;
}
between_merge_and_branch = false;
if (last_line_inst != nullptr) {
- // If the current instruction is OpLine and it is the same with
- // the last line instruction that is still effective (can be applied
+ // If the current instruction is OpLine or DebugLine and it is the same
+ // as the last line instruction that is still effective (can be applied
// to the next instruction), we skip writing the current instruction.
- if (opcode == SpvOpLine) {
+ if (i->IsLine()) {
uint32_t operand_index = 0;
if (last_line_inst->WhileEachInOperand(
[&operand_index, i](const uint32_t* word) {
@@ -167,39 +168,67 @@ void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const {
})) {
return;
}
- } else if (opcode != SpvOpNoLine && i->dbg_line_insts().empty()) {
+ } else if (!i->IsNoLine() && i->dbg_line_insts().empty()) {
// If the current instruction does not have the line information,
// the last line information is not effective any more. Emit OpNoLine
- // to specify it.
- binary->push_back((1 << 16) | static_cast<uint16_t>(SpvOpNoLine));
+ // or DebugNoLine to specify it.
+ uint32_t shader_set_id = context()
+ ->get_feature_mgr()
+ ->GetExtInstImportId_Shader100DebugInfo();
+ if (shader_set_id != 0) {
+ binary->push_back((5 << 16) | static_cast<uint16_t>(SpvOpExtInst));
+ binary->push_back(context()->get_type_mgr()->GetVoidTypeId());
+ binary->push_back(context()->TakeNextId());
+ binary->push_back(shader_set_id);
+ binary->push_back(NonSemanticShaderDebugInfo100DebugNoLine);
+ } else {
+ binary->push_back((1 << 16) | static_cast<uint16_t>(SpvOpNoLine));
+ }
last_line_inst = nullptr;
}
}
+
+ if (opcode == SpvOpLabel) {
+ between_label_and_phi_var = true;
+ } else if (opcode != SpvOpVariable && opcode != SpvOpPhi &&
+ !spvtools::opt::IsOpLineInst(opcode)) {
+ between_label_and_phi_var = false;
+ }
+
if (!(skip_nop && i->IsNop())) {
const auto& scope = i->GetDebugScope();
if (scope != last_scope) {
- // Emit DebugScope |scope| to |binary|.
- auto dbg_inst = ext_inst_debuginfo_.begin();
- scope.ToBinary(dbg_inst->type_id(), context()->TakeNextId(),
- dbg_inst->GetSingleWordOperand(2), binary);
+ // Can only emit nonsemantic instructions after all phi instructions
+ // in a block so don't emit scope instructions before phi instructions
+ // for NonSemantic.Shader.DebugInfo.100.
+ if (!between_label_and_phi_var ||
+ context()
+ ->get_feature_mgr()
+ ->GetExtInstImportId_OpenCL100DebugInfo()) {
+ // Emit DebugScope |scope| to |binary|.
+ auto dbg_inst = ext_inst_debuginfo_.begin();
+ scope.ToBinary(dbg_inst->type_id(), context()->TakeNextId(),
+ dbg_inst->GetSingleWordOperand(2), binary);
+ }
last_scope = scope;
}
i->ToBinaryWithoutAttachedDebugInsts(binary);
}
// Update the last line instruction.
- if (spvOpcodeIsBlockTerminator(opcode) || opcode == SpvOpNoLine) {
+ if (spvOpcodeIsBlockTerminator(opcode) || i->IsNoLine()) {
last_line_inst = nullptr;
} else if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) {
between_merge_and_branch = true;
last_line_inst = nullptr;
- } else if (opcode == SpvOpLine) {
+ } else if (i->IsLine()) {
last_line_inst = i;
}
};
ForEachInst(write_inst, true);
- // We create new instructions for DebugScope. The bound must be updated.
+ // We create new instructions for DebugScope and DebugNoLine. The bound must
+ // be updated.
binary->data()[bound_idx] = header_.bound;
}
@@ -231,9 +260,7 @@ bool Module::HasExplicitCapability(uint32_t cap) {
uint32_t Module::GetExtInstImportId(const char* extstr) {
for (auto& ei : ext_inst_imports_)
- if (!strcmp(extstr,
- reinterpret_cast<const char*>(&(ei.GetInOperand(0).words[0]))))
- return ei.result_id();
+ if (!ei.GetInOperand(0).AsString().compare(extstr)) return ei.result_id();
return 0;
}
diff --git a/source/opt/module.h b/source/opt/module.h
index d609b603..230be709 100644
--- a/source/opt/module.h
+++ b/source/opt/module.h
@@ -36,7 +36,7 @@ struct ModuleHeader {
uint32_t version;
uint32_t generator;
uint32_t bound;
- uint32_t reserved;
+ uint32_t schema;
};
// A SPIR-V module. It contains all the information for a SPIR-V module and
@@ -61,7 +61,7 @@ class Module {
}
// Returns the Id bound.
- uint32_t IdBound() { return header_.bound; }
+ uint32_t IdBound() const { return header_.bound; }
// Returns the current Id bound and increases it to the next available value.
// If the id bound has already reached its maximum value, then 0 is returned.
@@ -103,8 +103,8 @@ class Module {
// This is due to decision by the SPIR Working Group, pending publication.
inline void AddDebug3Inst(std::unique_ptr<Instruction> d);
- // Appends a debug info extension (OpenCL.DebugInfo.100 or DebugInfo)
- // instruction to this module.
+ // Appends a debug info extension (OpenCL.DebugInfo.100,
+ // NonSemantic.Shader.DebugInfo.100, or DebugInfo) instruction to this module.
inline void AddExtInstDebugInfo(std::unique_ptr<Instruction> d);
// Appends an annotation instruction to this module.
@@ -141,6 +141,8 @@ class Module {
inline uint32_t id_bound() const { return header_.bound; }
inline uint32_t version() const { return header_.version; }
+ inline uint32_t generator() const { return header_.generator; }
+ inline uint32_t schema() const { return header_.schema; }
inline void set_version(uint32_t v) { header_.version = v; }
@@ -192,8 +194,8 @@ class Module {
inline IteratorRange<const_inst_iterator> debugs3() const;
// Iterators for debug info instructions (excluding OpLine & OpNoLine)
- // contained in this module. These are OpExtInst for OpenCL.DebugInfo.100
- // or DebugInfo extension placed between section 9 and 10.
+ // contained in this module. These are OpExtInst for DebugInfo extension
+ // placed between section 9 and 10.
inline inst_iterator ext_inst_debuginfo_begin();
inline inst_iterator ext_inst_debuginfo_end();
inline IteratorRange<inst_iterator> ext_inst_debuginfo();
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp
index a5d10c3d..330093e4 100644
--- a/source/opt/optimizer.cpp
+++ b/source/opt/optimizer.cpp
@@ -288,6 +288,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
RegisterPass(CreateStripDebugInfoPass());
} else if (pass_name == "strip-reflect") {
RegisterPass(CreateStripReflectInfoPass());
+ } else if (pass_name == "strip-nonsemantic") {
+ RegisterPass(CreateStripNonSemanticInfoPass());
} else if (pass_name == "set-spec-const-default-value") {
if (pass_args.size() > 0) {
auto spec_ids_vals =
@@ -320,6 +322,10 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
RegisterPass(CreateCombineAccessChainsPass());
} else if (pass_name == "convert-local-access-chains") {
RegisterPass(CreateLocalAccessChainConvertPass());
+ } else if (pass_name == "replace-desc-array-access-using-var-index") {
+ RegisterPass(CreateReplaceDescArrayAccessUsingVarIndexPass());
+ } else if (pass_name == "spread-volatile-semantics") {
+ RegisterPass(CreateSpreadVolatileSemanticsPass());
} else if (pass_name == "descriptor-scalar-replacement") {
RegisterPass(CreateDescriptorScalarReplacementPass());
} else if (pass_name == "eliminate-dead-code-aggressive") {
@@ -385,7 +391,23 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
} else if (pass_name == "loop-invariant-code-motion") {
RegisterPass(CreateLoopInvariantCodeMotionPass());
} else if (pass_name == "reduce-load-size") {
- RegisterPass(CreateReduceLoadSizePass());
+ if (pass_args.size() == 0) {
+ RegisterPass(CreateReduceLoadSizePass());
+ } else {
+ double load_replacement_threshold = 0.9;
+ if (pass_args.find_first_not_of(".0123456789") == std::string::npos) {
+ load_replacement_threshold = atof(pass_args.c_str());
+ }
+
+ if (load_replacement_threshold >= 0) {
+ RegisterPass(CreateReduceLoadSizePass(load_replacement_threshold));
+ } else {
+ Error(consumer(), nullptr, {},
+ "--reduce-load-size must have no arguments or a non-negative "
+ "double argument");
+ return false;
+ }
+ }
} else if (pass_name == "redundancy-elimination") {
RegisterPass(CreateRedundancyEliminationPass());
} else if (pass_name == "private-to-local") {
@@ -401,22 +423,22 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
RegisterPass(CreateSimplificationPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateBlockMergePass());
- RegisterPass(CreateAggressiveDCEPass());
+ RegisterPass(CreateAggressiveDCEPass(true));
} else if (pass_name == "inst-desc-idx-check") {
RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true));
RegisterPass(CreateSimplificationPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateBlockMergePass());
- RegisterPass(CreateAggressiveDCEPass());
+ RegisterPass(CreateAggressiveDCEPass(true));
} else if (pass_name == "inst-buff-oob-check") {
RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, true, true));
RegisterPass(CreateSimplificationPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateBlockMergePass());
- RegisterPass(CreateAggressiveDCEPass());
+ RegisterPass(CreateAggressiveDCEPass(true));
} else if (pass_name == "inst-buff-addr-check") {
RegisterPass(CreateInstBuffAddrCheckPass(7, 23));
- RegisterPass(CreateAggressiveDCEPass());
+ RegisterPass(CreateAggressiveDCEPass(true));
} else if (pass_name == "convert-relaxed-to-half") {
RegisterPass(CreateConvertRelaxedToHalfPass());
} else if (pass_name == "relax-float-ops") {
@@ -489,6 +511,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
RegisterSizePasses();
} else if (pass_name == "legalize-hlsl") {
RegisterLegalizationPasses();
+ } else if (pass_name == "remove-unused-interface-variables") {
+ RegisterPass(CreateRemoveUnusedInterfaceVariablesPass());
} else if (pass_name == "graphics-robust-access") {
RegisterPass(CreateGraphicsRobustAccessPass());
} else if (pass_name == "wrap-opkill") {
@@ -497,6 +521,26 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
RegisterPass(CreateAmdExtToKhrPass());
} else if (pass_name == "interpolate-fixup") {
RegisterPass(CreateInterpolateFixupPass());
+ } else if (pass_name == "convert-to-sampled-image") {
+ if (pass_args.size() > 0) {
+ auto descriptor_set_binding_pairs =
+ opt::ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString(
+ pass_args.c_str());
+ if (!descriptor_set_binding_pairs) {
+ Errorf(consumer(), nullptr, {},
+ "Invalid argument for --convert-to-sampled-image: %s",
+ pass_args.c_str());
+ return false;
+ }
+ RegisterPass(CreateConvertToSampledImagePass(
+ std::move(*descriptor_set_binding_pairs)));
+ } else {
+ Errorf(consumer(), nullptr, {},
+ "Invalid pairs of descriptor set and binding '%s'. Expected a "
+ "string of <descriptor set>:<binding> pairs.",
+ pass_args.c_str());
+ return false;
+ }
} else {
Errorf(consumer(), nullptr, {},
"Unknown flag '--%s'. Use --help for a list of valid flags",
@@ -613,8 +657,12 @@ Optimizer::PassToken CreateStripDebugInfoPass() {
}
Optimizer::PassToken CreateStripReflectInfoPass() {
+ return CreateStripNonSemanticInfoPass();
+}
+
+Optimizer::PassToken CreateStripNonSemanticInfoPass() {
return MakeUnique<Optimizer::PassToken::Impl>(
- MakeUnique<opt::StripReflectInfoPass>());
+ MakeUnique<opt::StripNonSemanticInfoPass>());
}
Optimizer::PassToken CreateEliminateDeadFunctionsPass() {
@@ -726,7 +774,17 @@ Optimizer::PassToken CreateLocalMultiStoreElimPass() {
Optimizer::PassToken CreateAggressiveDCEPass() {
return MakeUnique<Optimizer::PassToken::Impl>(
- MakeUnique<opt::AggressiveDCEPass>());
+ MakeUnique<opt::AggressiveDCEPass>(false));
+}
+
+Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface) {
+ return MakeUnique<Optimizer::PassToken::Impl>(
+ MakeUnique<opt::AggressiveDCEPass>(preserve_interface));
+}
+
+Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass() {
+ return MakeUnique<Optimizer::PassToken::Impl>(
+ MakeUnique<opt::RemoveUnusedInterfaceVariablesPass>());
}
Optimizer::PassToken CreatePropagateLineInfoPass() {
@@ -852,9 +910,10 @@ Optimizer::PassToken CreateVectorDCEPass() {
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::VectorDCE>());
}
-Optimizer::PassToken CreateReduceLoadSizePass() {
+Optimizer::PassToken CreateReduceLoadSizePass(
+ double load_replacement_threshold) {
return MakeUnique<Optimizer::PassToken::Impl>(
- MakeUnique<opt::ReduceLoadSize>());
+ MakeUnique<opt::ReduceLoadSize>(load_replacement_threshold));
}
Optimizer::PassToken CreateCombineAccessChainsPass() {
@@ -914,6 +973,16 @@ Optimizer::PassToken CreateGraphicsRobustAccessPass() {
MakeUnique<opt::GraphicsRobustAccessPass>());
}
+Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass() {
+ return MakeUnique<Optimizer::PassToken::Impl>(
+ MakeUnique<opt::ReplaceDescArrayAccessUsingVarIndex>());
+}
+
+Optimizer::PassToken CreateSpreadVolatileSemanticsPass() {
+ return MakeUnique<Optimizer::PassToken::Impl>(
+ MakeUnique<opt::SpreadVolatileSemantics>());
+}
+
Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::DescriptorScalarReplacement>());
@@ -933,4 +1002,11 @@ Optimizer::PassToken CreateInterpolateFixupPass() {
MakeUnique<opt::InterpFixupPass>());
}
+Optimizer::PassToken CreateConvertToSampledImagePass(
+ const std::vector<opt::DescriptorSetAndBinding>&
+ descriptor_set_binding_pairs) {
+ return MakeUnique<Optimizer::PassToken::Impl>(
+ MakeUnique<opt::ConvertToSampledImagePass>(descriptor_set_binding_pairs));
+}
+
} // namespace spvtools
diff --git a/source/opt/pass.cpp b/source/opt/pass.cpp
index 09b78af9..017aad10 100644
--- a/source/opt/pass.cpp
+++ b/source/opt/pass.cpp
@@ -43,8 +43,8 @@ Pass::Status Pass::Run(IRContext* ctx) {
if (status == Status::SuccessWithChange) {
ctx->InvalidateAnalysesExceptFor(GetPreservedAnalyses());
}
- assert((status == Status::Failure || ctx->IsConsistent()) &&
- "An analysis in the context is out of date.");
+ if (!(status == Status::Failure || ctx->IsConsistent()))
+ assert(false && "An analysis in the context is out of date.");
return status;
}
diff --git a/source/opt/pass.h b/source/opt/pass.h
index a8c9c4b4..4a8ea674 100644
--- a/source/opt/pass.h
+++ b/source/opt/pass.h
@@ -129,7 +129,7 @@ class Pass {
// Processes the given |module|. Returns Status::Failure if errors occur when
// processing. Returns the corresponding Status::Success if processing is
- // succesful to indicate whether changes are made to the module.
+ // successful to indicate whether changes are made to the module.
virtual Status Process() = 0;
// Return the next available SSA id and increment it.
diff --git a/source/opt/pass_manager.cpp b/source/opt/pass_manager.cpp
index be53d344..a73ff7cf 100644
--- a/source/opt/pass_manager.cpp
+++ b/source/opt/pass_manager.cpp
@@ -35,10 +35,18 @@ Pass::Status PassManager::Run(IRContext* context) {
if (print_all_stream_) {
std::vector<uint32_t> binary;
context->module()->ToBinary(&binary, false);
- SpirvTools t(SPV_ENV_UNIVERSAL_1_2);
+ SpirvTools t(target_env_);
+ t.SetMessageConsumer(consumer());
std::string disassembly;
- t.Disassemble(binary, &disassembly, 0);
- *print_all_stream_ << preamble << (pass ? pass->name() : "") << "\n"
+ std::string pass_name = (pass ? pass->name() : "");
+ if (!t.Disassemble(binary, &disassembly, 0)) {
+ std::string msg = "Disassembly failed before pass ";
+ msg += pass_name + "\n";
+ spv_position_t null_pos{0, 0, 0};
+ consumer()(SPV_MSG_WARNING, "", null_pos, msg.c_str());
+ return;
+ }
+ *print_all_stream_ << preamble << pass_name << "\n"
<< disassembly << std::endl;
}
};
diff --git a/source/opt/pass_manager.h b/source/opt/pass_manager.h
index 9686dddc..11961a33 100644
--- a/source/opt/pass_manager.h
+++ b/source/opt/pass_manager.h
@@ -54,7 +54,7 @@ class PassManager {
// Adds an externally constructed pass.
void AddPass(std::unique_ptr<Pass> pass);
// Uses the argument |args| to construct a pass instance of type |T|, and adds
- // the pass instance to this pass manger. The pass added will use this pass
+ // the pass instance to this pass manager. The pass added will use this pass
// manager's message consumer.
template <typename T, typename... Args>
void AddPass(Args&&... args);
@@ -70,7 +70,7 @@ class PassManager {
// Runs all passes on the given |module|. Returns Status::Failure if errors
// occur when processing using one of the registered passes. All passes
// registered after the error-reporting pass will be skipped. Returns the
- // corresponding Status::Success if processing is succesful to indicate
+ // corresponding Status::Success if processing is successful to indicate
// whether changes are made to the module.
//
// After running all the passes, they are removed from the list.
diff --git a/source/opt/passes.h b/source/opt/passes.h
index bfb34af7..d51c306e 100644
--- a/source/opt/passes.h
+++ b/source/opt/passes.h
@@ -26,6 +26,7 @@
#include "source/opt/combine_access_chains.h"
#include "source/opt/compact_ids_pass.h"
#include "source/opt/convert_to_half_pass.h"
+#include "source/opt/convert_to_sampled_image_pass.h"
#include "source/opt/copy_prop_arrays.h"
#include "source/opt/dead_branch_elim_pass.h"
#include "source/opt/dead_insert_elim_pass.h"
@@ -64,14 +65,17 @@
#include "source/opt/redundancy_elimination.h"
#include "source/opt/relax_float_ops_pass.h"
#include "source/opt/remove_duplicates_pass.h"
+#include "source/opt/remove_unused_interface_variables_pass.h"
+#include "source/opt/replace_desc_array_access_using_var_index.h"
#include "source/opt/replace_invalid_opc.h"
#include "source/opt/scalar_replacement_pass.h"
#include "source/opt/set_spec_constant_default_value_pass.h"
#include "source/opt/simplification_pass.h"
+#include "source/opt/spread_volatile_semantics.h"
#include "source/opt/ssa_rewrite_pass.h"
#include "source/opt/strength_reduction_pass.h"
#include "source/opt/strip_debug_info_pass.h"
-#include "source/opt/strip_reflect_info_pass.h"
+#include "source/opt/strip_nonsemantic_info_pass.h"
#include "source/opt/unify_const_pass.h"
#include "source/opt/upgrade_memory_model.h"
#include "source/opt/vector_dce.h"
diff --git a/source/opt/private_to_local_pass.cpp b/source/opt/private_to_local_pass.cpp
index dd6cbbde..12a226d5 100644
--- a/source/opt/private_to_local_pass.cpp
+++ b/source/opt/private_to_local_pass.cpp
@@ -157,8 +157,7 @@ uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) {
bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const {
// The cases in this switch have to match the cases in |UpdateUse|.
// If we don't know how to update it, it is not valid.
- if (inst->GetOpenCL100DebugOpcode() ==
- OpenCLDebugInfo100DebugGlobalVariable) {
+ if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) {
return true;
}
switch (inst->opcode()) {
@@ -183,8 +182,7 @@ bool PrivateToLocalPass::UpdateUse(Instruction* inst, Instruction* user) {
// The cases in this switch have to match the cases in |IsValidUse|. If we
// don't think it is valid, the optimization will not view the variable as a
// candidate, and therefore the use will not be updated.
- if (inst->GetOpenCL100DebugOpcode() ==
- OpenCLDebugInfo100DebugGlobalVariable) {
+ if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) {
context()->get_debug_info_mgr()->ConvertDebugGlobalToLocalVariable(inst,
user);
return true;
diff --git a/source/opt/private_to_local_pass.h b/source/opt/private_to_local_pass.h
index c6127d67..e96a965e 100644
--- a/source/opt/private_to_local_pass.h
+++ b/source/opt/private_to_local_pass.h
@@ -44,7 +44,7 @@ class PrivateToLocalPass : public Pass {
// class of |function|. Returns false if the variable could not be moved.
bool MoveVariable(Instruction* variable, Function* function);
- // |inst| is an instruction declaring a varible. If that variable is
+ // |inst| is an instruction declaring a variable. If that variable is
// referenced in a single function and all of uses are valid as defined by
// |IsValidUse|, then that function is returned. Otherwise, the return
// value is |nullptr|.
diff --git a/source/opt/reduce_load_size.cpp b/source/opt/reduce_load_size.cpp
index c58adf47..e9b80874 100644
--- a/source/opt/reduce_load_size.cpp
+++ b/source/opt/reduce_load_size.cpp
@@ -27,7 +27,6 @@ namespace {
const uint32_t kExtractCompositeIdInIdx = 0;
const uint32_t kVariableStorageClassInIdx = 0;
const uint32_t kLoadPointerInIdx = 0;
-const double kThreshold = 0.9;
} // namespace
@@ -139,7 +138,7 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) {
all_elements_used =
!def_use_mgr->WhileEachUser(op_inst, [&elements_used](Instruction* use) {
- if (use->IsOpenCL100DebugInstr()) return true;
+ if (use->IsCommonDebugInstr()) return true;
if (use->opcode() != SpvOpCompositeExtract ||
use->NumInOperands() == 1) {
return false;
@@ -151,6 +150,8 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) {
bool should_replace = false;
if (all_elements_used) {
should_replace = false;
+ } else if (1.0 <= replacement_threshold_) {
+ should_replace = true;
} else {
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
analysis::TypeManager* type_mgr = context()->get_type_mgr();
@@ -172,7 +173,7 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) {
}
double percent_used = static_cast<double>(elements_used.size()) /
static_cast<double>(total_size);
- should_replace = (percent_used < kThreshold);
+ should_replace = (percent_used < replacement_threshold_);
}
should_replace_cache_[op_inst->result_id()] = should_replace;
diff --git a/source/opt/reduce_load_size.h b/source/opt/reduce_load_size.h
index ccac49be..b3238453 100644
--- a/source/opt/reduce_load_size.h
+++ b/source/opt/reduce_load_size.h
@@ -27,6 +27,9 @@ namespace opt {
// See optimizer.hpp for documentation.
class ReduceLoadSize : public Pass {
public:
+ explicit ReduceLoadSize(double replacement_threshold)
+ : replacement_threshold_(replacement_threshold) {}
+
const char* name() const override { return "reduce-load-size"; }
Status Process() override;
@@ -54,6 +57,11 @@ class ReduceLoadSize : public Pass {
// on the load feeding |inst|.
bool ShouldReplaceExtract(Instruction* inst);
+ // Threshold to determine whether we have to replace the load or not. If the
+ // ratio of the used components of the load is less than the threshold, we
+ // replace the load.
+ double replacement_threshold_;
+
// Maps the result id of an OpLoad instruction to the result of whether or
// not the OpCompositeExtract that use the id should be replaced.
std::unordered_map<uint32_t, bool> should_replace_cache_;
diff --git a/source/opt/redundancy_elimination.cpp b/source/opt/redundancy_elimination.cpp
index 362e54dc..398225bb 100644
--- a/source/opt/redundancy_elimination.cpp
+++ b/source/opt/redundancy_elimination.cpp
@@ -24,6 +24,10 @@ Pass::Status RedundancyEliminationPass::Process() {
ValueNumberTable vnTable(context());
for (auto& func : *get_module()) {
+ if (func.IsDeclaration()) {
+ continue;
+ }
+
// Build the dominator tree for this function. It is how the code is
// traversed.
DominatorTree& dom_tree =
diff --git a/source/opt/redundancy_elimination.h b/source/opt/redundancy_elimination.h
index 91809b5d..40451f40 100644
--- a/source/opt/redundancy_elimination.h
+++ b/source/opt/redundancy_elimination.h
@@ -41,7 +41,7 @@ class RedundancyEliminationPass : public LocalRedundancyEliminationPass {
// in the function containing |bb|.
//
// |value_to_ids| is a map from value number to ids. If {vn, id} is in
- // |value_to_ids| then vn is the value number of id, and the defintion of id
+ // |value_to_ids| then vn is the value number of id, and the definition of id
// dominates |bb|.
//
// Returns true if at least one instruction is deleted.
diff --git a/source/opt/reflect.h b/source/opt/reflect.h
index c7d46df5..c2ffb0be 100644
--- a/source/opt/reflect.h
+++ b/source/opt/reflect.h
@@ -34,7 +34,7 @@ inline bool IsDebug2Inst(SpvOp opcode) {
inline bool IsDebug3Inst(SpvOp opcode) {
return opcode == SpvOpModuleProcessed;
}
-inline bool IsDebugLineInst(SpvOp opcode) {
+inline bool IsOpLineInst(SpvOp opcode) {
return opcode == SpvOpLine || opcode == SpvOpNoLine;
}
inline bool IsAnnotationInst(SpvOp opcode) {
@@ -51,7 +51,8 @@ inline bool IsTypeInst(SpvOp opcode) {
opcode == SpvOpTypeCooperativeMatrixNV;
}
inline bool IsConstantInst(SpvOp opcode) {
- return opcode >= SpvOpConstantTrue && opcode <= SpvOpSpecConstantOp;
+ return (opcode >= SpvOpConstantTrue && opcode <= SpvOpSpecConstantOp) ||
+ opcode == SpvOpConstantFunctionPointerINTEL;
}
inline bool IsCompileTimeConstantInst(SpvOp opcode) {
return opcode >= SpvOpConstantTrue && opcode <= SpvOpConstantNull;
diff --git a/source/opt/register_pressure.cpp b/source/opt/register_pressure.cpp
index 5750c6d4..1ad33738 100644
--- a/source/opt/register_pressure.cpp
+++ b/source/opt/register_pressure.cpp
@@ -378,7 +378,7 @@ void RegisterLiveness::SimulateFusion(
// The loop fusion is injecting the l1 before the l2, the latch of l1 will be
// connected to the header of l2.
// To compute the register usage, we inject the loop live-in (union of l1 and
- // l2 live-in header blocks) into the the live in/out of each basic block of
+ // l2 live-in header blocks) into the live in/out of each basic block of
// l1 to get the peak register usage. We then repeat the operation to for l2
// basic blocks but in this case we inject the live-out of the latch of l1.
auto live_loop = MakeFilterIteratorRange(
diff --git a/source/opt/relax_float_ops_pass.cpp b/source/opt/relax_float_ops_pass.cpp
index 73f16ddf..3fcf8795 100644
--- a/source/opt/relax_float_ops_pass.cpp
+++ b/source/opt/relax_float_ops_pass.cpp
@@ -76,7 +76,7 @@ Pass::Status RelaxFloatOpsPass::ProcessImpl() {
Pass::ProcessFunction pfn = [this](Function* fp) {
return ProcessFunction(fp);
};
- bool modified = context()->ProcessEntryPointCallTree(pfn);
+ bool modified = context()->ProcessReachableCallTree(pfn);
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
diff --git a/source/opt/remove_duplicates_pass.cpp b/source/opt/remove_duplicates_pass.cpp
index 0e65cc8d..1ed8e2a0 100644
--- a/source/opt/remove_duplicates_pass.cpp
+++ b/source/opt/remove_duplicates_pass.cpp
@@ -72,9 +72,8 @@ bool RemoveDuplicatesPass::RemoveDuplicatesExtInstImports() const {
std::unordered_map<std::string, SpvId> ext_inst_imports;
for (auto* i = &*context()->ext_inst_import_begin(); i;) {
- auto res = ext_inst_imports.emplace(
- reinterpret_cast<const char*>(i->GetInOperand(0u).words.data()),
- i->result_id());
+ auto res = ext_inst_imports.emplace(i->GetInOperand(0u).AsString(),
+ i->result_id());
if (res.second) {
// Never seen before, keep it.
i = i->NextNode();
diff --git a/source/opt/remove_unused_interface_variables_pass.cpp b/source/opt/remove_unused_interface_variables_pass.cpp
new file mode 100644
index 00000000..31e87bd4
--- /dev/null
+++ b/source/opt/remove_unused_interface_variables_pass.cpp
@@ -0,0 +1,93 @@
+// Copyright (c) 2021 ZHOU He
+//
+// 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 "remove_unused_interface_variables_pass.h"
+#include "source/spirv_constant.h"
+namespace spvtools {
+namespace opt {
+
+class RemoveUnusedInterfaceVariablesContext {
+ RemoveUnusedInterfaceVariablesPass& parent_;
+ Instruction& entry_;
+ std::unordered_set<uint32_t> used_variables_;
+ IRContext::ProcessFunction pfn_ =
+ std::bind(&RemoveUnusedInterfaceVariablesContext::processFunction, this,
+ std::placeholders::_1);
+
+ bool processFunction(Function* func) {
+ for (const auto& basic_block : *func)
+ for (const auto& instruction : basic_block)
+ instruction.ForEachInId([&](const uint32_t* id) {
+ if (used_variables_.count(*id)) return;
+ auto* var = parent_.get_def_use_mgr()->GetDef(*id);
+ if (!var || var->opcode() != SpvOpVariable) return;
+ auto storage_class = var->GetSingleWordInOperand(0);
+ if (storage_class != SpvStorageClassFunction &&
+ (parent_.get_module()->version() >=
+ SPV_SPIRV_VERSION_WORD(1, 4) ||
+ storage_class == SpvStorageClassInput ||
+ storage_class == SpvStorageClassOutput))
+ used_variables_.insert(*id);
+ });
+ return false;
+ }
+
+ public:
+ RemoveUnusedInterfaceVariablesContext(
+ RemoveUnusedInterfaceVariablesPass& parent, Instruction& entry)
+ : parent_(parent), entry_(entry) {}
+
+ void CollectUsedVariables() {
+ std::queue<uint32_t> roots;
+ roots.push(entry_.GetSingleWordInOperand(1));
+ parent_.context()->ProcessCallTreeFromRoots(pfn_, &roots);
+ }
+
+ bool ShouldModify() {
+ std::unordered_set<uint32_t> old_variables;
+ for (int i = entry_.NumInOperands() - 1; i >= 3; --i) {
+ auto variable = entry_.GetInOperand(i).words[0];
+ if (!used_variables_.count(variable)) return true; // It is unused.
+ if (old_variables.count(variable)) return true; // It is duplicate.
+ old_variables.insert(variable);
+ }
+ if (old_variables.size() != used_variables_.size()) // Missing IDs.
+ return true;
+ return false;
+ }
+
+ void Modify() {
+ for (int i = entry_.NumInOperands() - 1; i >= 3; --i)
+ entry_.RemoveInOperand(i);
+ for (auto id : used_variables_) {
+ entry_.AddOperand(Operand(SPV_OPERAND_TYPE_ID, {id}));
+ }
+ }
+};
+
+RemoveUnusedInterfaceVariablesPass::Status
+RemoveUnusedInterfaceVariablesPass::Process() {
+ bool modified = false;
+ for (auto& entry : get_module()->entry_points()) {
+ RemoveUnusedInterfaceVariablesContext context(*this, entry);
+ context.CollectUsedVariables();
+ if (context.ShouldModify()) {
+ context.Modify();
+ modified = true;
+ }
+ }
+ return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
+}
+} // namespace opt
+} // namespace spvtools \ No newline at end of file
diff --git a/source/opt/remove_unused_interface_variables_pass.h b/source/opt/remove_unused_interface_variables_pass.h
new file mode 100644
index 00000000..7f11187c
--- /dev/null
+++ b/source/opt/remove_unused_interface_variables_pass.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2021 ZHOU He
+//
+// 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/pass.h"
+namespace spvtools {
+namespace opt {
+
+class RemoveUnusedInterfaceVariablesPass : public Pass {
+ const char* name() const override {
+ return "remove-unused-interface-variables-pass";
+ }
+ Status Process() override;
+};
+} // namespace opt
+} // namespace spvtools \ No newline at end of file
diff --git a/source/opt/replace_desc_array_access_using_var_index.cpp b/source/opt/replace_desc_array_access_using_var_index.cpp
new file mode 100644
index 00000000..1082e679
--- /dev/null
+++ b/source/opt/replace_desc_array_access_using_var_index.cpp
@@ -0,0 +1,423 @@
+// Copyright (c) 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/opt/replace_desc_array_access_using_var_index.h"
+
+#include "source/opt/desc_sroa_util.h"
+#include "source/opt/ir_builder.h"
+#include "source/util/string_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+const uint32_t kOpAccessChainInOperandIndexes = 1;
+const uint32_t kOpTypePointerInOperandType = 1;
+const uint32_t kOpTypeArrayInOperandType = 0;
+const uint32_t kOpTypeStructInOperandMember = 0;
+IRContext::Analysis kAnalysisDefUseAndInstrToBlockMapping =
+ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping;
+
+uint32_t GetValueWithKeyExistenceCheck(
+ uint32_t key, const std::unordered_map<uint32_t, uint32_t>& map) {
+ auto itr = map.find(key);
+ assert(itr != map.end() && "Key does not exist");
+ return itr->second;
+}
+
+} // namespace
+
+Pass::Status ReplaceDescArrayAccessUsingVarIndex::Process() {
+ Status status = Status::SuccessWithoutChange;
+ for (Instruction& var : context()->types_values()) {
+ if (descsroautil::IsDescriptorArray(context(), &var)) {
+ if (ReplaceVariableAccessesWithConstantElements(&var))
+ status = Status::SuccessWithChange;
+ }
+ }
+ return status;
+}
+
+bool ReplaceDescArrayAccessUsingVarIndex::
+ ReplaceVariableAccessesWithConstantElements(Instruction* var) const {
+ std::vector<Instruction*> work_list;
+ get_def_use_mgr()->ForEachUser(var, [&work_list](Instruction* use) {
+ switch (use->opcode()) {
+ case SpvOpAccessChain:
+ case SpvOpInBoundsAccessChain:
+ work_list.push_back(use);
+ break;
+ default:
+ break;
+ }
+ });
+
+ bool updated = false;
+ for (Instruction* access_chain : work_list) {
+ if (descsroautil::GetAccessChainIndexAsConst(context(), access_chain) ==
+ nullptr) {
+ ReplaceAccessChain(var, access_chain);
+ updated = true;
+ }
+ }
+ // Note that we do not consider OpLoad and OpCompositeExtract because
+ // OpCompositeExtract always has constant literals for indices.
+ return updated;
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain(
+ Instruction* var, Instruction* access_chain) const {
+ uint32_t number_of_elements =
+ descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var);
+ assert(number_of_elements != 0 && "Number of element is 0");
+ if (number_of_elements == 1) {
+ UseConstIndexForAccessChain(access_chain, 0);
+ get_def_use_mgr()->AnalyzeInstUse(access_chain);
+ return;
+ }
+ ReplaceUsersOfAccessChain(access_chain, number_of_elements);
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::ReplaceUsersOfAccessChain(
+ Instruction* access_chain, uint32_t number_of_elements) const {
+ std::vector<Instruction*> final_users;
+ CollectRecursiveUsersWithConcreteType(access_chain, &final_users);
+ for (auto* inst : final_users) {
+ std::deque<Instruction*> insts_to_be_cloned =
+ CollectRequiredImageInsts(inst);
+ ReplaceNonUniformAccessWithSwitchCase(
+ inst, access_chain, number_of_elements, insts_to_be_cloned);
+ }
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::CollectRecursiveUsersWithConcreteType(
+ Instruction* access_chain, std::vector<Instruction*>* final_users) const {
+ std::queue<Instruction*> work_list;
+ work_list.push(access_chain);
+ while (!work_list.empty()) {
+ auto* inst_from_work_list = work_list.front();
+ work_list.pop();
+ get_def_use_mgr()->ForEachUser(
+ inst_from_work_list, [this, final_users, &work_list](Instruction* use) {
+ // TODO: Support Boolean type as well.
+ if (!use->HasResultId() || IsConcreteType(use->type_id())) {
+ final_users->push_back(use);
+ } else {
+ work_list.push(use);
+ }
+ });
+ }
+}
+
+std::deque<Instruction*>
+ReplaceDescArrayAccessUsingVarIndex::CollectRequiredImageInsts(
+ Instruction* user_of_image_insts) const {
+ std::unordered_set<uint32_t> seen_inst_ids;
+ std::queue<Instruction*> work_list;
+
+ auto decision_to_include_operand = [this, &seen_inst_ids,
+ &work_list](uint32_t* idp) {
+ if (!seen_inst_ids.insert(*idp).second) return;
+ Instruction* operand = get_def_use_mgr()->GetDef(*idp);
+ if (context()->get_instr_block(operand) != nullptr &&
+ HasImageOrImagePtrType(operand)) {
+ work_list.push(operand);
+ }
+ };
+
+ std::deque<Instruction*> required_image_insts;
+ required_image_insts.push_front(user_of_image_insts);
+ user_of_image_insts->ForEachInId(decision_to_include_operand);
+ while (!work_list.empty()) {
+ auto* inst_from_work_list = work_list.front();
+ work_list.pop();
+ required_image_insts.push_front(inst_from_work_list);
+ inst_from_work_list->ForEachInId(decision_to_include_operand);
+ }
+ return required_image_insts;
+}
+
+bool ReplaceDescArrayAccessUsingVarIndex::HasImageOrImagePtrType(
+ const Instruction* inst) const {
+ assert(inst != nullptr && inst->type_id() != 0 && "Invalid instruction");
+ return IsImageOrImagePtrType(get_def_use_mgr()->GetDef(inst->type_id()));
+}
+
+bool ReplaceDescArrayAccessUsingVarIndex::IsImageOrImagePtrType(
+ const Instruction* type_inst) const {
+ if (type_inst->opcode() == SpvOpTypeImage ||
+ type_inst->opcode() == SpvOpTypeSampler ||
+ type_inst->opcode() == SpvOpTypeSampledImage) {
+ return true;
+ }
+ if (type_inst->opcode() == SpvOpTypePointer) {
+ Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(
+ type_inst->GetSingleWordInOperand(kOpTypePointerInOperandType));
+ return IsImageOrImagePtrType(pointee_type_inst);
+ }
+ if (type_inst->opcode() == SpvOpTypeArray) {
+ Instruction* element_type_inst = get_def_use_mgr()->GetDef(
+ type_inst->GetSingleWordInOperand(kOpTypeArrayInOperandType));
+ return IsImageOrImagePtrType(element_type_inst);
+ }
+ if (type_inst->opcode() != SpvOpTypeStruct) return false;
+ for (uint32_t in_operand_idx = kOpTypeStructInOperandMember;
+ in_operand_idx < type_inst->NumInOperands(); ++in_operand_idx) {
+ Instruction* member_type_inst = get_def_use_mgr()->GetDef(
+ type_inst->GetSingleWordInOperand(kOpTypeStructInOperandMember));
+ if (IsImageOrImagePtrType(member_type_inst)) return true;
+ }
+ return false;
+}
+
+bool ReplaceDescArrayAccessUsingVarIndex::IsConcreteType(
+ uint32_t type_id) const {
+ Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
+ if (type_inst->opcode() == SpvOpTypeInt ||
+ type_inst->opcode() == SpvOpTypeFloat) {
+ return true;
+ }
+ if (type_inst->opcode() == SpvOpTypeVector ||
+ type_inst->opcode() == SpvOpTypeMatrix ||
+ type_inst->opcode() == SpvOpTypeArray) {
+ return IsConcreteType(type_inst->GetSingleWordInOperand(0));
+ }
+ if (type_inst->opcode() == SpvOpTypeStruct) {
+ for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) {
+ if (!IsConcreteType(type_inst->GetSingleWordInOperand(i))) return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateCaseBlock(
+ Instruction* access_chain, uint32_t element_index,
+ const std::deque<Instruction*>& insts_to_be_cloned,
+ uint32_t branch_target_id,
+ std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
+ auto* case_block = CreateNewBlock();
+ AddConstElementAccessToCaseBlock(case_block, access_chain, element_index,
+ old_ids_to_new_ids);
+ CloneInstsToBlock(case_block, access_chain, insts_to_be_cloned,
+ old_ids_to_new_ids);
+ AddBranchToBlock(case_block, branch_target_id);
+ UseNewIdsInBlock(case_block, *old_ids_to_new_ids);
+ return case_block;
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock(
+ BasicBlock* block, Instruction* inst_to_skip_cloning,
+ const std::deque<Instruction*>& insts_to_be_cloned,
+ std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
+ for (auto* inst_to_be_cloned : insts_to_be_cloned) {
+ if (inst_to_be_cloned == inst_to_skip_cloning) continue;
+ std::unique_ptr<Instruction> clone(inst_to_be_cloned->Clone(context()));
+ if (inst_to_be_cloned->HasResultId()) {
+ uint32_t new_id = context()->TakeNextId();
+ clone->SetResultId(new_id);
+ (*old_ids_to_new_ids)[inst_to_be_cloned->result_id()] = new_id;
+ }
+ get_def_use_mgr()->AnalyzeInstDefUse(clone.get());
+ context()->set_instr_block(clone.get(), block);
+ block->AddInstruction(std::move(clone));
+ }
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::UseNewIdsInBlock(
+ BasicBlock* block,
+ const std::unordered_map<uint32_t, uint32_t>& old_ids_to_new_ids) const {
+ for (auto block_itr = block->begin(); block_itr != block->end();
+ ++block_itr) {
+ (&*block_itr)->ForEachInId([&old_ids_to_new_ids](uint32_t* idp) {
+ auto old_ids_to_new_ids_itr = old_ids_to_new_ids.find(*idp);
+ if (old_ids_to_new_ids_itr == old_ids_to_new_ids.end()) return;
+ *idp = old_ids_to_new_ids_itr->second;
+ });
+ get_def_use_mgr()->AnalyzeInstUse(&*block_itr);
+ }
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase(
+ Instruction* access_chain_final_user, Instruction* access_chain,
+ uint32_t number_of_elements,
+ const std::deque<Instruction*>& insts_to_be_cloned) const {
+ // Create merge block and add terminator
+ auto* block = context()->get_instr_block(access_chain_final_user);
+ auto* merge_block = SeparateInstructionsIntoNewBlock(
+ block, access_chain_final_user->NextNode());
+
+ auto* function = block->GetParent();
+
+ // Add case blocks
+ std::vector<uint32_t> phi_operands;
+ std::vector<uint32_t> case_block_ids;
+ for (uint32_t idx = 0; idx < number_of_elements; ++idx) {
+ std::unordered_map<uint32_t, uint32_t> old_ids_to_new_ids_for_cloned_insts;
+ std::unique_ptr<BasicBlock> case_block(CreateCaseBlock(
+ access_chain, idx, insts_to_be_cloned, merge_block->id(),
+ &old_ids_to_new_ids_for_cloned_insts));
+ case_block_ids.push_back(case_block->id());
+ function->InsertBasicBlockBefore(std::move(case_block), merge_block);
+
+ // Keep the operand for OpPhi
+ if (!access_chain_final_user->HasResultId()) continue;
+ uint32_t phi_operand =
+ GetValueWithKeyExistenceCheck(access_chain_final_user->result_id(),
+ old_ids_to_new_ids_for_cloned_insts);
+ phi_operands.push_back(phi_operand);
+ }
+
+ // Create default block
+ std::unique_ptr<BasicBlock> default_block(
+ CreateDefaultBlock(access_chain_final_user->HasResultId(), &phi_operands,
+ merge_block->id()));
+ uint32_t default_block_id = default_block->id();
+ function->InsertBasicBlockBefore(std::move(default_block), merge_block);
+
+ // Create OpSwitch
+ uint32_t access_chain_index_var_id =
+ descsroautil::GetFirstIndexOfAccessChain(access_chain);
+ AddSwitchForAccessChain(block, access_chain_index_var_id, default_block_id,
+ merge_block->id(), case_block_ids);
+
+ // Create phi instructions
+ if (!phi_operands.empty()) {
+ uint32_t phi_id = CreatePhiInstruction(merge_block, phi_operands,
+ case_block_ids, default_block_id);
+ context()->ReplaceAllUsesWith(access_chain_final_user->result_id(), phi_id);
+ }
+
+ // Replace OpPhi incoming block operand that uses |block| with |merge_block|
+ ReplacePhiIncomingBlock(block->id(), merge_block->id());
+}
+
+BasicBlock*
+ReplaceDescArrayAccessUsingVarIndex::SeparateInstructionsIntoNewBlock(
+ BasicBlock* block, Instruction* separation_begin_inst) const {
+ auto separation_begin = block->begin();
+ while (separation_begin != block->end() &&
+ &*separation_begin != separation_begin_inst) {
+ ++separation_begin;
+ }
+ return block->SplitBasicBlock(context(), context()->TakeNextId(),
+ separation_begin);
+}
+
+BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateNewBlock() const {
+ auto* new_block = new BasicBlock(std::unique_ptr<Instruction>(
+ new Instruction(context(), SpvOpLabel, 0, context()->TakeNextId(), {})));
+ get_def_use_mgr()->AnalyzeInstDefUse(new_block->GetLabelInst());
+ context()->set_instr_block(new_block->GetLabelInst(), new_block);
+ return new_block;
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::UseConstIndexForAccessChain(
+ Instruction* access_chain, uint32_t const_element_idx) const {
+ uint32_t const_element_idx_id =
+ context()->get_constant_mgr()->GetUIntConst(const_element_idx);
+ access_chain->SetInOperand(kOpAccessChainInOperandIndexes,
+ {const_element_idx_id});
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::AddConstElementAccessToCaseBlock(
+ BasicBlock* case_block, Instruction* access_chain,
+ uint32_t const_element_idx,
+ std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
+ std::unique_ptr<Instruction> access_clone(access_chain->Clone(context()));
+ UseConstIndexForAccessChain(access_clone.get(), const_element_idx);
+
+ uint32_t new_access_id = context()->TakeNextId();
+ (*old_ids_to_new_ids)[access_clone->result_id()] = new_access_id;
+ access_clone->SetResultId(new_access_id);
+ get_def_use_mgr()->AnalyzeInstDefUse(access_clone.get());
+
+ context()->set_instr_block(access_clone.get(), case_block);
+ case_block->AddInstruction(std::move(access_clone));
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::AddBranchToBlock(
+ BasicBlock* parent_block, uint32_t branch_destination) const {
+ InstructionBuilder builder{context(), parent_block,
+ kAnalysisDefUseAndInstrToBlockMapping};
+ builder.AddBranch(branch_destination);
+}
+
+BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateDefaultBlock(
+ bool null_const_for_phi_is_needed, std::vector<uint32_t>* phi_operands,
+ uint32_t merge_block_id) const {
+ auto* default_block = CreateNewBlock();
+ AddBranchToBlock(default_block, merge_block_id);
+ if (!null_const_for_phi_is_needed) return default_block;
+
+ // Create null value for OpPhi
+ Instruction* inst = context()->get_def_use_mgr()->GetDef((*phi_operands)[0]);
+ auto* null_const_inst = GetConstNull(inst->type_id());
+ phi_operands->push_back(null_const_inst->result_id());
+ return default_block;
+}
+
+Instruction* ReplaceDescArrayAccessUsingVarIndex::GetConstNull(
+ uint32_t type_id) const {
+ assert(type_id != 0 && "Result type is expected");
+ auto* type = context()->get_type_mgr()->GetType(type_id);
+ auto* null_const = context()->get_constant_mgr()->GetConstant(type, {});
+ return context()->get_constant_mgr()->GetDefiningInstruction(null_const);
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::AddSwitchForAccessChain(
+ BasicBlock* parent_block, uint32_t access_chain_index_var_id,
+ uint32_t default_id, uint32_t merge_id,
+ const std::vector<uint32_t>& case_block_ids) const {
+ InstructionBuilder builder{context(), parent_block,
+ kAnalysisDefUseAndInstrToBlockMapping};
+ std::vector<std::pair<Operand::OperandData, uint32_t>> cases;
+ for (uint32_t i = 0; i < static_cast<uint32_t>(case_block_ids.size()); ++i) {
+ cases.emplace_back(Operand::OperandData{i}, case_block_ids[i]);
+ }
+ builder.AddSwitch(access_chain_index_var_id, default_id, cases, merge_id);
+}
+
+uint32_t ReplaceDescArrayAccessUsingVarIndex::CreatePhiInstruction(
+ BasicBlock* parent_block, const std::vector<uint32_t>& phi_operands,
+ const std::vector<uint32_t>& case_block_ids,
+ uint32_t default_block_id) const {
+ std::vector<uint32_t> incomings;
+ assert(case_block_ids.size() + 1 == phi_operands.size() &&
+ "Number of Phi operands must be exactly 1 bigger than the one of case "
+ "blocks");
+ for (size_t i = 0; i < case_block_ids.size(); ++i) {
+ incomings.push_back(phi_operands[i]);
+ incomings.push_back(case_block_ids[i]);
+ }
+ incomings.push_back(phi_operands.back());
+ incomings.push_back(default_block_id);
+
+ InstructionBuilder builder{context(), &*parent_block->begin(),
+ kAnalysisDefUseAndInstrToBlockMapping};
+ uint32_t phi_result_type_id =
+ context()->get_def_use_mgr()->GetDef(phi_operands[0])->type_id();
+ auto* phi = builder.AddPhi(phi_result_type_id, incomings);
+ return phi->result_id();
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::ReplacePhiIncomingBlock(
+ uint32_t old_incoming_block_id, uint32_t new_incoming_block_id) const {
+ context()->ReplaceAllUsesWithPredicate(
+ old_incoming_block_id, new_incoming_block_id,
+ [](Instruction* use) { return use->opcode() == SpvOpPhi; });
+}
+
+} // namespace opt
+} // namespace spvtools
diff --git a/source/opt/replace_desc_array_access_using_var_index.h b/source/opt/replace_desc_array_access_using_var_index.h
new file mode 100644
index 00000000..0c97f7eb
--- /dev/null
+++ b/source/opt/replace_desc_array_access_using_var_index.h
@@ -0,0 +1,204 @@
+// Copyright (c) 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_
+#define SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_
+
+#include <cstdio>
+#include <memory>
+#include <queue>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "source/opt/function.h"
+#include "source/opt/pass.h"
+#include "source/opt/type_manager.h"
+
+namespace spvtools {
+namespace opt {
+
+// See optimizer.hpp for documentation.
+class ReplaceDescArrayAccessUsingVarIndex : public Pass {
+ public:
+ ReplaceDescArrayAccessUsingVarIndex() {}
+
+ const char* name() const override {
+ return "replace-desc-array-access-using-var-index";
+ }
+
+ Status Process() override;
+
+ IRContext::Analysis GetPreservedAnalyses() override {
+ return IRContext::kAnalysisDefUse |
+ IRContext::kAnalysisInstrToBlockMapping |
+ IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
+ }
+
+ private:
+ // Replaces all accesses to |var| using variable indices with constant
+ // elements of the array |var|. Creates switch-case statements to determine
+ // the value of the variable index for all the possible cases. Returns
+ // whether replacement is done or not.
+ bool ReplaceVariableAccessesWithConstantElements(Instruction* var) const;
+
+ // Replaces the OpAccessChain or OpInBoundsAccessChain instruction |use| that
+ // uses the descriptor variable |var| with the OpAccessChain or
+ // OpInBoundsAccessChain instruction with a constant Indexes operand.
+ void ReplaceAccessChain(Instruction* var, Instruction* use) const;
+
+ // Updates the first Indexes operand of the OpAccessChain or
+ // OpInBoundsAccessChain instruction |access_chain| to let it use a constant
+ // index |const_element_idx|.
+ void UseConstIndexForAccessChain(Instruction* access_chain,
+ uint32_t const_element_idx) const;
+
+ // Replaces users of the OpAccessChain or OpInBoundsAccessChain instruction
+ // |access_chain| that accesses an array descriptor variable using variable
+ // indices with constant elements. |number_of_elements| is the number
+ // of array elements.
+ void ReplaceUsersOfAccessChain(Instruction* access_chain,
+ uint32_t number_of_elements) const;
+
+ // Puts all the recursive users of |access_chain| with concrete result types
+ // or the ones without result it in |final_users|.
+ void CollectRecursiveUsersWithConcreteType(
+ Instruction* access_chain, std::vector<Instruction*>* final_users) const;
+
+ // Recursively collects the operands of |user_of_image_insts| (and operands
+ // of the operands) whose result types are images/samplers or pointers/array/
+ // struct of them and returns them.
+ std::deque<Instruction*> CollectRequiredImageInsts(
+ Instruction* user_of_image_insts) const;
+
+ // Returns whether result type of |inst| is an image/sampler/pointer of image
+ // or sampler or not.
+ bool HasImageOrImagePtrType(const Instruction* inst) const;
+
+ // Returns whether |type_inst| is an image/sampler or pointer/array/struct of
+ // image or sampler or not.
+ bool IsImageOrImagePtrType(const Instruction* type_inst) const;
+
+ // Returns whether the type with |type_id| is a concrete type or not.
+ bool IsConcreteType(uint32_t type_id) const;
+
+ // Replaces the non-uniform access to a descriptor variable
+ // |access_chain_final_user| with OpSwitch instruction and case blocks. Each
+ // case block will contain a clone of |access_chain| and clones of
+ // |non_uniform_accesses_to_clone| that are recursively used by
+ // |access_chain_final_user|. The clone of |access_chain| (or
+ // OpInBoundsAccessChain) will have a constant index for its first index. The
+ // OpSwitch instruction will have the cases for the variable index of
+ // |access_chain| from 0 to |number_of_elements| - 1.
+ void ReplaceNonUniformAccessWithSwitchCase(
+ Instruction* access_chain_final_user, Instruction* access_chain,
+ uint32_t number_of_elements,
+ const std::deque<Instruction*>& non_uniform_accesses_to_clone) const;
+
+ // Creates and returns a new basic block that contains all instructions of
+ // |block| after |separation_begin_inst|. The new basic block is added to the
+ // function in this method.
+ BasicBlock* SeparateInstructionsIntoNewBlock(
+ BasicBlock* block, Instruction* separation_begin_inst) const;
+
+ // Creates and returns a new block.
+ BasicBlock* CreateNewBlock() const;
+
+ // Returns the first operand id of the OpAccessChain or OpInBoundsAccessChain
+ // instruction |access_chain|.
+ uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain) const;
+
+ // Adds a clone of the OpAccessChain or OpInBoundsAccessChain instruction
+ // |access_chain| to |case_block|. The clone of |access_chain| will use
+ // |const_element_idx| for its first index. |old_ids_to_new_ids| keeps the
+ // mapping from the result id of |access_chain| to the result of its clone.
+ void AddConstElementAccessToCaseBlock(
+ BasicBlock* case_block, Instruction* access_chain,
+ uint32_t const_element_idx,
+ std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
+
+ // Clones all instructions in |insts_to_be_cloned| and put them to |block|.
+ // |old_ids_to_new_ids| keeps the mapping from the result id of each
+ // instruction of |insts_to_be_cloned| to the result of their clones.
+ void CloneInstsToBlock(
+ BasicBlock* block, Instruction* inst_to_skip_cloning,
+ const std::deque<Instruction*>& insts_to_be_cloned,
+ std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
+
+ // Adds OpBranch to |branch_destination| at the end of |parent_block|.
+ void AddBranchToBlock(BasicBlock* parent_block,
+ uint32_t branch_destination) const;
+
+ // Replaces in-operands of all instructions in the basic block |block| using
+ // |old_ids_to_new_ids|. It conducts the replacement only if the in-operand
+ // id is a key of |old_ids_to_new_ids|.
+ void UseNewIdsInBlock(
+ BasicBlock* block,
+ const std::unordered_map<uint32_t, uint32_t>& old_ids_to_new_ids) const;
+
+ // Creates a case block for |element_index| case. It adds clones of
+ // |insts_to_be_cloned| and a clone of |access_chain| with |element_index| as
+ // its first index. The termination instruction of the created case block will
+ // be a branch to |branch_target_id|. Puts old ids to new ids map for the
+ // cloned instructions in |old_ids_to_new_ids|.
+ BasicBlock* CreateCaseBlock(
+ Instruction* access_chain, uint32_t element_index,
+ const std::deque<Instruction*>& insts_to_be_cloned,
+ uint32_t branch_target_id,
+ std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
+
+ // Creates a default block for switch-case statement that has only a single
+ // instruction OpBranch whose target is a basic block with |merge_block_id|.
+ // If |null_const_for_phi_is_needed| is true, gets or creates a default null
+ // constant value for a phi instruction whose operands are |phi_operands| and
+ // puts it in |phi_operands|.
+ BasicBlock* CreateDefaultBlock(bool null_const_for_phi_is_needed,
+ std::vector<uint32_t>* phi_operands,
+ uint32_t merge_block_id) const;
+
+ // Creates and adds an OpSwitch used for the selection of OpAccessChain whose
+ // first Indexes operand is |access_chain_index_var_id|. The OpSwitch will be
+ // added at the end of |parent_block|. It will jump to |default_id| for the
+ // default case and jumps to one of case blocks whose ids are |case_block_ids|
+ // if |access_chain_index_var_id| matches the case number. |merge_id| is the
+ // merge block id.
+ void AddSwitchForAccessChain(
+ BasicBlock* parent_block, uint32_t access_chain_index_var_id,
+ uint32_t default_id, uint32_t merge_id,
+ const std::vector<uint32_t>& case_block_ids) const;
+
+ // Creates a phi instruction with |phi_operands| as values and
+ // |case_block_ids| and |default_block_id| as incoming blocks. The size of
+ // |phi_operands| must be exactly 1 larger than the size of |case_block_ids|.
+ // The last element of |phi_operands| will be used for |default_block_id|. It
+ // adds the phi instruction to the beginning of |parent_block|.
+ uint32_t CreatePhiInstruction(BasicBlock* parent_block,
+ const std::vector<uint32_t>& phi_operands,
+ const std::vector<uint32_t>& case_block_ids,
+ uint32_t default_block_id) const;
+
+ // Replaces the incoming block operand of OpPhi instructions with
+ // |new_incoming_block_id| if the incoming block operand is
+ // |old_incoming_block_id|.
+ void ReplacePhiIncomingBlock(uint32_t old_incoming_block_id,
+ uint32_t new_incoming_block_id) const;
+
+ // Create an OpConstantNull instruction whose result type id is |type_id|.
+ Instruction* GetConstNull(uint32_t type_id) const;
+};
+
+} // namespace opt
+} // namespace spvtools
+
+#endif // SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_
diff --git a/source/opt/replace_invalid_opc.cpp b/source/opt/replace_invalid_opc.cpp
index 38b7539b..1dcd06f5 100644
--- a/source/opt/replace_invalid_opc.cpp
+++ b/source/opt/replace_invalid_opc.cpp
@@ -71,10 +71,10 @@ bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function,
function->ForEachInst(
[model, &modified, &last_line_dbg_inst, this](Instruction* inst) {
// Track the debug information so we can have a meaningful message.
- if (inst->opcode() == SpvOpLabel || inst->opcode() == SpvOpNoLine) {
+ if (inst->opcode() == SpvOpLabel || inst->IsNoLine()) {
last_line_dbg_inst = nullptr;
return;
- } else if (inst->opcode() == SpvOpLine) {
+ } else if (inst->IsLine()) {
last_line_dbg_inst = inst;
return;
}
@@ -100,10 +100,19 @@ bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function,
ReplaceInstruction(inst, nullptr, 0, 0);
} else {
// Get the name of the source file.
- Instruction* file_name = context()->get_def_use_mgr()->GetDef(
- last_line_dbg_inst->GetSingleWordInOperand(0));
- const char* source = reinterpret_cast<const char*>(
- &file_name->GetInOperand(0).words[0]);
+ uint32_t file_name_id = 0;
+ if (last_line_dbg_inst->opcode() == SpvOpLine) {
+ file_name_id = last_line_dbg_inst->GetSingleWordInOperand(0);
+ } else { // Shader100::DebugLine
+ uint32_t debug_source_id =
+ last_line_dbg_inst->GetSingleWordInOperand(2);
+ Instruction* debug_source_inst =
+ context()->get_def_use_mgr()->GetDef(debug_source_id);
+ file_name_id = debug_source_inst->GetSingleWordInOperand(2);
+ }
+ Instruction* file_name =
+ context()->get_def_use_mgr()->GetDef(file_name_id);
+ const std::string source = file_name->GetInOperand(0).AsString();
// Get the line number and column number.
uint32_t line_number =
@@ -111,7 +120,7 @@ bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function,
uint32_t col_number = last_line_dbg_inst->GetSingleWordInOperand(2);
// Replace the instruction.
- ReplaceInstruction(inst, source, line_number, col_number);
+ ReplaceInstruction(inst, source.c_str(), line_number, col_number);
}
}
},
diff --git a/source/opt/scalar_analysis.cpp b/source/opt/scalar_analysis.cpp
index 38555e64..2b0a824c 100644
--- a/source/opt/scalar_analysis.cpp
+++ b/source/opt/scalar_analysis.cpp
@@ -581,7 +581,7 @@ static void PushToString(T id, std::u32string* str) {
// Implements the hashing of SENodes.
size_t SENodeHash::operator()(const SENode* node) const {
- // Concatinate the terms into a string which we can hash.
+ // Concatenate the terms into a string which we can hash.
std::u32string hash_string{};
// Hashing the type as a string is safer than hashing the enum as the enum is
diff --git a/source/opt/scalar_analysis_nodes.h b/source/opt/scalar_analysis_nodes.h
index b0e3fefd..91ce446f 100644
--- a/source/opt/scalar_analysis_nodes.h
+++ b/source/opt/scalar_analysis_nodes.h
@@ -167,7 +167,7 @@ class SENode {
const ChildContainerType& GetChildren() const { return children_; }
ChildContainerType& GetChildren() { return children_; }
- // Return true if this node is a cant compute node.
+ // Return true if this node is a can't compute node.
bool IsCantCompute() const { return GetType() == CanNotCompute; }
// Implements a casting method for each type.
diff --git a/source/opt/scalar_analysis_simplification.cpp b/source/opt/scalar_analysis_simplification.cpp
index 52f2d6ad..3c1ecc08 100644
--- a/source/opt/scalar_analysis_simplification.cpp
+++ b/source/opt/scalar_analysis_simplification.cpp
@@ -88,7 +88,7 @@ class SENodeSimplifyImpl {
private:
// Recursively descend through the graph to build up the accumulator objects
- // which are used to flatten the graph. |child| is the node currenty being
+ // which are used to flatten the graph. |child| is the node currently being
// traversed and the |negation| flag is used to signify that this operation
// was preceded by a unary negative operation and as such the result should be
// negated.
@@ -134,7 +134,7 @@ class SENodeSimplifyImpl {
// offset.
SENode* EliminateZeroCoefficientRecurrents(SENode* node);
- // A reference the the analysis which requested the simplification.
+ // A reference the analysis which requested the simplification.
ScalarEvolutionAnalysis& analysis_;
// The node being simplified.
diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp
index 4d47bdd8..4d6a7aad 100644
--- a/source/opt/scalar_replacement_pass.cpp
+++ b/source/opt/scalar_replacement_pass.cpp
@@ -27,6 +27,7 @@
static const uint32_t kDebugValueOperandValueIndex = 5;
static const uint32_t kDebugValueOperandExpressionIndex = 6;
+static const uint32_t kDebugDeclareOperandVariableIndex = 5;
namespace spvtools {
namespace opt {
@@ -34,6 +35,10 @@ namespace opt {
Pass::Status ScalarReplacementPass::Process() {
Status status = Status::SuccessWithoutChange;
for (auto& f : *get_module()) {
+ if (f.IsDeclaration()) {
+ continue;
+ }
+
Status functionStatus = ProcessFunction(&f);
if (functionStatus == Status::Failure)
return functionStatus;
@@ -83,14 +88,14 @@ Pass::Status ScalarReplacementPass::ReplaceVariable(
std::vector<Instruction*> dead;
bool replaced_all_uses = get_def_use_mgr()->WhileEachUser(
inst, [this, &replacements, &dead](Instruction* user) {
- if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) {
+ if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) {
if (ReplaceWholeDebugDeclare(user, replacements)) {
dead.push_back(user);
return true;
}
return false;
}
- if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) {
+ if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) {
if (ReplaceWholeDebugValue(user, replacements)) {
dead.push_back(user);
return true;
@@ -172,10 +177,14 @@ bool ScalarReplacementPass::ReplaceWholeDebugDeclare(
// Add DebugValue instruction with Indexes operand and Deref operation.
int32_t idx = 0;
for (const auto* var : replacements) {
+ Instruction* insert_before = var->NextNode();
+ while (insert_before->opcode() == SpvOpVariable)
+ insert_before = insert_before->NextNode();
+ assert(insert_before != nullptr && "unexpected end of list");
Instruction* added_dbg_value =
context()->get_debug_info_mgr()->AddDebugValueForDecl(
dbg_decl, /*value_id=*/var->result_id(),
- /*insert_before=*/var->NextNode(), /*scope_and_line=*/dbg_decl);
+ /*insert_before=*/insert_before, /*scope_and_line=*/dbg_decl);
if (added_dbg_value == nullptr) return false;
added_dbg_value->AddOperand(
@@ -504,7 +513,7 @@ void ScalarReplacementPass::CreateVariable(
}
}
- // Update the OpenCL.DebugInfo.100 debug information.
+ // Update the DebugInfo debug information.
inst->UpdateDebugInfoFrom(varInst);
replacements->push_back(inst);
@@ -791,8 +800,8 @@ bool ScalarReplacementPass::CheckUses(const Instruction* inst,
get_def_use_mgr()->ForEachUse(inst, [this, max_legal_index, stats, &ok](
const Instruction* user,
uint32_t index) {
- if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare ||
- user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) {
+ if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare ||
+ user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) {
// TODO: include num_partial_accesses if it uses Fragment operation or
// DebugValue has Indexes operand.
stats->num_full_accesses++;
@@ -864,6 +873,11 @@ bool ScalarReplacementPass::CheckUsesRelaxed(const Instruction* inst) const {
case SpvOpImageTexelPointer:
if (!CheckImageTexelPointer(index)) ok = false;
break;
+ case SpvOpExtInst:
+ if (user->GetCommonDebugOpcode() != CommonDebugInfoDebugDeclare ||
+ !CheckDebugDeclare(index))
+ ok = false;
+ break;
default:
ok = false;
break;
@@ -894,6 +908,12 @@ bool ScalarReplacementPass::CheckStore(const Instruction* inst,
return false;
return true;
}
+
+bool ScalarReplacementPass::CheckDebugDeclare(uint32_t index) const {
+ if (index != kDebugDeclareOperandVariableIndex) return false;
+ return true;
+}
+
bool ScalarReplacementPass::IsLargerThanSizeLimit(uint64_t length) const {
if (max_num_elements_ == 0) {
return false;
diff --git a/source/opt/scalar_replacement_pass.h b/source/opt/scalar_replacement_pass.h
index 9e9f0739..0928830c 100644
--- a/source/opt/scalar_replacement_pass.h
+++ b/source/opt/scalar_replacement_pass.h
@@ -142,6 +142,9 @@ class ScalarReplacementPass : public Pass {
// of |inst| and the store is not to volatile memory.
bool CheckStore(const Instruction* inst, uint32_t index) const;
+ // Returns true if the DebugDeclare can be scalarized at |index|.
+ bool CheckDebugDeclare(uint32_t index) const;
+
// Returns true if |index| is the pointer operand of an OpImageTexelPointer
// instruction.
bool CheckImageTexelPointer(uint32_t index) const;
diff --git a/source/opt/set_spec_constant_default_value_pass.cpp b/source/opt/set_spec_constant_default_value_pass.cpp
index 4c8d116f..4def2b09 100644
--- a/source/opt/set_spec_constant_default_value_pass.cpp
+++ b/source/opt/set_spec_constant_default_value_pass.cpp
@@ -85,6 +85,10 @@ std::vector<uint32_t> ParseDefaultValueStr(const char* text,
// with 0x1, which represents a 'true'.
// If all words in the bit pattern are zero, returns a bit pattern with 0x0,
// which represents a 'false'.
+// For integer and floating point types narrower than 32 bits, the upper bits
+// in the input bit pattern are ignored. Instead the upper bits are set
+// according to SPIR-V literal requirements: sign extend a signed integer, and
+// otherwise set the upper bits to zero.
std::vector<uint32_t> ParseDefaultValueBitPattern(
const std::vector<uint32_t>& input_bit_pattern,
const analysis::Type* type) {
@@ -98,12 +102,33 @@ std::vector<uint32_t> ParseDefaultValueBitPattern(
}
return result;
} else if (const auto* IT = type->AsInteger()) {
- if (IT->width() == input_bit_pattern.size() * sizeof(uint32_t) * 8) {
- return std::vector<uint32_t>(input_bit_pattern);
+ const auto width = IT->width();
+ assert(width > 0);
+ const auto adjusted_width = std::max(32u, width);
+ if (adjusted_width == input_bit_pattern.size() * sizeof(uint32_t) * 8) {
+ result = std::vector<uint32_t>(input_bit_pattern);
+ if (width < 32) {
+ const uint32_t high_active_bit = (1u << width) >> 1;
+ if (IT->IsSigned() && (high_active_bit & result[0])) {
+ // Sign extend. This overwrites the sign bit again, but that's ok.
+ result[0] = result[0] | ~(high_active_bit - 1);
+ } else {
+ // Upper bits must be zero.
+ result[0] = result[0] & ((1u << width) - 1);
+ }
+ }
+ return result;
}
} else if (const auto* FT = type->AsFloat()) {
- if (FT->width() == input_bit_pattern.size() * sizeof(uint32_t) * 8) {
- return std::vector<uint32_t>(input_bit_pattern);
+ const auto width = FT->width();
+ const auto adjusted_width = std::max(32u, width);
+ if (adjusted_width == input_bit_pattern.size() * sizeof(uint32_t) * 8) {
+ result = std::vector<uint32_t>(input_bit_pattern);
+ if (width < 32) {
+ // Upper bits must be zero.
+ result[0] = result[0] & ((1u << width) - 1);
+ }
+ return result;
}
}
result.clear();
diff --git a/source/opt/simplification_pass.cpp b/source/opt/simplification_pass.cpp
index 319ceecf..43ec15f8 100644
--- a/source/opt/simplification_pass.cpp
+++ b/source/opt/simplification_pass.cpp
@@ -45,6 +45,10 @@ void SimplificationPass::AddNewOperands(
}
bool SimplificationPass::SimplifyFunction(Function* function) {
+ if (function->IsDeclaration()) {
+ return false;
+ }
+
bool modified = false;
// Phase 1: Traverse all instructions in dominance order.
// The second phase will only be on the instructions whose inputs have changed
diff --git a/source/opt/spread_volatile_semantics.cpp b/source/opt/spread_volatile_semantics.cpp
new file mode 100644
index 00000000..17a4c725
--- /dev/null
+++ b/source/opt/spread_volatile_semantics.cpp
@@ -0,0 +1,314 @@
+// 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/spread_volatile_semantics.h"
+
+#include "source/opt/decoration_manager.h"
+#include "source/opt/ir_builder.h"
+#include "source/spirv_constant.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+const uint32_t kOpDecorateInOperandBuiltinDecoration = 2u;
+const uint32_t kOpLoadInOperandMemoryOperands = 1u;
+const uint32_t kOpEntryPointInOperandEntryPoint = 1u;
+const uint32_t kOpEntryPointInOperandInterface = 3u;
+
+bool HasBuiltinDecoration(analysis::DecorationManager* decoration_manager,
+ uint32_t var_id, uint32_t built_in) {
+ return decoration_manager->FindDecoration(
+ var_id, SpvDecorationBuiltIn, [built_in](const Instruction& inst) {
+ return built_in == inst.GetSingleWordInOperand(
+ kOpDecorateInOperandBuiltinDecoration);
+ });
+}
+
+bool IsBuiltInForRayTracingVolatileSemantics(uint32_t built_in) {
+ switch (built_in) {
+ case SpvBuiltInSMIDNV:
+ case SpvBuiltInWarpIDNV:
+ case SpvBuiltInSubgroupSize:
+ case SpvBuiltInSubgroupLocalInvocationId:
+ case SpvBuiltInSubgroupEqMask:
+ case SpvBuiltInSubgroupGeMask:
+ case SpvBuiltInSubgroupGtMask:
+ case SpvBuiltInSubgroupLeMask:
+ case SpvBuiltInSubgroupLtMask:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool HasBuiltinForRayTracingVolatileSemantics(
+ analysis::DecorationManager* decoration_manager, uint32_t var_id) {
+ return decoration_manager->FindDecoration(
+ var_id, SpvDecorationBuiltIn, [](const Instruction& inst) {
+ uint32_t built_in =
+ inst.GetSingleWordInOperand(kOpDecorateInOperandBuiltinDecoration);
+ return IsBuiltInForRayTracingVolatileSemantics(built_in);
+ });
+}
+
+bool HasVolatileDecoration(analysis::DecorationManager* decoration_manager,
+ uint32_t var_id) {
+ 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;
+ }
+
+ const bool is_vk_memory_model_enabled =
+ context()->get_feature_mgr()->HasCapability(
+ SpvCapabilityVulkanMemoryModel);
+ CollectTargetsForVolatileSemantics(is_vk_memory_model_enabled);
+
+ // If VulkanMemoryModel capability is not enabled, we have to set Volatile
+ // decoration for interface variables instead of setting Volatile for load
+ // instructions. If an interface (or pointers to it) is used by two load
+ // instructions in two entry points and one must be volatile while another
+ // is not, we have to report an error for the conflict.
+ if (!is_vk_memory_model_enabled &&
+ HasInterfaceInConflictOfVolatileSemantics()) {
+ return Status::Failure;
+ }
+
+ return SpreadVolatileSemanticsToVariables(is_vk_memory_model_enabled);
+}
+
+Pass::Status SpreadVolatileSemantics::SpreadVolatileSemanticsToVariables(
+ const bool is_vk_memory_model_enabled) {
+ Status status = Status::SuccessWithoutChange;
+ for (Instruction& var : context()->types_values()) {
+ auto entry_function_ids =
+ EntryFunctionsToSpreadVolatileSemanticsForVar(var.result_id());
+ if (entry_function_ids.empty()) {
+ continue;
+ }
+
+ if (is_vk_memory_model_enabled) {
+ SetVolatileForLoadsInEntries(&var, entry_function_ids);
+ } else {
+ DecorateVarWithVolatile(&var);
+ }
+ status = Status::SuccessWithChange;
+ }
+ return status;
+}
+
+bool SpreadVolatileSemantics::IsTargetUsedByNonVolatileLoadInEntryPoint(
+ uint32_t var_id, Instruction* entry_point) {
+ uint32_t entry_function_id =
+ entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint);
+ return !VisitLoadsOfPointersToVariableInEntries(
+ var_id,
+ [](Instruction* load) {
+ // If it has a load without volatile memory operand, finish traversal
+ // and return false.
+ if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) {
+ return false;
+ }
+ uint32_t memory_operands =
+ load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands);
+ return (memory_operands & SpvMemoryAccessVolatileMask) != 0;
+ },
+ {entry_function_id});
+}
+
+bool SpreadVolatileSemantics::HasInterfaceInConflictOfVolatileSemantics() {
+ for (Instruction& entry_point : get_module()->entry_points()) {
+ SpvExecutionModel execution_model =
+ static_cast<SpvExecutionModel>(entry_point.GetSingleWordInOperand(0));
+ for (uint32_t operand_index = kOpEntryPointInOperandInterface;
+ operand_index < entry_point.NumInOperands(); ++operand_index) {
+ uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index);
+ if (!EntryFunctionsToSpreadVolatileSemanticsForVar(var_id).empty() &&
+ !IsTargetForVolatileSemantics(var_id, execution_model) &&
+ IsTargetUsedByNonVolatileLoadInEntryPoint(var_id, &entry_point)) {
+ Instruction* inst = context()->get_def_use_mgr()->GetDef(var_id);
+ context()->EmitErrorMessage(
+ "Variable is a target for Volatile semantics for an entry point, "
+ "but it is not for another entry point",
+ inst);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void SpreadVolatileSemantics::MarkVolatileSemanticsForVariable(
+ uint32_t var_id, Instruction* entry_point) {
+ uint32_t entry_function_id =
+ entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint);
+ auto itr = var_ids_to_entry_fn_for_volatile_semantics_.find(var_id);
+ if (itr == var_ids_to_entry_fn_for_volatile_semantics_.end()) {
+ var_ids_to_entry_fn_for_volatile_semantics_[var_id] = {entry_function_id};
+ return;
+ }
+ itr->second.insert(entry_function_id);
+}
+
+void SpreadVolatileSemantics::CollectTargetsForVolatileSemantics(
+ const bool is_vk_memory_model_enabled) {
+ for (Instruction& entry_point : get_module()->entry_points()) {
+ SpvExecutionModel execution_model =
+ static_cast<SpvExecutionModel>(entry_point.GetSingleWordInOperand(0));
+ for (uint32_t operand_index = kOpEntryPointInOperandInterface;
+ operand_index < entry_point.NumInOperands(); ++operand_index) {
+ uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index);
+ if (!IsTargetForVolatileSemantics(var_id, execution_model)) {
+ continue;
+ }
+ if (is_vk_memory_model_enabled ||
+ IsTargetUsedByNonVolatileLoadInEntryPoint(var_id, &entry_point)) {
+ MarkVolatileSemanticsForVariable(var_id, &entry_point);
+ }
+ }
+ }
+}
+
+void SpreadVolatileSemantics::DecorateVarWithVolatile(Instruction* var) {
+ analysis::DecorationManager* decoration_manager =
+ context()->get_decoration_mgr();
+ uint32_t var_id = var->result_id();
+ if (HasVolatileDecoration(decoration_manager, var_id)) {
+ return;
+ }
+ get_decoration_mgr()->AddDecoration(
+ SpvOpDecorate, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {var_id}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
+ {SpvDecorationVolatile}}});
+}
+
+bool SpreadVolatileSemantics::VisitLoadsOfPointersToVariableInEntries(
+ uint32_t var_id, const std::function<bool(Instruction*)>& handle_load,
+ const std::unordered_set<uint32_t>& entry_function_ids) {
+ std::vector<uint32_t> worklist({var_id});
+ auto* def_use_mgr = context()->get_def_use_mgr();
+ while (!worklist.empty()) {
+ uint32_t ptr_id = worklist.back();
+ worklist.pop_back();
+ bool finish_traversal = !def_use_mgr->WhileEachUser(
+ ptr_id, [this, &worklist, &ptr_id, handle_load,
+ &entry_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()) {
+ return true;
+ }
+
+ if (user->opcode() == SpvOpAccessChain ||
+ user->opcode() == SpvOpInBoundsAccessChain ||
+ user->opcode() == SpvOpPtrAccessChain ||
+ user->opcode() == SpvOpInBoundsPtrAccessChain ||
+ user->opcode() == SpvOpCopyObject) {
+ if (ptr_id == user->GetSingleWordInOperand(0))
+ worklist.push_back(user->result_id());
+ return true;
+ }
+
+ if (user->opcode() != SpvOpLoad) {
+ return true;
+ }
+
+ return handle_load(user);
+ });
+ if (finish_traversal) return false;
+ }
+ return true;
+}
+
+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}});
+ return true;
+ }
+ uint32_t memory_operands =
+ load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands);
+ memory_operands |= SpvMemoryAccessVolatileMask;
+ load->SetInOperand(kOpLoadInOperandMemoryOperands, {memory_operands});
+ return true;
+ },
+ entry_function_ids);
+}
+
+bool SpreadVolatileSemantics::IsTargetForVolatileSemantics(
+ uint32_t var_id, SpvExecutionModel execution_model) {
+ analysis::DecorationManager* decoration_manager =
+ context()->get_decoration_mgr();
+ if (execution_model == SpvExecutionModelFragment) {
+ return get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 6) &&
+ HasBuiltinDecoration(decoration_manager, var_id,
+ SpvBuiltInHelperInvocation);
+ }
+
+ if (execution_model == SpvExecutionModelIntersectionKHR ||
+ execution_model == SpvExecutionModelIntersectionNV) {
+ if (HasBuiltinDecoration(decoration_manager, var_id,
+ SpvBuiltInRayTmaxKHR)) {
+ return true;
+ }
+ }
+
+ switch (execution_model) {
+ case SpvExecutionModelRayGenerationKHR:
+ case SpvExecutionModelClosestHitKHR:
+ case SpvExecutionModelMissKHR:
+ case SpvExecutionModelCallableKHR:
+ case SpvExecutionModelIntersectionKHR:
+ return HasBuiltinForRayTracingVolatileSemantics(decoration_manager,
+ var_id);
+ default:
+ return false;
+ }
+}
+
+} // namespace opt
+} // namespace spvtools
diff --git a/source/opt/spread_volatile_semantics.h b/source/opt/spread_volatile_semantics.h
new file mode 100644
index 00000000..3d0a1839
--- /dev/null
+++ b/source/opt/spread_volatile_semantics.h
@@ -0,0 +1,110 @@
+// 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_SPREAD_VOLATILE_SEMANTICS_H_
+#define SOURCE_OPT_SPREAD_VOLATILE_SEMANTICS_H_
+
+#include "source/opt/pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// See optimizer.hpp for documentation.
+class SpreadVolatileSemantics : public Pass {
+ public:
+ SpreadVolatileSemantics() {}
+
+ const char* name() const override { return "spread-volatile-semantics"; }
+
+ Status Process() override;
+
+ IRContext::Analysis GetPreservedAnalyses() override {
+ return IRContext::kAnalysisDefUse | IRContext::kAnalysisDecorations |
+ IRContext::kAnalysisInstrToBlockMapping;
+ }
+
+ private:
+ // Iterates interface variables and spreads the Volatile semantics if it has
+ // load instructions for the Volatile semantics.
+ Pass::Status SpreadVolatileSemanticsToVariables(
+ const bool is_vk_memory_model_enabled);
+
+ // Returns whether |var_id| is the result id of a target builtin variable for
+ // the volatile semantics for |execution_model| based on the Vulkan spec
+ // VUID-StandaloneSpirv-VulkanMemoryModel-04678 or
+ // VUID-StandaloneSpirv-VulkanMemoryModel-04679.
+ bool IsTargetForVolatileSemantics(uint32_t var_id,
+ SpvExecutionModel execution_model);
+
+ // Collects interface variables that need the volatile semantics.
+ // |is_vk_memory_model_enabled| is true if VulkanMemoryModel capability is
+ // enabled.
+ void CollectTargetsForVolatileSemantics(
+ const bool is_vk_memory_model_enabled);
+
+ // Reports an error if an interface variable is used by two entry points and
+ // it needs the Volatile decoration for one but not for another. Returns true
+ // if the error must be reported.
+ bool HasInterfaceInConflictOfVolatileSemantics();
+
+ // Returns whether the variable whose result is |var_id| is used by a
+ // non-volatile load or a pointer to it is used by a non-volatile load in
+ // |entry_point| or not.
+ bool IsTargetUsedByNonVolatileLoadInEntryPoint(uint32_t var_id,
+ 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.
+ bool VisitLoadsOfPointersToVariableInEntries(
+ uint32_t var_id, const std::function<bool(Instruction*)>& handle_load,
+ const std::unordered_set<uint32_t>& entry_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
+ // included in |entry_function_ids|.
+ void SetVolatileForLoadsInEntries(
+ Instruction* var, const std::unordered_set<uint32_t>& entry_function_ids);
+
+ // Adds OpDecorate Volatile for |var| if it does not exist.
+ void DecorateVarWithVolatile(Instruction* var);
+
+ // Returns a set of entry function ids to spread the volatile semantics for
+ // the variable with the result id |var_id|.
+ std::unordered_set<uint32_t> EntryFunctionsToSpreadVolatileSemanticsForVar(
+ uint32_t var_id) {
+ auto itr = var_ids_to_entry_fn_for_volatile_semantics_.find(var_id);
+ if (itr == var_ids_to_entry_fn_for_volatile_semantics_.end()) return {};
+ return itr->second;
+ }
+
+ // Specifies that we have to spread the volatile semantics for the
+ // variable with the result id |var_id| for the entry point |entry_point|.
+ void MarkVolatileSemanticsForVariable(uint32_t var_id,
+ Instruction* entry_point);
+
+ // Result ids of variables to entry function ids for the volatile semantics
+ // spread.
+ std::unordered_map<uint32_t, std::unordered_set<uint32_t>>
+ var_ids_to_entry_fn_for_volatile_semantics_;
+};
+
+} // namespace opt
+} // namespace spvtools
+
+#endif // SOURCE_OPT_SPREAD_VOLATILE_SEMANTICS_H_
diff --git a/source/opt/ssa_rewrite_pass.cpp b/source/opt/ssa_rewrite_pass.cpp
index 81770d77..29ab6123 100644
--- a/source/opt/ssa_rewrite_pass.cpp
+++ b/source/opt/ssa_rewrite_pass.cpp
@@ -753,6 +753,9 @@ Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) {
Pass::Status SSARewritePass::Process() {
Status status = Status::SuccessWithoutChange;
for (auto& fn : *get_module()) {
+ if (fn.IsDeclaration()) {
+ continue;
+ }
status =
CombineStatus(status, SSARewriter(this).RewriteFunctionIntoSSA(&fn));
// Kill DebugDeclares for target variables.
diff --git a/source/opt/strength_reduction_pass.h b/source/opt/strength_reduction_pass.h
index 8dfeb307..1cbbbcc6 100644
--- a/source/opt/strength_reduction_pass.h
+++ b/source/opt/strength_reduction_pass.h
@@ -34,7 +34,7 @@ class StrengthReductionPass : public Pass {
// Returns true if something changed.
bool ReplaceMultiplyByPowerOf2(BasicBlock::iterator*);
- // Scan the types and constants in the module looking for the the integer
+ // Scan the types and constants in the module looking for the integer
// types that we are
// interested in. The shift operation needs a small unsigned integer. We
// need to find
diff --git a/source/opt/strip_debug_info_pass.cpp b/source/opt/strip_debug_info_pass.cpp
index c86ce578..6a0ebf24 100644
--- a/source/opt/strip_debug_info_pass.cpp
+++ b/source/opt/strip_debug_info_pass.cpp
@@ -14,6 +14,7 @@
#include "source/opt/strip_debug_info_pass.h"
#include "source/opt/ir_context.h"
+#include "source/util/string_utils.h"
namespace spvtools {
namespace opt {
@@ -21,9 +22,8 @@ namespace opt {
Pass::Status StripDebugInfoPass::Process() {
bool uses_non_semantic_info = false;
for (auto& inst : context()->module()->extensions()) {
- const char* ext_name =
- reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]);
- if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) {
+ const std::string ext_name = inst.GetInOperand(0).AsString();
+ if (ext_name == "SPV_KHR_non_semantic_info") {
uses_non_semantic_info = true;
}
}
@@ -46,9 +46,10 @@ Pass::Status StripDebugInfoPass::Process() {
if (use->opcode() == SpvOpExtInst) {
auto ext_inst_set =
def_use->GetDef(use->GetSingleWordInOperand(0u));
- const char* extension_name = reinterpret_cast<const char*>(
- &ext_inst_set->GetInOperand(0).words[0]);
- if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) {
+ const std::string extension_name =
+ ext_inst_set->GetInOperand(0).AsString();
+ if (spvtools::utils::starts_with(extension_name,
+ "NonSemantic.")) {
// found a non-semantic use, return false as we cannot
// remove this OpString
return false;
diff --git a/source/opt/strip_reflect_info_pass.cpp b/source/opt/strip_nonsemantic_info_pass.cpp
index 8b0f2db7..cd1fbb63 100644
--- a/source/opt/strip_reflect_info_pass.cpp
+++ b/source/opt/strip_nonsemantic_info_pass.cpp
@@ -12,18 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "source/opt/strip_reflect_info_pass.h"
+#include "source/opt/strip_nonsemantic_info_pass.h"
#include <cstring>
#include <vector>
#include "source/opt/instruction.h"
#include "source/opt/ir_context.h"
+#include "source/util/string_utils.h"
namespace spvtools {
namespace opt {
-Pass::Status StripReflectInfoPass::Process() {
+Pass::Status StripNonSemanticInfoPass::Process() {
bool modified = false;
std::vector<Instruction*> to_remove;
@@ -32,7 +33,8 @@ Pass::Status StripReflectInfoPass::Process() {
for (auto& inst : context()->module()->annotations()) {
switch (inst.opcode()) {
case SpvOpDecorateStringGOOGLE:
- if (inst.GetSingleWordInOperand(1) == SpvDecorationHlslSemanticGOOGLE) {
+ if (inst.GetSingleWordInOperand(1) == SpvDecorationHlslSemanticGOOGLE ||
+ inst.GetSingleWordInOperand(1) == SpvDecorationUserTypeGOOGLE) {
to_remove.push_back(&inst);
} else {
other_uses_for_decorate_string = true;
@@ -40,7 +42,8 @@ Pass::Status StripReflectInfoPass::Process() {
break;
case SpvOpMemberDecorateStringGOOGLE:
- if (inst.GetSingleWordInOperand(2) == SpvDecorationHlslSemanticGOOGLE) {
+ if (inst.GetSingleWordInOperand(2) == SpvDecorationHlslSemanticGOOGLE ||
+ inst.GetSingleWordInOperand(2) == SpvDecorationUserTypeGOOGLE) {
to_remove.push_back(&inst);
} else {
other_uses_for_decorate_string = true;
@@ -60,33 +63,26 @@ Pass::Status StripReflectInfoPass::Process() {
}
for (auto& inst : context()->module()->extensions()) {
- const char* ext_name =
- reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]);
- if (0 == std::strcmp(ext_name, "SPV_GOOGLE_hlsl_functionality1")) {
+ const std::string ext_name = inst.GetInOperand(0).AsString();
+ if (ext_name == "SPV_GOOGLE_hlsl_functionality1") {
+ to_remove.push_back(&inst);
+ } else if (ext_name == "SPV_GOOGLE_user_type") {
to_remove.push_back(&inst);
} else if (!other_uses_for_decorate_string &&
- 0 == std::strcmp(ext_name, "SPV_GOOGLE_decorate_string")) {
+ ext_name == "SPV_GOOGLE_decorate_string") {
to_remove.push_back(&inst);
- } else if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) {
+ } else if (ext_name == "SPV_KHR_non_semantic_info") {
to_remove.push_back(&inst);
}
}
- // clear all debug data now if it hasn't been cleared already, to remove any
- // remaining OpString that may have been referenced by non-semantic extinsts
- for (auto& dbg : context()->debugs1()) to_remove.push_back(&dbg);
- for (auto& dbg : context()->debugs2()) to_remove.push_back(&dbg);
- for (auto& dbg : context()->debugs3()) to_remove.push_back(&dbg);
- for (auto& dbg : context()->ext_inst_debuginfo()) to_remove.push_back(&dbg);
-
// remove any extended inst imports that are non semantic
std::unordered_set<uint32_t> non_semantic_sets;
for (auto& inst : context()->module()->ext_inst_imports()) {
assert(inst.opcode() == SpvOpExtInstImport &&
"Expecting an import of an extension's instruction set.");
- const char* extension_name =
- reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]);
- if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) {
+ const std::string extension_name = inst.GetInOperand(0).AsString();
+ if (spvtools::utils::starts_with(extension_name, "NonSemantic.")) {
non_semantic_sets.insert(inst.result_id());
to_remove.push_back(&inst);
}
@@ -103,19 +99,10 @@ Pass::Status StripReflectInfoPass::Process() {
to_remove.push_back(inst);
}
}
- });
+ },
+ true);
}
- // OpName must come first, since they may refer to other debug instructions.
- // If they are after the instructions that refer to, then they will be killed
- // when that instruction is killed, which will lead to a double kill.
- std::sort(to_remove.begin(), to_remove.end(),
- [](Instruction* lhs, Instruction* rhs) -> bool {
- if (lhs->opcode() == SpvOpName && rhs->opcode() != SpvOpName)
- return true;
- return false;
- });
-
for (auto* inst : to_remove) {
modified = true;
context()->KillInst(inst);
diff --git a/source/opt/strip_reflect_info_pass.h b/source/opt/strip_nonsemantic_info_pass.h
index 4e1999ed..ff4e2e1d 100644
--- a/source/opt/strip_reflect_info_pass.h
+++ b/source/opt/strip_nonsemantic_info_pass.h
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef SOURCE_OPT_STRIP_REFLECT_INFO_PASS_H_
-#define SOURCE_OPT_STRIP_REFLECT_INFO_PASS_H_
+#ifndef SOURCE_OPT_STRIP_NONSEMANTIC_INFO_PASS_H_
+#define SOURCE_OPT_STRIP_NONSEMANTIC_INFO_PASS_H_
#include "source/opt/ir_context.h"
#include "source/opt/module.h"
@@ -23,9 +23,9 @@ namespace spvtools {
namespace opt {
// See optimizer.hpp for documentation.
-class StripReflectInfoPass : public Pass {
+class StripNonSemanticInfoPass : public Pass {
public:
- const char* name() const override { return "strip-reflect"; }
+ const char* name() const override { return "strip-nonsemantic"; }
Status Process() override;
// Return the mask of preserved Analyses.
@@ -41,4 +41,4 @@ class StripReflectInfoPass : public Pass {
} // namespace opt
} // namespace spvtools
-#endif // SOURCE_OPT_STRIP_REFLECT_INFO_PASS_H_
+#endif // SOURCE_OPT_STRIP_NONSEMANTIC_INFO_PASS_H_
diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp
index 7935ad33..6da4b57b 100644
--- a/source/opt/type_manager.cpp
+++ b/source/opt/type_manager.cpp
@@ -23,6 +23,7 @@
#include "source/opt/log.h"
#include "source/opt/reflect.h"
#include "source/util/make_unique.h"
+#include "source/util/string_utils.h"
namespace spvtools {
namespace opt {
@@ -349,11 +350,8 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
}
case Type::kOpaque: {
const Opaque* opaque = type->AsOpaque();
- size_t size = opaque->name().size();
// Convert to null-terminated packed UTF-8 string.
- std::vector<uint32_t> words(size / 4 + 1, 0);
- char* dst = reinterpret_cast<char*>(words.data());
- strncpy(dst, opaque->name().c_str(), size);
+ std::vector<uint32_t> words = spvtools::utils::MakeVector(opaque->name());
typeInst = MakeUnique<Instruction>(
context(), SpvOpTypeOpaque, 0, id,
std::initializer_list<Operand>{
@@ -781,8 +779,7 @@ Type* TypeManager::RecordIfTypeDefinition(const Instruction& inst) {
}
} break;
case SpvOpTypeOpaque: {
- const uint32_t* data = inst.GetInOperand(0).words.data();
- type = new Opaque(reinterpret_cast<const char*>(data));
+ type = new Opaque(inst.GetInOperand(0).AsString());
} break;
case SpvOpTypePointer: {
uint32_t pointee_type_id = inst.GetSingleWordInOperand(1);
diff --git a/source/opt/type_manager.h b/source/opt/type_manager.h
index ce9d83d4..72e37f48 100644
--- a/source/opt/type_manager.h
+++ b/source/opt/type_manager.h
@@ -160,6 +160,13 @@ class TypeManager {
uint32_t GetFloatTypeId() { return GetTypeInstruction(GetFloatType()); }
+ Type* GetDoubleType() {
+ Float float_type(64);
+ return GetRegisteredType(&float_type);
+ }
+
+ uint32_t GetDoubleTypeId() { return GetTypeInstruction(GetDoubleType()); }
+
Type* GetUIntVectorType(uint32_t size) {
Vector vec_type(GetUIntType(), size);
return GetRegisteredType(&vec_type);
diff --git a/source/opt/types.cpp b/source/opt/types.cpp
index b1eb3a50..ea4baadb 100644
--- a/source/opt/types.cpp
+++ b/source/opt/types.cpp
@@ -425,7 +425,6 @@ std::string Array::str() const {
void Array::GetExtraHashWords(std::vector<uint32_t>* words,
std::unordered_set<const Type*>* seen) const {
element_type_->GetHashWords(words, seen);
- // This should mirror the logic in IsSameImpl
words->insert(words->end(), length_info_.words.begin(),
length_info_.words.end());
}
diff --git a/source/opt/unify_const_pass.cpp b/source/opt/unify_const_pass.cpp
index 227fd61d..6bfa11a5 100644
--- a/source/opt/unify_const_pass.cpp
+++ b/source/opt/unify_const_pass.cpp
@@ -151,7 +151,7 @@ Pass::Status UnifyConstantPass::Process() {
// 'SpecId' decoration and all of them should be treated as unique.
// 'SpecId' is not applicable to SpecConstants defined with
// OpSpecConstant{Op|Composite}, their values are not necessary to be
- // unique. When all the operands/compoents are the same between two
+ // unique. When all the operands/components are the same between two
// OpSpecConstant{Op|Composite} results, their result values must be the
// same so are unifiable.
case SpvOp::SpvOpSpecConstantOp:
diff --git a/source/opt/upgrade_memory_model.cpp b/source/opt/upgrade_memory_model.cpp
index ab252059..9d6a5bce 100644
--- a/source/opt/upgrade_memory_model.cpp
+++ b/source/opt/upgrade_memory_model.cpp
@@ -20,6 +20,7 @@
#include "source/opt/ir_context.h"
#include "source/spirv_constant.h"
#include "source/util/make_unique.h"
+#include "source/util/string_utils.h"
namespace spvtools {
namespace opt {
@@ -58,9 +59,7 @@ void UpgradeMemoryModel::UpgradeMemoryModelInstruction() {
std::initializer_list<Operand>{
{SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityVulkanMemoryModelKHR}}}));
const std::string extension = "SPV_KHR_vulkan_memory_model";
- std::vector<uint32_t> words(extension.size() / 4 + 1, 0);
- char* dst = reinterpret_cast<char*>(words.data());
- strncpy(dst, extension.c_str(), extension.size());
+ std::vector<uint32_t> words = spvtools::utils::MakeVector(extension);
context()->AddExtension(
MakeUnique<Instruction>(context(), SpvOpExtension, 0, 0,
std::initializer_list<Operand>{
@@ -85,8 +84,7 @@ void UpgradeMemoryModel::UpgradeInstructions() {
if (ext_inst == GLSLstd450Modf || ext_inst == GLSLstd450Frexp) {
auto import =
get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u));
- if (reinterpret_cast<char*>(import->GetInOperand(0u).words.data()) ==
- std::string("GLSL.std.450")) {
+ if (import->GetInOperand(0u).AsString() == "GLSL.std.450") {
UpgradeExtInst(inst);
}
}
diff --git a/source/opt/value_number_table.cpp b/source/opt/value_number_table.cpp
index 32d6de94..5271e3fe 100644
--- a/source/opt/value_number_table.cpp
+++ b/source/opt/value_number_table.cpp
@@ -50,7 +50,7 @@ uint32_t ValueNumberTable::AssignValueNumber(Instruction* inst) {
// OpSampledImage and OpImage must remain in the same basic block in which
// they are used, because of this we will assign each one it own value number.
if (!context()->IsCombinatorInstruction(inst) &&
- !inst->IsOpenCL100DebugInstr()) {
+ !inst->IsCommonDebugInstr()) {
value = TakeNextValueNumber();
id_to_value_[inst->result_id()] = value;
return value;
diff --git a/source/opt/vector_dce.cpp b/source/opt/vector_dce.cpp
index 14a55c1e..28d94a07 100644
--- a/source/opt/vector_dce.cpp
+++ b/source/opt/vector_dce.cpp
@@ -52,7 +52,7 @@ void VectorDCE::FindLiveComponents(Function* function,
// components are live because of arbitrary nesting of structs.
function->ForEachInst(
[&work_list, this, live_components](Instruction* current_inst) {
- if (current_inst->IsOpenCL100DebugInstr()) {
+ if (current_inst->IsCommonDebugInstr()) {
return;
}
if (!HasVectorOrScalarResult(current_inst) ||
@@ -110,7 +110,11 @@ void VectorDCE::MarkExtractUseAsLive(const Instruction* current_inst,
if (current_inst->NumInOperands() < 2) {
new_item.components = live_elements;
} else {
- new_item.components.Set(current_inst->GetSingleWordInOperand(1));
+ uint32_t element_index = current_inst->GetSingleWordInOperand(1);
+ uint32_t item_size = GetVectorComponentCount(operand_inst->type_id());
+ if (element_index < item_size) {
+ new_item.components.Set(element_index);
+ }
}
AddItemToWorkListIfNeeded(new_item, live_components, work_list);
}
@@ -176,10 +180,10 @@ void VectorDCE::MarkVectorShuffleUsesAsLive(
second_operand.instruction =
def_use_mgr->GetDef(current_item.instruction->GetSingleWordInOperand(1));
- analysis::TypeManager* type_mgr = context()->get_type_mgr();
- analysis::Vector* first_type =
- type_mgr->GetType(first_operand.instruction->type_id())->AsVector();
- uint32_t size_of_first_operand = first_type->element_count();
+ uint32_t size_of_first_operand =
+ GetVectorComponentCount(first_operand.instruction->type_id());
+ uint32_t size_of_second_operand =
+ GetVectorComponentCount(second_operand.instruction->type_id());
for (uint32_t in_op = 2; in_op < current_item.instruction->NumInOperands();
++in_op) {
@@ -187,7 +191,7 @@ void VectorDCE::MarkVectorShuffleUsesAsLive(
if (current_item.components.Get(in_op - 2)) {
if (index < size_of_first_operand) {
first_operand.components.Set(index);
- } else {
+ } else if (index - size_of_first_operand < size_of_second_operand) {
second_operand.components.Set(index - size_of_first_operand);
}
}
@@ -202,7 +206,6 @@ void VectorDCE::MarkCompositeContructUsesAsLive(
VectorDCE::LiveComponentMap* live_components,
std::vector<VectorDCE::WorkListItem>* work_list) {
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
- analysis::TypeManager* type_mgr = context()->get_type_mgr();
uint32_t current_component = 0;
Instruction* current_inst = work_item.instruction;
@@ -223,8 +226,7 @@ void VectorDCE::MarkCompositeContructUsesAsLive(
assert(HasVectorResult(op_inst));
WorkListItem new_work_item;
new_work_item.instruction = op_inst;
- uint32_t op_vector_size =
- type_mgr->GetType(op_inst->type_id())->AsVector()->element_count();
+ uint32_t op_vector_size = GetVectorComponentCount(op_inst->type_id());
for (uint32_t op_vector_idx = 0; op_vector_idx < op_vector_size;
op_vector_idx++, current_component++) {
@@ -297,6 +299,18 @@ bool VectorDCE::HasScalarResult(const Instruction* inst) const {
}
}
+uint32_t VectorDCE::GetVectorComponentCount(uint32_t type_id) {
+ assert(type_id != 0 &&
+ "Trying to get the vector element count, but the type id is 0");
+ analysis::TypeManager* type_mgr = context()->get_type_mgr();
+ const analysis::Type* type = type_mgr->GetType(type_id);
+ const analysis::Vector* vector_type = type->AsVector();
+ assert(
+ vector_type &&
+ "Trying to get the vector element count, but the type is not a vector");
+ return vector_type->element_count();
+}
+
bool VectorDCE::RewriteInstructions(
Function* function, const VectorDCE::LiveComponentMap& live_components) {
bool modified = false;
@@ -394,7 +408,7 @@ void VectorDCE::MarkDebugValueUsesAsDead(
Instruction* composite, std::vector<Instruction*>* dead_dbg_value) {
context()->get_def_use_mgr()->ForEachUser(
composite, [&dead_dbg_value](Instruction* use) {
- if (use->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue)
+ if (use->GetCommonDebugOpcode() == CommonDebugInfoDebugValue)
dead_dbg_value->push_back(use);
});
}
diff --git a/source/opt/vector_dce.h b/source/opt/vector_dce.h
index 0df9aee1..a55bda69 100644
--- a/source/opt/vector_dce.h
+++ b/source/opt/vector_dce.h
@@ -73,7 +73,7 @@ class VectorDCE : public MemPass {
bool RewriteInstructions(Function* function,
const LiveComponentMap& live_components);
- // Makrs all DebugValue instructions that use |composite| for their values as
+ // Makes all DebugValue instructions that use |composite| for their values as
// dead instructions by putting them into |dead_dbg_value|.
void MarkDebugValueUsesAsDead(Instruction* composite,
std::vector<Instruction*>* dead_dbg_value);
@@ -94,12 +94,15 @@ class VectorDCE : public MemPass {
// Returns true if the result of |inst| is a vector or a scalar.
bool HasVectorOrScalarResult(const Instruction* inst) const;
- // Returns true if the result of |inst| is a scalar.
+ // Returns true if the result of |inst| is a vector.
bool HasVectorResult(const Instruction* inst) const;
- // Returns true if the result of |inst| is a vector.
+ // Returns true if the result of |inst| is a scalar.
bool HasScalarResult(const Instruction* inst) const;
+ // Returns the number of elements in the vector type with id |type_id|.
+ uint32_t GetVectorComponentCount(uint32_t type_id);
+
// Adds |work_item| to |work_list| if it is not already live according to
// |live_components|. |live_components| is updated to indicate that
// |work_item| is now live.
diff --git a/source/print.cpp b/source/print.cpp
index 128587ae..2418c5bc 100644
--- a/source/print.cpp
+++ b/source/print.cpp
@@ -15,7 +15,7 @@
#include "source/print.h"
#if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \
- defined(SPIRV_IOS) || defined(SPIRV_FREEBSD) || \
+ defined(SPIRV_IOS) || defined(SPIRV_TVOS) || defined(SPIRV_FREEBSD) || \
defined(SPIRV_EMSCRIPTEN) || defined(SPIRV_FUCHSIA)
namespace spvtools {
diff --git a/source/reduce/CMakeLists.txt b/source/reduce/CMakeLists.txt
index a3291c77..6fd8409f 100644
--- a/source/reduce/CMakeLists.txt
+++ b/source/reduce/CMakeLists.txt
@@ -14,6 +14,8 @@
set(SPIRV_TOOLS_REDUCE_SOURCES
change_operand_reduction_opportunity.h
change_operand_to_undef_reduction_opportunity.h
+ conditional_branch_to_simple_conditional_branch_opportunity_finder.h
+ conditional_branch_to_simple_conditional_branch_reduction_opportunity.h
merge_blocks_reduction_opportunity.h
merge_blocks_reduction_opportunity_finder.h
operand_to_const_reduction_opportunity_finder.h
@@ -34,15 +36,17 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
remove_struct_member_reduction_opportunity.h
remove_unused_instruction_reduction_opportunity_finder.h
remove_unused_struct_member_reduction_opportunity_finder.h
- structured_loop_to_selection_reduction_opportunity.h
- structured_loop_to_selection_reduction_opportunity_finder.h
- conditional_branch_to_simple_conditional_branch_opportunity_finder.h
- conditional_branch_to_simple_conditional_branch_reduction_opportunity.h
simple_conditional_branch_to_branch_opportunity_finder.h
simple_conditional_branch_to_branch_reduction_opportunity.h
+ structured_construct_to_block_reduction_opportunity.h
+ structured_construct_to_block_reduction_opportunity_finder.h
+ structured_loop_to_selection_reduction_opportunity.h
+ structured_loop_to_selection_reduction_opportunity_finder.h
change_operand_reduction_opportunity.cpp
change_operand_to_undef_reduction_opportunity.cpp
+ conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp
+ conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp
merge_blocks_reduction_opportunity.cpp
merge_blocks_reduction_opportunity_finder.cpp
operand_to_const_reduction_opportunity_finder.cpp
@@ -63,12 +67,12 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
remove_struct_member_reduction_opportunity.cpp
remove_unused_instruction_reduction_opportunity_finder.cpp
remove_unused_struct_member_reduction_opportunity_finder.cpp
- structured_loop_to_selection_reduction_opportunity.cpp
- structured_loop_to_selection_reduction_opportunity_finder.cpp
- conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp
- conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp
simple_conditional_branch_to_branch_opportunity_finder.cpp
simple_conditional_branch_to_branch_reduction_opportunity.cpp
+ structured_construct_to_block_reduction_opportunity.cpp
+ structured_construct_to_block_reduction_opportunity_finder.cpp
+ structured_loop_to_selection_reduction_opportunity.cpp
+ structured_loop_to_selection_reduction_opportunity_finder.cpp
)
if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
diff --git a/source/reduce/reducer.cpp b/source/reduce/reducer.cpp
index 16bb94fe..b752f415 100644
--- a/source/reduce/reducer.cpp
+++ b/source/reduce/reducer.cpp
@@ -28,6 +28,7 @@
#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h"
#include "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h"
#include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
+#include "source/reduce/structured_construct_to_block_reduction_opportunity_finder.h"
#include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h"
#include "source/spirv_reducer_options.h"
@@ -113,6 +114,8 @@ void Reducer::AddDefaultReductionPasses() {
AddReductionPass(
spvtools::MakeUnique<OperandToDominatingIdReductionOpportunityFinder>());
AddReductionPass(spvtools::MakeUnique<
+ StructuredConstructToBlockReductionOpportunityFinder>());
+ AddReductionPass(spvtools::MakeUnique<
StructuredLoopToSelectionReductionOpportunityFinder>());
AddReductionPass(
spvtools::MakeUnique<MergeBlocksReductionOpportunityFinder>());
@@ -141,7 +144,7 @@ void Reducer::AddReductionPass(
std::unique_ptr<ReductionOpportunityFinder> finder) {
passes_.push_back(
spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder)));
-}
+}
void Reducer::AddCleanupReductionPass(
std::unique_ptr<ReductionOpportunityFinder> finder) {
diff --git a/source/reduce/remove_struct_member_reduction_opportunity.cpp b/source/reduce/remove_struct_member_reduction_opportunity.cpp
index da096e1e..e72ed351 100644
--- a/source/reduce/remove_struct_member_reduction_opportunity.cpp
+++ b/source/reduce/remove_struct_member_reduction_opportunity.cpp
@@ -153,7 +153,7 @@ void RemoveStructMemberReductionOpportunity::AdjustAccessedIndices(
next_type = type_inst->GetSingleWordInOperand(0);
break;
case SpvOpTypeStruct: {
- // Struct types are special becuase (a) we may need to adjust the index
+ // Struct types are special because (a) we may need to adjust the index
// being used, if the struct type is the one from which we are removing
// a member, and (b) the type encountered by following the current index
// is dependent on the value of the index.
diff --git a/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp b/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp
index e72be625..cd0c4e4d 100644
--- a/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp
+++ b/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp
@@ -136,9 +136,9 @@ RemoveUnusedStructMemberReductionOpportunityFinder::GetAvailableOpportunities(
}
}
- // We now know those struct indices that are unsed, and we make a reduction
+ // We now know those struct indices that are unused, and we make a reduction
// opportunity for each of them. By mapping each relevant member index to the
- // structs in which it is unsed, we will group all opportunities to remove
+ // structs in which it is unused, we will group all opportunities to remove
// member k of a struct (for some k) together. This reduces the likelihood
// that opportunities to remove members from the same struct will be adjacent,
// which is good because such opportunities mutually disable one another.
diff --git a/source/reduce/structured_construct_to_block_reduction_opportunity.cpp b/source/reduce/structured_construct_to_block_reduction_opportunity.cpp
new file mode 100644
index 00000000..ed738411
--- /dev/null
+++ b/source/reduce/structured_construct_to_block_reduction_opportunity.cpp
@@ -0,0 +1,67 @@
+// Copyright (c) 2021 Alastair F. Donaldson
+//
+// 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/reduce/structured_construct_to_block_reduction_opportunity.h"
+
+namespace spvtools {
+namespace reduce {
+
+bool StructuredConstructToBlockReductionOpportunity::PreconditionHolds() {
+ return context_->get_def_use_mgr()->GetDef(construct_header_) != nullptr;
+}
+
+void StructuredConstructToBlockReductionOpportunity::Apply() {
+ auto header_block = context_->cfg()->block(construct_header_);
+ auto merge_block = context_->cfg()->block(header_block->MergeBlockId());
+
+ auto* enclosing_function = header_block->GetParent();
+
+ // A region of blocks is defined in terms of dominators and post-dominators,
+ // so we compute these for the enclosing function.
+ auto* dominators = context_->GetDominatorAnalysis(enclosing_function);
+ auto* postdominators = context_->GetPostDominatorAnalysis(enclosing_function);
+
+ // For each block in the function, determine whether it is inside the region.
+ // If it is, delete it.
+ for (auto block_it = enclosing_function->begin();
+ block_it != enclosing_function->end();) {
+ if (header_block != &*block_it && merge_block != &*block_it &&
+ dominators->Dominates(header_block, &*block_it) &&
+ postdominators->Dominates(merge_block, &*block_it)) {
+ block_it = block_it.Erase();
+ } else {
+ ++block_it;
+ }
+ }
+ // Having removed some blocks from the module it is necessary to invalidate
+ // analyses, since the remaining patch-up work depends on various analyses
+ // which will otherwise reference blocks that have been deleted.
+ context_->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+
+ // We demote the header of the region to a regular block by deleting its merge
+ // instruction.
+ context_->KillInst(header_block->GetMergeInst());
+
+ // The terminator for the header block is changed to be an unconditional
+ // branch to the merge block.
+ header_block->terminator()->SetOpcode(SpvOpBranch);
+ header_block->terminator()->SetInOperands(
+ {{SPV_OPERAND_TYPE_ID, {merge_block->id()}}});
+
+ // This is an intrusive change, so we invalidate all analyses.
+ context_->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+} // namespace reduce
+} // namespace spvtools
diff --git a/source/reduce/structured_construct_to_block_reduction_opportunity.h b/source/reduce/structured_construct_to_block_reduction_opportunity.h
new file mode 100644
index 00000000..f461a2f0
--- /dev/null
+++ b/source/reduce/structured_construct_to_block_reduction_opportunity.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2021 Alastair F. Donaldson
+//
+// 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_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_H_
+#define SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_H_
+
+#include "source/opt/ir_context.h"
+#include "source/reduce/reduction_opportunity.h"
+
+namespace spvtools {
+namespace reduce {
+
+// An opportunity to replace a skeletal structured control flow construct with a
+// single block.
+class StructuredConstructToBlockReductionOpportunity
+ : public ReductionOpportunity {
+ public:
+ // Constructs an opportunity from a header block id.
+ StructuredConstructToBlockReductionOpportunity(opt::IRContext* context,
+ uint32_t construct_header)
+ : context_(context), construct_header_(construct_header) {}
+
+ // Returns true if and only if |construct_header_| exists in the module -
+ // another opportunity may have removed it.
+ bool PreconditionHolds() override;
+
+ protected:
+ void Apply() override;
+
+ private:
+ opt::IRContext* context_;
+ uint32_t construct_header_;
+};
+
+} // namespace reduce
+} // namespace spvtools
+
+#endif // SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_H_
diff --git a/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp b/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp
new file mode 100644
index 00000000..29fbe551
--- /dev/null
+++ b/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp
@@ -0,0 +1,185 @@
+// Copyright (c) 2021 Alastair F. Donaldson
+//
+// 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/reduce/structured_construct_to_block_reduction_opportunity_finder.h"
+
+#include <unordered_set>
+
+#include "source/reduce/structured_construct_to_block_reduction_opportunity.h"
+
+namespace spvtools {
+namespace reduce {
+
+std::vector<std::unique_ptr<ReductionOpportunity>>
+StructuredConstructToBlockReductionOpportunityFinder::GetAvailableOpportunities(
+ opt::IRContext* context, uint32_t target_function) const {
+ std::vector<std::unique_ptr<ReductionOpportunity>> result;
+
+ // Consider every function in the module.
+ for (auto* function : GetTargetFunctions(context, target_function)) {
+ // For every header block in the function, there is potentially a region of
+ // blocks that could be collapsed.
+ std::unordered_map<opt::BasicBlock*, std::unordered_set<opt::BasicBlock*>>
+ regions;
+
+ // Regions are identified using dominators and postdominators, so we compute
+ // those for the function.
+ auto* dominators = context->GetDominatorAnalysis(function);
+ auto* postdominators = context->GetPostDominatorAnalysis(function);
+
+ // Consider every block in the function.
+ for (auto& block : *function) {
+ // If a block has an unreachable predecessor then folding away a region in
+ // which that block is contained gets complicated, so we ignore regions
+ // that contain such blocks. We note whether this block suffers from this
+ // problem.
+ bool has_unreachable_predecessor =
+ HasUnreachablePredecessor(block, context);
+
+ // Look through all the regions we have identified so far to see whether
+ // this block is part of a region, or spoils a region (by having an
+ // unreachable predecessor).
+ for (auto entry = regions.begin(); entry != regions.end();) {
+ // |block| is in this region if it is dominated by the header,
+ // post-dominated by the merge, and different from the merge.
+ assert(&block != entry->first &&
+ "The block should not be the region's header because we only "
+ "make a region when we encounter its header.");
+ if (entry->first->MergeBlockId() != block.id() &&
+ dominators->Dominates(entry->first, &block) &&
+ postdominators->Dominates(
+ entry->first->GetMergeInst()->GetSingleWordInOperand(0),
+ block.id())) {
+ if (has_unreachable_predecessor) {
+ // The block would be in this region, but it has an unreachable
+ // predecessor. This spoils the region, so we remove it.
+ entry = regions.erase(entry);
+ continue;
+ } else {
+ // Add the block to the region.
+ entry->second.insert(&block);
+ }
+ }
+ ++entry;
+ }
+ if (block.MergeBlockIdIfAny() == 0) {
+ // The block isn't a header, so it doesn't constitute a new region.
+ continue;
+ }
+ if (!context->IsReachable(block)) {
+ // The block isn't reachable, so it doesn't constitute a new region.
+ continue;
+ }
+ auto* merge_block = context->cfg()->block(
+ block.GetMergeInst()->GetSingleWordInOperand(0));
+ if (!context->IsReachable(*merge_block)) {
+ // The block's merge is unreachable, so it doesn't constitute a new
+ // region.
+ continue;
+ }
+ assert(dominators->Dominates(&block, merge_block) &&
+ "The merge block is reachable, so the header must dominate it");
+ if (!postdominators->Dominates(merge_block, &block)) {
+ // The block is not post-dominated by its merge. This happens for
+ // instance when there is a break from a conditional, or an early exit.
+ // This also means that we don't add a region.
+ continue;
+ }
+ // We have a reachable header block with a reachable merge that
+ // postdominates the header: this means we have a new region.
+ regions.emplace(&block, std::unordered_set<opt::BasicBlock*>());
+ }
+
+ // Now that we have found all the regions and blocks within them, we check
+ // whether any region defines an id that is used outside the region. If this
+ // is *not* the case, then we have an opportunity to collapse the region
+ // down to its header block and merge block.
+ for (auto& entry : regions) {
+ if (DefinitionsRestrictedToRegion(*entry.first, entry.second, context)) {
+ result.emplace_back(
+ MakeUnique<StructuredConstructToBlockReductionOpportunity>(
+ context, entry.first->id()));
+ }
+ }
+ }
+ return result;
+}
+
+bool StructuredConstructToBlockReductionOpportunityFinder::
+ DefinitionsRestrictedToRegion(
+ const opt::BasicBlock& header,
+ const std::unordered_set<opt::BasicBlock*>& region,
+ opt::IRContext* context) {
+ // Consider every block in the region.
+ for (auto& block : region) {
+ // Consider every instruction in the block - this includes the label
+ // instruction
+ if (!block->WhileEachInst(
+ [context, &header, &region](opt::Instruction* inst) -> bool {
+ if (inst->result_id() == 0) {
+ // The instruction does not generate a result id, thus it cannot
+ // be referred to outside the region - this is fine.
+ return true;
+ }
+ // Consider every use of the instruction's result id.
+ if (!context->get_def_use_mgr()->WhileEachUse(
+ inst->result_id(),
+ [context, &header, &region](opt::Instruction* user,
+ uint32_t) -> bool {
+ auto user_block = context->get_instr_block(user);
+ if (user == header.GetMergeInst() ||
+ user == header.terminator()) {
+ // We are going to delete the header's merge
+ // instruction and rewrite its terminator, so it does
+ // not matter if the user is one of these
+ // instructions.
+ return true;
+ }
+ if (user_block == nullptr ||
+ region.count(user_block) == 0) {
+ // The user is either a global instruction, or an
+ // instruction in a block outside the region. Removing
+ // the region would invalidate this user.
+ return false;
+ }
+ return true;
+ })) {
+ return false;
+ }
+ return true;
+ })) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool StructuredConstructToBlockReductionOpportunityFinder::
+ HasUnreachablePredecessor(const opt::BasicBlock& block,
+ opt::IRContext* context) {
+ for (auto pred : context->cfg()->preds(block.id())) {
+ if (!context->IsReachable(*context->cfg()->block(pred))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+std::string StructuredConstructToBlockReductionOpportunityFinder::GetName()
+ const {
+ return "StructuredConstructToBlockReductionOpportunityFinder";
+}
+
+} // namespace reduce
+} // namespace spvtools
diff --git a/source/reduce/structured_construct_to_block_reduction_opportunity_finder.h b/source/reduce/structured_construct_to_block_reduction_opportunity_finder.h
new file mode 100644
index 00000000..28bbc17c
--- /dev/null
+++ b/source/reduce/structured_construct_to_block_reduction_opportunity_finder.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2021 Alastair F. Donaldson
+//
+// 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_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H
+#define SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H
+
+#include "source/reduce/reduction_opportunity_finder.h"
+
+namespace spvtools {
+namespace reduce {
+
+// A finder for opportunities to replace a skeletal structured control flow
+// construct - that is, a construct that does not define anything that's used
+// outside the construct - into its header block.
+class StructuredConstructToBlockReductionOpportunityFinder
+ : public ReductionOpportunityFinder {
+ public:
+ StructuredConstructToBlockReductionOpportunityFinder() = default;
+
+ ~StructuredConstructToBlockReductionOpportunityFinder() override = default;
+
+ std::string GetName() const final;
+
+ std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
+ opt::IRContext* context, uint32_t target_function) const final;
+
+ private:
+ // Returns true if and only if all instructions defined in |region| are used
+ // only inside |region|, with the exception that they may be used by the merge
+ // or terminator instruction of |header|, which must be the header block for
+ // the region.
+ static bool DefinitionsRestrictedToRegion(
+ const opt::BasicBlock& header,
+ const std::unordered_set<opt::BasicBlock*>& region,
+ opt::IRContext* context);
+
+ // Returns true if and only if |block| has at least one predecessor that is
+ // unreachable in the control flow graph of its function.
+ static bool HasUnreachablePredecessor(const opt::BasicBlock& block,
+ opt::IRContext* context);
+};
+
+} // namespace reduce
+} // namespace spvtools
+
+#endif // SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H
diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp
index 0c004439..850af456 100644
--- a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp
+++ b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp
@@ -27,16 +27,14 @@ const uint32_t kMergeNodeIndex = 0;
bool StructuredLoopToSelectionReductionOpportunity::PreconditionHolds() {
// Is the loop header reachable?
- return loop_construct_header_->GetLabel()
- ->context()
- ->GetDominatorAnalysis(enclosing_function_)
- ->IsReachable(loop_construct_header_);
+ return loop_construct_header_->GetLabel()->context()->IsReachable(
+ *loop_construct_header_);
}
void StructuredLoopToSelectionReductionOpportunity::Apply() {
// Force computation of dominator analysis, CFG and structured CFG analysis
// before we start to mess with edges in the function.
- context_->GetDominatorAnalysis(enclosing_function_);
+ context_->GetDominatorAnalysis(loop_construct_header_->GetParent());
context_->cfg();
context_->GetStructuredCFGAnalysis();
@@ -78,8 +76,7 @@ void StructuredLoopToSelectionReductionOpportunity::RedirectToClosestMergeBlock(
}
already_seen.insert(pred);
- if (!context_->GetDominatorAnalysis(enclosing_function_)
- ->IsReachable(pred)) {
+ if (!context_->IsReachable(*context_->cfg()->block(pred))) {
// We do not care about unreachable predecessors (and dominance
// information, and thus the notion of structured control flow, makes
// little sense for unreachable blocks).
@@ -216,7 +213,7 @@ void StructuredLoopToSelectionReductionOpportunity::ChangeLoopToSelection() {
void StructuredLoopToSelectionReductionOpportunity::FixNonDominatedIdUses() {
// Consider each instruction in the function.
- for (auto& block : *enclosing_function_) {
+ for (auto& block : *loop_construct_header_->GetParent()) {
for (auto& def : block) {
if (def.opcode() == SpvOpVariable) {
// Variables are defined at the start of the function, and can be
@@ -243,7 +240,7 @@ void StructuredLoopToSelectionReductionOpportunity::FixNonDominatedIdUses() {
case SpvStorageClassFunction:
use->SetOperand(
index, {FindOrCreateFunctionVariable(
- context_, enclosing_function_,
+ context_, loop_construct_header_->GetParent(),
context_->get_type_mgr()->GetId(pointer_type))});
break;
default:
@@ -276,11 +273,11 @@ bool StructuredLoopToSelectionReductionOpportunity::
if (use->opcode() == SpvOpPhi) {
// A use in a phi doesn't need to be dominated by its definition, but the
// associated parent block does need to be dominated by the definition.
- return context_->GetDominatorAnalysis(enclosing_function_)
+ return context_->GetDominatorAnalysis(loop_construct_header_->GetParent())
->Dominates(def_block.id(), use->GetSingleWordOperand(use_index + 1));
}
// In non-phi cases, a use needs to be dominated by its definition.
- return context_->GetDominatorAnalysis(enclosing_function_)
+ return context_->GetDominatorAnalysis(loop_construct_header_->GetParent())
->Dominates(def, use);
}
diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity.h b/source/reduce/structured_loop_to_selection_reduction_opportunity.h
index 4c576196..0e3c840a 100644
--- a/source/reduce/structured_loop_to_selection_reduction_opportunity.h
+++ b/source/reduce/structured_loop_to_selection_reduction_opportunity.h
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef SOURCE_REDUCE_CUT_LOOP_REDUCTION_OPPORTUNITY_H_
-#define SOURCE_REDUCE_CUT_LOOP_REDUCTION_OPPORTUNITY_H_
+#ifndef SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_H_
+#define SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_H_
#include "source/opt/def_use_manager.h"
#include "source/opt/dominator_analysis.h"
@@ -30,11 +30,8 @@ class StructuredLoopToSelectionReductionOpportunity
// Constructs an opportunity from a loop header block and the function that
// encloses it.
explicit StructuredLoopToSelectionReductionOpportunity(
- opt::IRContext* context, opt::BasicBlock* loop_construct_header,
- opt::Function* enclosing_function)
- : context_(context),
- loop_construct_header_(loop_construct_header),
- enclosing_function_(enclosing_function) {}
+ opt::IRContext* context, opt::BasicBlock* loop_construct_header)
+ : context_(context), loop_construct_header_(loop_construct_header) {}
// Returns true if the loop header is reachable. A structured loop might
// become unreachable as a result of turning another structured loop into
@@ -88,10 +85,9 @@ class StructuredLoopToSelectionReductionOpportunity
opt::IRContext* context_;
opt::BasicBlock* loop_construct_header_;
- opt::Function* enclosing_function_;
};
} // namespace reduce
} // namespace spvtools
-#endif // SOURCE_REDUCE_CUT_LOOP_REDUCTION_OPPORTUNITY_H_
+#endif // SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_H_
diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp b/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp
index fdf3ab04..3fe61280 100644
--- a/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp
+++ b/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp
@@ -86,8 +86,8 @@ StructuredLoopToSelectionReductionOpportunityFinder::GetAvailableOpportunities(
// We can turn this structured loop into a selection, so add the
// opportunity to do so.
result.push_back(
- MakeUnique<StructuredLoopToSelectionReductionOpportunity>(
- context, &block, function));
+ MakeUnique<StructuredLoopToSelectionReductionOpportunity>(context,
+ &block));
}
}
return result;
diff --git a/source/spirv_constant.h b/source/spirv_constant.h
index 39771ccb..8636806c 100644
--- a/source/spirv_constant.h
+++ b/source/spirv_constant.h
@@ -84,6 +84,7 @@ typedef enum spv_generator_t {
SPV_GENERATOR_KHRONOS_LLVM_TRANSLATOR = 6,
SPV_GENERATOR_KHRONOS_ASSEMBLER = 7,
SPV_GENERATOR_KHRONOS_GLSLANG = 8,
+ SPV_GENERATOR_KHRONOS_LINKER = 17,
SPV_GENERATOR_NUM_ENTRIES,
SPV_FORCE_16_BIT_ENUM(spv_generator_t)
} spv_generator_t;
diff --git a/source/spirv_definition.h b/source/spirv_definition.h
index 63a4ef0d..5dbd6ab2 100644
--- a/source/spirv_definition.h
+++ b/source/spirv_definition.h
@@ -27,7 +27,7 @@ typedef struct spv_header_t {
uint32_t generator;
uint32_t bound;
uint32_t schema; // NOTE: Reserved
- const uint32_t* instructions; // NOTE: Unfixed pointer to instruciton stream
+ const uint32_t* instructions; // NOTE: Unfixed pointer to instruction stream
} spv_header_t;
#endif // SOURCE_SPIRV_DEFINITION_H_
diff --git a/source/spirv_endian.h b/source/spirv_endian.h
index c2540bec..b4927f31 100644
--- a/source/spirv_endian.h
+++ b/source/spirv_endian.h
@@ -31,7 +31,7 @@ uint64_t spvFixDoubleWord(const uint32_t low, const uint32_t high,
spv_result_t spvBinaryEndianness(const spv_const_binary binary,
spv_endianness_t* endian);
-// Returns true if the given endianness matches the host's native endiannes.
+// Returns true if the given endianness matches the host's native endianness.
bool spvIsHostEndian(spv_endianness_t endian);
#endif // SOURCE_SPIRV_ENDIAN_H_
diff --git a/source/spirv_target_env.cpp b/source/spirv_target_env.cpp
index f20ebb4f..9a038174 100644
--- a/source/spirv_target_env.cpp
+++ b/source/spirv_target_env.cpp
@@ -62,7 +62,7 @@ const char* spvTargetEnvDescription(spv_target_env env) {
case SPV_ENV_VULKAN_1_1:
return "SPIR-V 1.3 (under Vulkan 1.1 semantics)";
case SPV_ENV_WEBGPU_0:
- assert(false);
+ assert(false && "Deprecated target environment value.");
break;
case SPV_ENV_UNIVERSAL_1_4:
return "SPIR-V 1.4";
@@ -72,6 +72,13 @@ const char* spvTargetEnvDescription(spv_target_env env) {
return "SPIR-V 1.5";
case SPV_ENV_VULKAN_1_2:
return "SPIR-V 1.5 (under Vulkan 1.2 semantics)";
+ case SPV_ENV_UNIVERSAL_1_6:
+ return "SPIR-V 1.6";
+ case SPV_ENV_VULKAN_1_3:
+ return "SPIR-V 1.6 (under Vulkan 1.3 semantics)";
+ case SPV_ENV_MAX:
+ assert(false && "Invalid target environment value.");
+ break;
}
return "";
}
@@ -102,7 +109,7 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) {
case SPV_ENV_VULKAN_1_1:
return SPV_SPIRV_VERSION_WORD(1, 3);
case SPV_ENV_WEBGPU_0:
- assert(false);
+ assert(false && "Deprecated target environment value.");
break;
case SPV_ENV_UNIVERSAL_1_4:
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
@@ -110,6 +117,12 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) {
case SPV_ENV_UNIVERSAL_1_5:
case SPV_ENV_VULKAN_1_2:
return SPV_SPIRV_VERSION_WORD(1, 5);
+ case SPV_ENV_UNIVERSAL_1_6:
+ case SPV_ENV_VULKAN_1_3:
+ return SPV_SPIRV_VERSION_WORD(1, 6);
+ case SPV_ENV_MAX:
+ assert(false && "Invalid target environment value.");
+ break;
}
return SPV_SPIRV_VERSION_WORD(0, 0);
}
@@ -119,12 +132,14 @@ static const std::pair<const char*, spv_target_env> spvTargetEnvNameMap[] = {
{"vulkan1.0", SPV_ENV_VULKAN_1_0},
{"vulkan1.1", SPV_ENV_VULKAN_1_1},
{"vulkan1.2", SPV_ENV_VULKAN_1_2},
+ {"vulkan1.3", SPV_ENV_VULKAN_1_3},
{"spv1.0", SPV_ENV_UNIVERSAL_1_0},
{"spv1.1", SPV_ENV_UNIVERSAL_1_1},
{"spv1.2", SPV_ENV_UNIVERSAL_1_2},
{"spv1.3", SPV_ENV_UNIVERSAL_1_3},
{"spv1.4", SPV_ENV_UNIVERSAL_1_4},
{"spv1.5", SPV_ENV_UNIVERSAL_1_5},
+ {"spv1.6", SPV_ENV_UNIVERSAL_1_6},
{"opencl1.2embedded", SPV_ENV_OPENCL_EMBEDDED_1_2},
{"opencl1.2", SPV_ENV_OPENCL_1_2},
{"opencl2.0embedded", SPV_ENV_OPENCL_EMBEDDED_2_0},
@@ -171,7 +186,8 @@ static const VulkanEnv ordered_vulkan_envs[] = {
{SPV_ENV_VULKAN_1_0, VULKAN_VER(1, 0), SPIRV_VER(1, 0)},
{SPV_ENV_VULKAN_1_1, VULKAN_VER(1, 1), SPIRV_VER(1, 3)},
{SPV_ENV_VULKAN_1_1_SPIRV_1_4, VULKAN_VER(1, 1), SPIRV_VER(1, 4)},
- {SPV_ENV_VULKAN_1_2, VULKAN_VER(1, 2), SPIRV_VER(1, 5)}};
+ {SPV_ENV_VULKAN_1_2, VULKAN_VER(1, 2), SPIRV_VER(1, 5)},
+ {SPV_ENV_VULKAN_1_3, VULKAN_VER(1, 3), SPIRV_VER(1, 6)}};
bool spvParseVulkanEnv(uint32_t vulkan_ver, uint32_t spirv_ver,
spv_target_env* env) {
@@ -205,14 +221,19 @@ bool spvIsVulkanEnv(spv_target_env env) {
case SPV_ENV_UNIVERSAL_1_3:
case SPV_ENV_UNIVERSAL_1_4:
case SPV_ENV_UNIVERSAL_1_5:
+ case SPV_ENV_UNIVERSAL_1_6:
return false;
case SPV_ENV_VULKAN_1_0:
case SPV_ENV_VULKAN_1_1:
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
case SPV_ENV_VULKAN_1_2:
+ case SPV_ENV_VULKAN_1_3:
return true;
case SPV_ENV_WEBGPU_0:
- assert(false);
+ assert(false && "Deprecated target environment value.");
+ break;
+ case SPV_ENV_MAX:
+ assert(false && "Invalid target environment value.");
break;
}
return false;
@@ -235,6 +256,8 @@ bool spvIsOpenCLEnv(spv_target_env env) {
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
case SPV_ENV_UNIVERSAL_1_5:
case SPV_ENV_VULKAN_1_2:
+ case SPV_ENV_UNIVERSAL_1_6:
+ case SPV_ENV_VULKAN_1_3:
return false;
case SPV_ENV_OPENCL_1_2:
case SPV_ENV_OPENCL_EMBEDDED_1_2:
@@ -246,7 +269,10 @@ bool spvIsOpenCLEnv(spv_target_env env) {
case SPV_ENV_OPENCL_2_2:
return true;
case SPV_ENV_WEBGPU_0:
- assert(false);
+ assert(false && "Deprecated target environment value.");
+ break;
+ case SPV_ENV_MAX:
+ assert(false && "Invalid target environment value.");
break;
}
return false;
@@ -272,6 +298,8 @@ bool spvIsOpenGLEnv(spv_target_env env) {
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
case SPV_ENV_UNIVERSAL_1_5:
case SPV_ENV_VULKAN_1_2:
+ case SPV_ENV_UNIVERSAL_1_6:
+ case SPV_ENV_VULKAN_1_3:
return false;
case SPV_ENV_OPENGL_4_0:
case SPV_ENV_OPENGL_4_1:
@@ -280,7 +308,45 @@ bool spvIsOpenGLEnv(spv_target_env env) {
case SPV_ENV_OPENGL_4_5:
return true;
case SPV_ENV_WEBGPU_0:
- assert(false);
+ assert(false && "Deprecated target environment value.");
+ break;
+ case SPV_ENV_MAX:
+ assert(false && "Invalid target environment value.");
+ break;
+ }
+ return false;
+}
+
+bool spvIsValidEnv(spv_target_env env) {
+ switch (env) {
+ case SPV_ENV_UNIVERSAL_1_0:
+ case SPV_ENV_VULKAN_1_0:
+ case SPV_ENV_UNIVERSAL_1_1:
+ case SPV_ENV_UNIVERSAL_1_2:
+ case SPV_ENV_UNIVERSAL_1_3:
+ case SPV_ENV_VULKAN_1_1:
+ case SPV_ENV_OPENCL_1_2:
+ case SPV_ENV_OPENCL_EMBEDDED_1_2:
+ case SPV_ENV_OPENCL_2_0:
+ case SPV_ENV_OPENCL_EMBEDDED_2_0:
+ case SPV_ENV_OPENCL_EMBEDDED_2_1:
+ case SPV_ENV_OPENCL_EMBEDDED_2_2:
+ case SPV_ENV_OPENCL_2_1:
+ case SPV_ENV_OPENCL_2_2:
+ case SPV_ENV_UNIVERSAL_1_4:
+ case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
+ case SPV_ENV_UNIVERSAL_1_5:
+ case SPV_ENV_VULKAN_1_2:
+ case SPV_ENV_UNIVERSAL_1_6:
+ case SPV_ENV_VULKAN_1_3:
+ case SPV_ENV_OPENGL_4_0:
+ case SPV_ENV_OPENGL_4_1:
+ case SPV_ENV_OPENGL_4_2:
+ case SPV_ENV_OPENGL_4_3:
+ case SPV_ENV_OPENGL_4_5:
+ return true;
+ case SPV_ENV_WEBGPU_0:
+ case SPV_ENV_MAX:
break;
}
return false;
@@ -307,20 +373,25 @@ std::string spvLogStringForEnv(spv_target_env env) {
}
case SPV_ENV_VULKAN_1_0:
case SPV_ENV_VULKAN_1_1:
- case SPV_ENV_VULKAN_1_1_SPIRV_1_4: {
- case SPV_ENV_VULKAN_1_2:
- return "Vulkan";
+ case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
+ case SPV_ENV_VULKAN_1_2:
+ case SPV_ENV_VULKAN_1_3: {
+ return "Vulkan";
}
case SPV_ENV_UNIVERSAL_1_0:
case SPV_ENV_UNIVERSAL_1_1:
case SPV_ENV_UNIVERSAL_1_2:
case SPV_ENV_UNIVERSAL_1_3:
case SPV_ENV_UNIVERSAL_1_4:
- case SPV_ENV_UNIVERSAL_1_5: {
+ case SPV_ENV_UNIVERSAL_1_5:
+ case SPV_ENV_UNIVERSAL_1_6: {
return "Universal";
}
case SPV_ENV_WEBGPU_0:
- assert(false);
+ assert(false && "Deprecated target environment value.");
+ break;
+ case SPV_ENV_MAX:
+ assert(false && "Invalid target environment value.");
break;
}
return "Unknown";
diff --git a/source/spirv_target_env.h b/source/spirv_target_env.h
index a804d615..f3b0c2f6 100644
--- a/source/spirv_target_env.h
+++ b/source/spirv_target_env.h
@@ -28,6 +28,9 @@ bool spvIsOpenCLEnv(spv_target_env env);
// Returns true if |env| is an OPENGL environment, false otherwise.
bool spvIsOpenGLEnv(spv_target_env env);
+// Returns true if |env| is an implemented/valid environment, false otherwise.
+bool spvIsValidEnv(spv_target_env env);
+
// Returns the version number for the given SPIR-V target environment.
uint32_t spvVersionForTargetEnv(spv_target_env env);
@@ -37,7 +40,7 @@ std::string spvLogStringForEnv(spv_target_env env);
// Returns a formatted list of all SPIR-V target environment names that
// can be parsed by spvParseTargetEnv.
-// |pad| is the number of space characters that the begining of each line
+// |pad| is the number of space characters that the beginning of each line
// except the first one will be padded with.
// |wrap| is the max length of lines the user desires. Word-wrapping will
// occur to satisfy this limit.
diff --git a/source/spirv_validator_options.cpp b/source/spirv_validator_options.cpp
index 2716cca9..e5b1eece 100644
--- a/source/spirv_validator_options.cpp
+++ b/source/spirv_validator_options.cpp
@@ -120,3 +120,8 @@ void spvValidatorOptionsSetSkipBlockLayout(spv_validator_options options,
bool val) {
options->skip_block_layout = val;
}
+
+void spvValidatorOptionsSetAllowLocalSizeId(spv_validator_options options,
+ bool val) {
+ options->allow_localsizeid = val;
+}
diff --git a/source/spirv_validator_options.h b/source/spirv_validator_options.h
index baaa5359..a357c031 100644
--- a/source/spirv_validator_options.h
+++ b/source/spirv_validator_options.h
@@ -47,6 +47,7 @@ struct spv_validator_options_t {
scalar_block_layout(false),
workgroup_scalar_block_layout(false),
skip_block_layout(false),
+ allow_localsizeid(false),
before_hlsl_legalization(false) {}
validator_universal_limits_t universal_limits_;
@@ -57,6 +58,7 @@ struct spv_validator_options_t {
bool scalar_block_layout;
bool workgroup_scalar_block_layout;
bool skip_block_layout;
+ bool allow_localsizeid;
bool before_hlsl_legalization;
};
diff --git a/source/table.cpp b/source/table.cpp
index d4a2d7e9..822cefeb 100644
--- a/source/table.cpp
+++ b/source/table.cpp
@@ -41,6 +41,8 @@ spv_context spvContextCreate(spv_target_env env) {
case SPV_ENV_UNIVERSAL_1_4:
case SPV_ENV_UNIVERSAL_1_5:
case SPV_ENV_VULKAN_1_2:
+ case SPV_ENV_UNIVERSAL_1_6:
+ case SPV_ENV_VULKAN_1_3:
break;
default:
return nullptr;
diff --git a/source/text.cpp b/source/text.cpp
index 88a8e8ff..415c059d 100644
--- a/source/text.cpp
+++ b/source/text.cpp
@@ -715,6 +715,12 @@ spv_result_t GetNumericIds(const spvtools::AssemblyGrammar& grammar,
while (context.hasText()) {
spv_instruction_t inst;
+ // Operand parsing sometimes involves knowing the opcode of the instruction
+ // being parsed. A malformed input might feature such an operand *before*
+ // the opcode is known. To guard against accessing an uninitialized opcode,
+ // the instruction's opcode is initialized to a default value.
+ inst.opcode = SpvOpMax;
+
if (spvTextEncodeOpcode(grammar, &context, &inst)) {
return SPV_ERROR_INVALID_TEXT;
}
diff --git a/source/text_handler.cpp b/source/text_handler.cpp
index c31f34a6..fe12a26e 100644
--- a/source/text_handler.cpp
+++ b/source/text_handler.cpp
@@ -29,6 +29,7 @@
#include "source/util/bitutils.h"
#include "source/util/hex_float.h"
#include "source/util/parse_number.h"
+#include "source/util/string_utils.h"
namespace spvtools {
namespace {
@@ -120,7 +121,8 @@ spv_result_t getWord(spv_text text, spv_position position, std::string* word) {
case '\n':
case '\r':
if (escaping || quoting) break;
- // Fall through.
+ word->assign(text->str + start_index, text->str + position->index);
+ return SPV_SUCCESS;
case '\0': { // NOTE: End of word found!
word->assign(text->str + start_index, text->str + position->index);
return SPV_SUCCESS;
@@ -306,14 +308,8 @@ spv_result_t AssemblyContext::binaryEncodeString(const char* value,
<< SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << " words.";
}
- pInst->words.resize(newWordCount);
-
- // Make sure all the bytes in the last word are 0, in case we only
- // write a partial word at the end.
- pInst->words.back() = 0;
-
- char* dest = (char*)&pInst->words[oldWordCount];
- strncpy(dest, value, length + 1);
+ pInst->words.reserve(newWordCount);
+ spvtools::utils::AppendToVector(value, &pInst->words);
return SPV_SUCCESS;
}
diff --git a/source/util/bit_vector.h b/source/util/bit_vector.h
index 3e189cb1..826d62f0 100644
--- a/source/util/bit_vector.h
+++ b/source/util/bit_vector.h
@@ -32,7 +32,7 @@ class BitVector {
enum { kInitialNumBits = 1024 };
public:
- // Creates a bit vector contianing 0s.
+ // Creates a bit vector containing 0s.
BitVector(uint32_t reserved_size = kInitialNumBits)
: bits_((reserved_size - 1) / kBitContainerSize + 1, 0) {}
diff --git a/source/util/hex_float.h b/source/util/hex_float.h
index cfc40fa6..903b6288 100644
--- a/source/util/hex_float.h
+++ b/source/util/hex_float.h
@@ -199,7 +199,7 @@ bool operator==(const FloatProxy<T>& first, const FloatProxy<T>& second) {
// Reads a FloatProxy value as a normal float from a stream.
template <typename T>
std::istream& operator>>(std::istream& is, FloatProxy<T>& value) {
- T float_val;
+ T float_val = static_cast<T>(0.0);
is >> float_val;
value = FloatProxy<T>(float_val);
return is;
@@ -1005,6 +1005,9 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
is.get();
next_char = is.peek();
}
+
+ // Finished reading the part preceding any '.' or 'p'.
+
bits_written = false;
while (seen_dot && !seen_p) {
// Handle only fractional parts now.
@@ -1037,11 +1040,16 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
next_char = is.peek();
}
+ // Finished reading the part preceding 'p'.
+ // In hex floats syntax, the binary exponent is required.
+
bool seen_sign = false;
int8_t exponent_sign = 1;
+ bool seen_written_exponent_digits = false;
int_type written_exponent = 0;
while (true) {
- if ((next_char == '-' || next_char == '+')) {
+ if (!seen_written_exponent_digits &&
+ (next_char == '-' || next_char == '+')) {
if (seen_sign) {
is.setstate(std::ios::failbit);
return is;
@@ -1049,6 +1057,7 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
seen_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 =
@@ -1059,6 +1068,11 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
is.get();
next_char = is.peek();
}
+ if (!seen_written_exponent_digits) {
+ // Binary exponent had no digits.
+ is.setstate(std::ios::failbit);
+ return is;
+ }
written_exponent = static_cast<int_type>(written_exponent * exponent_sign);
exponent = static_cast<int_type>(exponent + written_exponent);
diff --git a/source/util/ilist.h b/source/util/ilist.h
index 9837b09b..b7ecf01e 100644
--- a/source/util/ilist.h
+++ b/source/util/ilist.h
@@ -59,7 +59,7 @@ class IntrusiveList {
// Moves the contents of the given list to the list being constructed.
IntrusiveList(IntrusiveList&&);
- // Destorys the list. Note that the elements of the list will not be deleted,
+ // Destroys the list. Note that the elements of the list will not be deleted,
// but they will be removed from the list.
virtual ~IntrusiveList();
diff --git a/source/util/parse_number.h b/source/util/parse_number.h
index 729aac54..d0f2a09a 100644
--- a/source/util/parse_number.h
+++ b/source/util/parse_number.h
@@ -220,7 +220,7 @@ EncodeNumberStatus ParseAndEncodeIntegerNumber(
std::function<void(uint32_t)> emit, std::string* error_msg);
// Parses a floating point value of a given |type| from the given |text| and
-// encodes the number by the given |emit| funciton. On success, returns
+// encodes the number by the given |emit| function. On success, returns
// EncodeNumberStatus::kSuccess and the parsed number will be consumed by the
// given |emit| function word by word (least significant word first). On
// failure, this function returns the error code of the encoding status and
diff --git a/source/util/small_vector.h b/source/util/small_vector.h
index f2c1147b..f1762a9f 100644
--- a/source/util/small_vector.h
+++ b/source/util/small_vector.h
@@ -175,9 +175,12 @@ class SmallVector {
return true;
}
+// Avoid infinite recursion from rewritten operators in C++20
+#if __cplusplus <= 201703L
friend bool operator==(const std::vector<T>& lhs, const SmallVector& rhs) {
return rhs == lhs;
}
+#endif
friend bool operator!=(const SmallVector& lhs, const std::vector<T>& rhs) {
return !(lhs == rhs);
@@ -363,7 +366,7 @@ class SmallVector {
}
}
- // Upate the size.
+ // Update the size.
size_ += num_of_new_elements;
return pos;
}
@@ -449,7 +452,7 @@ class SmallVector {
T* small_data_;
// The actual data used to store the array elements. It must never be used
- // directly, but must only be accesed through |small_data_|.
+ // directly, but must only be accessed through |small_data_|.
typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type
buffer[small_size];
diff --git a/source/util/string_utils.h b/source/util/string_utils.h
index 4282aa94..03e20b3d 100644
--- a/source/util/string_utils.h
+++ b/source/util/string_utils.h
@@ -16,6 +16,8 @@
#define SOURCE_UTIL_STRING_UTILS_H_
#include <assert.h>
+
+#include <cstring>
#include <sstream>
#include <string>
#include <vector>
@@ -44,9 +46,10 @@ std::string CardinalToOrdinal(size_t cardinal);
// string will be empty.
std::pair<std::string, std::string> SplitFlagArgs(const std::string& flag);
-// Encodes a string as a sequence of words, using the SPIR-V encoding.
-inline std::vector<uint32_t> MakeVector(std::string input) {
- std::vector<uint32_t> result;
+// Encodes a string as a sequence of words, using the SPIR-V encoding, appending
+// to an existing vector.
+inline void AppendToVector(const std::string& input,
+ std::vector<uint32_t>* result) {
uint32_t word = 0;
size_t num_bytes = input.size();
// SPIR-V strings are null-terminated. The byte_index == num_bytes
@@ -56,24 +59,36 @@ inline std::vector<uint32_t> MakeVector(std::string input) {
(byte_index < num_bytes ? uint8_t(input[byte_index]) : uint8_t(0));
word |= (new_byte << (8 * (byte_index % sizeof(uint32_t))));
if (3 == (byte_index % sizeof(uint32_t))) {
- result.push_back(word);
+ result->push_back(word);
word = 0;
}
}
// Emit a trailing partial word.
if ((num_bytes + 1) % sizeof(uint32_t)) {
- result.push_back(word);
+ result->push_back(word);
}
+}
+
+// Encodes a string as a sequence of words, using the SPIR-V encoding.
+inline std::vector<uint32_t> MakeVector(const std::string& input) {
+ std::vector<uint32_t> result;
+ AppendToVector(input, &result);
return result;
}
-// Decode a string from a sequence of words, using the SPIR-V encoding.
-template <class VectorType>
-inline std::string MakeString(const VectorType& words) {
+// Decode a string from a sequence of words between first and last, using the
+// SPIR-V encoding. Assert that a terminating 0-byte was found (unless
+// assert_found_terminating_null is passed as false).
+template <class InputIt>
+inline std::string MakeString(InputIt first, InputIt last,
+ bool assert_found_terminating_null = true) {
std::string result;
+ constexpr size_t kCharsPerWord = sizeof(*first);
+ static_assert(kCharsPerWord == 4, "expect 4-byte word");
- for (uint32_t word : words) {
- for (int byte_index = 0; byte_index < 4; byte_index++) {
+ for (InputIt pos = first; pos != last; ++pos) {
+ uint32_t word = *pos;
+ for (size_t byte_index = 0; byte_index < kCharsPerWord; byte_index++) {
uint32_t extracted_word = (word >> (8 * byte_index)) & 0xFF;
char c = static_cast<char>(extracted_word);
if (c == 0) {
@@ -82,9 +97,33 @@ inline std::string MakeString(const VectorType& words) {
result += c;
}
}
- assert(false && "Did not find terminating null for the string.");
+ assert(!assert_found_terminating_null &&
+ "Did not find terminating null for the string.");
+ (void)assert_found_terminating_null; /* No unused parameters in release
+ builds. */
return result;
-} // namespace utils
+}
+
+// Decode a string from a sequence of words in a vector, using the SPIR-V
+// encoding.
+template <class VectorType>
+inline std::string MakeString(const VectorType& words,
+ bool assert_found_terminating_null = true) {
+ return MakeString(words.cbegin(), words.cend(),
+ assert_found_terminating_null);
+}
+
+// Decode a string from array words, consuming up to count words, using the
+// SPIR-V encoding.
+inline std::string MakeString(const uint32_t* words, size_t num_words,
+ bool assert_found_terminating_null = true) {
+ return MakeString(words, words + num_words, assert_found_terminating_null);
+}
+
+// Check if str starts with prefix (only included since C++20)
+inline bool starts_with(const std::string& str, const char* prefix) {
+ return 0 == str.compare(0, std::strlen(prefix), prefix);
+}
} // namespace utils
} // namespace spvtools
diff --git a/source/util/timer.h b/source/util/timer.h
index fc4b747b..08083119 100644
--- a/source/util/timer.h
+++ b/source/util/timer.h
@@ -206,16 +206,16 @@ class Timer {
// Variable to save the result of clock_gettime(CLOCK_PROCESS_CPUTIME_ID) when
// Timer::Stop() is called. It is used as the last status of CPU time. The
- // resouce usage is measured by subtracting |cpu_before_| from it.
+ // resource usage is measured by subtracting |cpu_before_| from it.
timespec cpu_after_;
// Variable to save the result of clock_gettime(CLOCK_MONOTONIC) when
// Timer::Stop() is called. It is used as the last status of WALL time. The
- // resouce usage is measured by subtracting |wall_before_| from it.
+ // resource usage is measured by subtracting |wall_before_| from it.
timespec wall_after_;
// Variable to save the result of getrusage() when Timer::Stop() is called. It
- // is used as the last status of USR time, SYS time, and RSS. Those resouce
+ // is used as the last status of USR time, SYS time, and RSS. Those resource
// usages are measured by subtracting |usage_before_| from it.
rusage usage_after_;
diff --git a/source/val/basic_block.h b/source/val/basic_block.h
index 5af4b9e4..47cd06d0 100644
--- a/source/val/basic_block.h
+++ b/source/val/basic_block.h
@@ -84,26 +84,26 @@ class BasicBlock {
type_.set(type);
}
- /// Sets the immedate dominator of this basic block
+ /// Sets the immediate dominator of this basic block
///
/// @param[in] dom_block The dominator block
void SetImmediateDominator(BasicBlock* dom_block);
- /// Sets the immedate post dominator of this basic block
+ /// Sets the immediate post dominator of this basic block
///
/// @param[in] pdom_block The post dominator block
void SetImmediatePostDominator(BasicBlock* pdom_block);
- /// Returns the immedate dominator of this basic block
+ /// Returns the immediate dominator of this basic block
BasicBlock* immediate_dominator();
- /// Returns the immedate dominator of this basic block
+ /// Returns the immediate dominator of this basic block
const BasicBlock* immediate_dominator() const;
- /// Returns the immedate post dominator of this basic block
+ /// Returns the immediate post dominator of this basic block
BasicBlock* immediate_post_dominator();
- /// Returns the immedate post dominator of this basic block
+ /// Returns the immediate post dominator of this basic block
const BasicBlock* immediate_post_dominator() const;
/// Returns the label instruction for the block, or nullptr if not set.
diff --git a/source/val/construct.cpp b/source/val/construct.cpp
index 53008690..251e2bba 100644
--- a/source/val/construct.cpp
+++ b/source/val/construct.cpp
@@ -181,8 +181,9 @@ 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.second == 1 && use.first->block()->dominates(*block)) {
return use.first->block();
+ }
}
return block->immediate_dominator();
};
diff --git a/source/val/function.cpp b/source/val/function.cpp
index 249c8664..f3292b0e 100644
--- a/source/val/function.cpp
+++ b/source/val/function.cpp
@@ -57,7 +57,7 @@ spv_result_t Function::RegisterFunctionParameter(uint32_t parameter_id,
uint32_t type_id) {
assert(current_block_ == nullptr &&
"RegisterFunctionParameter can only be called when parsing the binary "
- "ouside of a block");
+ "outside of a block");
// TODO(umar): Validate function parameter type order and count
// TODO(umar): Use these variables to validate parameter type
(void)parameter_id;
@@ -130,7 +130,7 @@ spv_result_t Function::RegisterBlock(uint32_t block_id, bool is_definition) {
undefined_blocks_.erase(block_id);
current_block_ = &inserted_block->second;
ordered_blocks_.push_back(current_block_);
- } else if (success) { // Block doesn't exsist but this is not a definition
+ } else if (success) { // Block doesn't exist but this is not a definition
undefined_blocks_.insert(block_id);
}
@@ -308,6 +308,9 @@ int Function::GetBlockDepth(BasicBlock* bb) {
if (block_depth_.find(bb) != block_depth_.end()) {
return block_depth_[bb];
}
+ // Avoid recursion. Something is wrong if the same block is encountered
+ // multiple times.
+ block_depth_[bb] = 0;
BasicBlock* bb_dom = bb->immediate_dominator();
if (!bb_dom || bb == bb_dom) {
diff --git a/source/val/function.h b/source/val/function.h
index 400bb634..2fe30bdc 100644
--- a/source/val/function.h
+++ b/source/val/function.h
@@ -73,8 +73,8 @@ class Function {
/// Registers a variable in the current block
///
- /// @param[in] type_id The type ID of the varaible
- /// @param[in] id The ID of the varaible
+ /// @param[in] type_id The type ID of the variable
+ /// @param[in] id The ID of the variable
/// @param[in] storage The storage of the variable
/// @param[in] init_id The initializer ID of the variable
///
@@ -197,10 +197,10 @@ class Function {
/// been identified and dominators have been computed.
int GetBlockDepth(BasicBlock* bb);
- /// Prints a GraphViz digraph of the CFG of the current funciton
+ /// Prints a GraphViz digraph of the CFG of the current function
void PrintDotGraph() const;
- /// Prints a directed graph of the CFG of the current funciton
+ /// Prints a directed graph of the CFG of the current function
void PrintBlocks() const;
/// Registers execution model limitation such as "Feature X is only available
@@ -285,7 +285,7 @@ class Function {
/// The type of the return value
uint32_t result_type_id_;
- /// The control fo the funciton
+ /// The control fo the function
SpvFunctionControlMask function_control_;
/// The type of declaration of each function
diff --git a/source/val/instruction.cpp b/source/val/instruction.cpp
index b9155898..f16fcd73 100644
--- a/source/val/instruction.cpp
+++ b/source/val/instruction.cpp
@@ -16,6 +16,9 @@
#include <utility>
+#include "source/binary.h"
+#include "source/util/string_utils.h"
+
namespace spvtools {
namespace val {
@@ -41,5 +44,12 @@ bool operator==(const Instruction& lhs, uint32_t rhs) {
return lhs.id() == rhs;
}
+template <>
+std::string Instruction::GetOperandAs<std::string>(size_t index) const {
+ const spv_parsed_operand_t& o = operands_.at(index);
+ assert(o.offset + o.num_words <= inst_.num_words);
+ return spvtools::utils::MakeString(words_.data() + o.offset, o.num_words);
+}
+
} // namespace val
} // namespace spvtools
diff --git a/source/val/instruction.h b/source/val/instruction.h
index 617cb066..6d1f9f4f 100644
--- a/source/val/instruction.h
+++ b/source/val/instruction.h
@@ -133,6 +133,9 @@ bool operator<(const Instruction& lhs, uint32_t rhs);
bool operator==(const Instruction& lhs, const Instruction& rhs);
bool operator==(const Instruction& lhs, uint32_t rhs);
+template <>
+std::string Instruction::GetOperandAs<std::string>(size_t index) const;
+
} // namespace val
} // namespace spvtools
diff --git a/source/val/validate.cpp b/source/val/validate.cpp
index 45b6a463..ecc9fdb6 100644
--- a/source/val/validate.cpp
+++ b/source/val/validate.cpp
@@ -202,7 +202,7 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
/* diagnostic = */ nullptr);
// Parse the module and perform inline validation checks. These checks do
- // not require the the knowledge of the whole module.
+ // not require the knowledge of the whole module.
if (auto error = spvBinaryParse(&context, vstate, words, num_words,
/*parsed_header =*/nullptr,
ProcessInstruction, pDiagnostic)) {
@@ -219,9 +219,7 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
if (inst->opcode() == SpvOpEntryPoint) {
const auto entry_point = inst->GetOperandAs<uint32_t>(1);
const auto execution_model = inst->GetOperandAs<SpvExecutionModel>(0);
- const char* str = reinterpret_cast<const char*>(
- inst->words().data() + inst->operand(2).offset);
- const std::string desc_name(str);
+ const std::string desc_name = inst->GetOperandAs<std::string>(2);
ValidationState_t::EntryPointDescription desc;
desc.name = desc_name;
@@ -237,9 +235,8 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
for (const Instruction* check_inst : visited_entry_points) {
const auto check_execution_model =
check_inst->GetOperandAs<SpvExecutionModel>(0);
- const char* check_str = reinterpret_cast<const char*>(
- check_inst->words().data() + inst->operand(2).offset);
- const std::string check_name(check_str);
+ const std::string check_name =
+ check_inst->GetOperandAs<std::string>(2);
if (desc_name == check_name &&
execution_model == check_execution_model) {
@@ -351,7 +348,7 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
}
// Validate the preconditions involving adjacent instructions. e.g. SpvOpPhi
- // must only be preceeded by SpvOpLabel, SpvOpPhi, or SpvOpLine.
+ // must only be preceded by SpvOpLabel, SpvOpPhi, or SpvOpLine.
if (auto error = ValidateAdjacency(*vstate)) return error;
if (auto error = ValidateEntryPoints(*vstate)) return error;
diff --git a/source/val/validate.h b/source/val/validate.h
index 3fc183de..cb1d05a5 100644
--- a/source/val/validate.h
+++ b/source/val/validate.h
@@ -70,7 +70,7 @@ spv_result_t CheckIdDefinitionDominateUse(ValidationState_t& _);
///
/// This function will iterate over all instructions and check for any required
/// predecessor and/or successor instructions. e.g. SpvOpPhi must only be
-/// preceeded by SpvOpLabel, SpvOpPhi, or SpvOpLine.
+/// preceded by SpvOpLabel, SpvOpPhi, or SpvOpLine.
///
/// @param[in] _ the validation state of the module
///
diff --git a/source/val/validate_adjacency.cpp b/source/val/validate_adjacency.cpp
index 64655b0d..8e6c373e 100644
--- a/source/val/validate_adjacency.cpp
+++ b/source/val/validate_adjacency.cpp
@@ -60,7 +60,10 @@ spv_result_t ValidateAdjacency(ValidationState_t& _) {
// TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We need
// to discuss the location of DebugScope, DebugNoScope, DebugDeclare,
// and DebugValue.
- if (!spvExtInstIsDebugInfo(inst.ext_inst_type())) {
+ // NOTE: This does not apply to the non-semantic vulkan debug info.
+ if (!spvExtInstIsDebugInfo(inst.ext_inst_type()) ||
+ inst.ext_inst_type() ==
+ SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
adjacency_status = PHI_AND_VAR_INVALID;
}
break;
diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp
index 85d2b751..0614e162 100644
--- a/source/val/validate_annotation.cpp
+++ b/source/val/validate_annotation.cpp
@@ -138,14 +138,14 @@ std::string LogStringForDecoration(uint32_t decoration) {
return "PerTaskNV";
case SpvDecorationPerVertexNV:
return "PerVertexNV";
- case SpvDecorationNonUniformEXT:
- return "NonUniformEXT";
- case SpvDecorationRestrictPointerEXT:
- return "RestrictPointerEXT";
- case SpvDecorationAliasedPointerEXT:
- return "AliasedPointerEXT";
- case SpvDecorationHlslCounterBufferGOOGLE:
- return "HlslCounterBufferGOOGLE";
+ case SpvDecorationNonUniform:
+ return "NonUniform";
+ case SpvDecorationRestrictPointer:
+ return "RestrictPointer";
+ case SpvDecorationAliasedPointer:
+ return "AliasedPointer";
+ case SpvDecorationCounterBuffer:
+ return "CounterBuffer";
case SpvDecorationHlslSemanticGOOGLE:
return "HlslSemanticGOOGLE";
default:
@@ -156,8 +156,8 @@ std::string LogStringForDecoration(uint32_t decoration) {
// Returns true if the decoration takes ID parameters.
// TODO(dneto): This can be generated from the grammar.
-bool DecorationTakesIdParameters(uint32_t type) {
- switch (static_cast<SpvDecoration>(type)) {
+bool DecorationTakesIdParameters(SpvDecoration type) {
+ switch (type) {
case SpvDecorationUniformId:
case SpvDecorationAlignmentId:
case SpvDecorationMaxByteOffsetId:
@@ -169,17 +169,213 @@ bool DecorationTakesIdParameters(uint32_t type) {
return false;
}
-spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
- const auto decoration = inst->GetOperandAs<uint32_t>(1);
- if (decoration == SpvDecorationSpecId) {
- const auto target_id = inst->GetOperandAs<uint32_t>(0);
- const auto target = _.FindDef(target_id);
- if (!target || !spvOpcodeIsScalarSpecConstant(target->opcode())) {
- return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "OpDecorate SpecId decoration target <id> '"
- << _.getIdName(target_id)
- << "' is not a scalar specialization constant.";
+bool IsMemberDecorationOnly(SpvDecoration dec) {
+ switch (dec) {
+ case SpvDecorationRowMajor:
+ case SpvDecorationColMajor:
+ case SpvDecorationMatrixStride:
+ // SPIR-V spec bug? Offset is generated on variables when dealing with
+ // transform feedback.
+ // case SpvDecorationOffset:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool IsNotMemberDecoration(SpvDecoration dec) {
+ switch (dec) {
+ case SpvDecorationSpecId:
+ case SpvDecorationBlock:
+ case SpvDecorationBufferBlock:
+ case SpvDecorationArrayStride:
+ case SpvDecorationGLSLShared:
+ case SpvDecorationGLSLPacked:
+ case SpvDecorationCPacked:
+ // TODO: https://github.com/KhronosGroup/glslang/issues/703:
+ // glslang applies Restrict to structure members.
+ // case SpvDecorationRestrict:
+ case SpvDecorationAliased:
+ case SpvDecorationConstant:
+ case SpvDecorationUniform:
+ case SpvDecorationUniformId:
+ case SpvDecorationSaturatedConversion:
+ case SpvDecorationIndex:
+ case SpvDecorationBinding:
+ case SpvDecorationDescriptorSet:
+ case SpvDecorationFuncParamAttr:
+ case SpvDecorationFPRoundingMode:
+ case SpvDecorationFPFastMathMode:
+ case SpvDecorationLinkageAttributes:
+ case SpvDecorationNoContraction:
+ case SpvDecorationInputAttachmentIndex:
+ case SpvDecorationAlignment:
+ case SpvDecorationMaxByteOffset:
+ case SpvDecorationAlignmentId:
+ case SpvDecorationMaxByteOffsetId:
+ case SpvDecorationNoSignedWrap:
+ case SpvDecorationNoUnsignedWrap:
+ case SpvDecorationNonUniform:
+ case SpvDecorationRestrictPointer:
+ case SpvDecorationAliasedPointer:
+ case SpvDecorationCounterBuffer:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec,
+ const Instruction* inst,
+ const Instruction* target) {
+ auto fail = [&_, dec, inst, target](uint32_t vuid) -> DiagnosticStream {
+ DiagnosticStream ds = std::move(
+ _.diag(SPV_ERROR_INVALID_ID, inst)
+ << _.VkErrorID(vuid) << LogStringForDecoration(dec)
+ << " decoration on target <id> '" << _.getIdName(target->id()) << "' ");
+ return ds;
+ };
+ switch (dec) {
+ case SpvDecorationSpecId:
+ if (!spvOpcodeIsScalarSpecConstant(target->opcode())) {
+ return fail(0) << "must be a scalar specialization constant";
+ }
+ break;
+ case SpvDecorationBlock:
+ case SpvDecorationBufferBlock:
+ case SpvDecorationGLSLShared:
+ case SpvDecorationGLSLPacked:
+ case SpvDecorationCPacked:
+ if (target->opcode() != SpvOpTypeStruct) {
+ return fail(0) << "must be a structure type";
+ }
+ break;
+ case SpvDecorationArrayStride:
+ if (target->opcode() != SpvOpTypeArray &&
+ target->opcode() != SpvOpTypeRuntimeArray &&
+ target->opcode() != SpvOpTypePointer) {
+ return fail(0) << "must be an array or pointer type";
+ }
+ break;
+ case SpvDecorationBuiltIn:
+ if (target->opcode() != SpvOpVariable &&
+ !spvOpcodeIsConstant(target->opcode())) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "BuiltIns can only target variables, structure members or "
+ "constants";
+ }
+ if (_.HasCapability(SpvCapabilityShader) &&
+ inst->GetOperandAs<SpvBuiltIn>(2) == SpvBuiltInWorkgroupSize) {
+ if (!spvOpcodeIsConstant(target->opcode())) {
+ return fail(0) << "must be a constant for WorkgroupSize";
+ }
+ } else if (target->opcode() != SpvOpVariable) {
+ return fail(0) << "must be a variable";
+ }
+ break;
+ case SpvDecorationNoPerspective:
+ case SpvDecorationFlat:
+ case SpvDecorationPatch:
+ case SpvDecorationCentroid:
+ case SpvDecorationSample:
+ case SpvDecorationRestrict:
+ case SpvDecorationAliased:
+ case SpvDecorationVolatile:
+ case SpvDecorationCoherent:
+ case SpvDecorationNonWritable:
+ case SpvDecorationNonReadable:
+ case SpvDecorationXfbBuffer:
+ case SpvDecorationXfbStride:
+ case SpvDecorationComponent:
+ case SpvDecorationStream:
+ case SpvDecorationRestrictPointer:
+ case SpvDecorationAliasedPointer:
+ if (target->opcode() != SpvOpVariable &&
+ target->opcode() != SpvOpFunctionParameter) {
+ return fail(0) << "must be a memory object declaration";
+ }
+ if (_.GetIdOpcode(target->type_id()) != SpvOpTypePointer) {
+ return fail(0) << "must be a pointer type";
+ }
+ break;
+ case SpvDecorationInvariant:
+ case SpvDecorationConstant:
+ case SpvDecorationLocation:
+ case SpvDecorationIndex:
+ case SpvDecorationBinding:
+ case SpvDecorationDescriptorSet:
+ case SpvDecorationInputAttachmentIndex:
+ if (target->opcode() != SpvOpVariable) {
+ return fail(0) << "must be a variable";
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (spvIsVulkanEnv(_.context()->target_env)) {
+ // The following were all checked as pointer types above.
+ SpvStorageClass sc = SpvStorageClassUniform;
+ const auto type = _.FindDef(target->type_id());
+ if (type && type->operands().size() > 2) {
+ sc = type->GetOperandAs<SpvStorageClass>(1);
}
+ switch (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) {
+ return _.diag(SPV_ERROR_INVALID_ID, target)
+ << LogStringForDecoration(dec)
+ << " decoration must not be applied to this storage class";
+ }
+ break;
+ case SpvDecorationIndex:
+ if (sc != SpvStorageClassOutput) {
+ return fail(0) << "must be in the Output storage class";
+ }
+ break;
+ case SpvDecorationBinding:
+ case SpvDecorationDescriptorSet:
+ if (sc != SpvStorageClassStorageBuffer &&
+ sc != SpvStorageClassUniform &&
+ sc != SpvStorageClassUniformConstant) {
+ return fail(0) << "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";
+ }
+ break;
+ case SpvDecorationFlat:
+ case SpvDecorationNoPerspective:
+ case SpvDecorationCentroid:
+ case SpvDecorationSample:
+ if (sc != SpvStorageClassInput && sc != SpvStorageClassOutput) {
+ return fail(4670) << "storage class must be Input or Output";
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return SPV_SUCCESS;
+}
+
+spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
+ const auto decoration = inst->GetOperandAs<SpvDecoration>(1);
+ const auto target_id = inst->GetOperandAs<uint32_t>(0);
+ const auto target = _.FindDef(target_id);
+ if (!target) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst) << "target is not defined";
}
if (spvIsVulkanEnv(_.context()->target_env)) {
@@ -197,17 +393,34 @@ spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
<< "Decorations taking ID parameters may not be used with "
"OpDecorateId";
}
+
+ if (target->opcode() != SpvOpDecorationGroup) {
+ if (IsMemberDecorationOnly(decoration)) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << LogStringForDecoration(decoration)
+ << " can only be applied to structure members";
+ }
+
+ if (auto error = ValidateDecorationTarget(_, decoration, inst, target)) {
+ return error;
+ }
+ }
+
// TODO: Add validations for all decorations.
return SPV_SUCCESS;
}
spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) {
- const auto decoration = inst->GetOperandAs<uint32_t>(1);
+ const auto decoration = inst->GetOperandAs<SpvDecoration>(1);
if (!DecorationTakesIdParameters(decoration)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Decorations that don't take ID parameters may not be used with "
"OpDecorateId";
}
+
+ // No member decorations take id parameters, so we don't bother checking if
+ // we are using a member only decoration here.
+
// TODO: Add validations for these decorations.
// UniformId is covered elsewhere.
return SPV_SUCCESS;
@@ -234,6 +447,13 @@ spv_result_t ValidateMemberDecorate(ValidationState_t& _,
<< " members. Largest valid index is " << member_count - 1 << ".";
}
+ const auto decoration = inst->GetOperandAs<SpvDecoration>(2);
+ if (IsNotMemberDecoration(decoration)) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << LogStringForDecoration(decoration)
+ << " cannot be applied to structure members";
+ }
+
return SPV_SUCCESS;
}
diff --git a/source/val/validate_arithmetics.cpp b/source/val/validate_arithmetics.cpp
index 433330d7..bae9b5dc 100644
--- a/source/val/validate_arithmetics.cpp
+++ b/source/val/validate_arithmetics.cpp
@@ -155,7 +155,7 @@ spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst) {
first_vector_num_components = num_components;
} else if (num_components != first_vector_num_components) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected operands to have the same number of componenets: "
+ << "Expected operands to have the same number of components: "
<< spvOpcodeString(opcode);
}
}
diff --git a/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp
index fa53ca1f..cfa15d9f 100644
--- a/source/val/validate_atomics.cpp
+++ b/source/val/validate_atomics.cpp
@@ -184,7 +184,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
}
// Can't use result_type because OpAtomicStore doesn't have a result
- if (_.GetBitWidth(data_type) == 64 && _.IsIntScalarType(data_type) &&
+ if ( _.IsIntScalarType(data_type) &&_.GetBitWidth(data_type) == 64 &&
!_.HasCapability(SpvCapabilityInt64Atomics)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< spvOpcodeString(opcode)
@@ -222,6 +222,13 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
if (opcode == SpvOpAtomicFAddEXT) {
// result type being float checked already
+ if ((_.GetBitWidth(result_type) == 16) &&
+ (!_.HasCapability(SpvCapabilityAtomicFloat16AddEXT))) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << spvOpcodeString(opcode)
+ << ": float add atomics require the AtomicFloat32AddEXT "
+ "capability";
+ }
if ((_.GetBitWidth(result_type) == 32) &&
(!_.HasCapability(SpvCapabilityAtomicFloat32AddEXT))) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp
index 3c9df9fc..57dde8ad 100644
--- a/source/val/validate_builtins.cpp
+++ b/source/val/validate_builtins.cpp
@@ -2846,7 +2846,7 @@ 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 execution model. "
+ << " to be used only with GLCompute, MeshNV, or TaskNV execution model. "
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
referenced_from_inst, execution_model);
}
@@ -2928,7 +2928,7 @@ 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 execution model. "
+ << " to be used only with GLCompute, MeshNV, or TaskNV execution model. "
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
referenced_from_inst, execution_model);
}
@@ -3070,14 +3070,16 @@ spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtReference(
const Instruction& referenced_from_inst) {
if (spvIsVulkanEnv(_.context()->target_env)) {
for (const SpvExecutionModel execution_model : execution_models_) {
- if (execution_model != SpvExecutionModelGLCompute) {
+ if (execution_model != SpvExecutionModelGLCompute &&
+ execution_model != SpvExecutionModelTaskNV &&
+ execution_model != SpvExecutionModelMeshNV) {
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 execution model. "
+ << " to be used only with GLCompute, MeshNV, or TaskNV execution model. "
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
referenced_from_inst, execution_model);
}
@@ -3991,14 +3993,6 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
const Decoration& decoration, const Instruction& inst) {
const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]);
- // Builtins can only be applied to variables, structures or constants.
- auto target_opcode = inst.opcode();
- if (target_opcode != SpvOpTypeStruct && target_opcode != SpvOpVariable &&
- !spvOpcodeIsConstant(target_opcode)) {
- return _.diag(SPV_ERROR_INVALID_DATA, &inst)
- << "BuiltIns can only target variables, structs or constants";
- }
-
if (!spvIsVulkanEnv(_.context()->target_env)) {
// Early return. All currently implemented rules are based on Vulkan spec.
//
@@ -4190,6 +4184,7 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
case SpvBuiltInMeshViewIndicesNV:
case SpvBuiltInBaryCoordNV:
case SpvBuiltInBaryCoordNoPerspNV:
+ case SpvBuiltInCurrentRayTimeNV:
// No validation rules (for the moment).
break;
diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp
index 36f632a8..88abd754 100644
--- a/source/val/validate_cfg.cpp
+++ b/source/val/validate_cfg.cpp
@@ -27,6 +27,7 @@
#include "source/cfa.h"
#include "source/opcode.h"
+#include "source/spirv_constant.h"
#include "source/spirv_target_env.h"
#include "source/spirv_validator_options.h"
#include "source/val/basic_block.h"
@@ -191,6 +192,12 @@ spv_result_t ValidateBranchConditional(ValidationState_t& _,
"ID of an OpLabel instruction";
}
+ if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) && true_id == false_id) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "In SPIR-V 1.6 or later, True Label and False Label must be "
+ "different labels";
+ }
+
return SPV_SUCCESS;
}
@@ -199,6 +206,18 @@ spv_result_t ValidateSwitch(ValidationState_t& _, const Instruction* inst) {
// At least two operands (selector, default), any more than that are
// literal/target.
+ const auto sel_type_id = _.GetOperandTypeId(inst, 0);
+ if (!_.IsIntScalarType(sel_type_id)) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "Selector type must be OpTypeInt";
+ }
+
+ const auto default_label = _.FindDef(inst->GetOperandAs<uint32_t>(1));
+ if (default_label->opcode() != SpvOpLabel) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "Default must be an OpLabel instruction";
+ }
+
// target operands must be OpLabel
for (size_t i = 2; i < num_operands; i += 2) {
// literal, id
@@ -647,16 +666,16 @@ spv_result_t ValidateStructuredSelections(
// Mark the upcoming blocks as seen now, but only error out if this block
// was missing a merge instruction and both labels hadn't been seen
// previously.
- const bool both_unseen =
- seen.insert(true_label).second && seen.insert(false_label).second;
- if (!merge && both_unseen) {
+ const bool true_label_unseen = seen.insert(true_label).second;
+ const bool false_label_unseen = seen.insert(false_label).second;
+ if (!merge && true_label_unseen && false_label_unseen) {
return _.diag(SPV_ERROR_INVALID_CFG, terminator)
<< "Selection must be structured";
}
} else if (terminator->opcode() == SpvOpSwitch) {
if (!merge) {
return _.diag(SPV_ERROR_INVALID_CFG, terminator)
- << "OpSwitch must be preceeded by an OpSelectionMerge "
+ << "OpSwitch must be preceded by an OpSelectionMerge "
"instruction";
}
// Mark the targets as seen.
@@ -898,7 +917,7 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) {
}
}
}
- // If we have structed control flow, check that no block has a control
+ // If we have structured control flow, check that no block has a control
// flow nesting depth larger than the limit.
if (_.HasCapability(SpvCapabilityShader)) {
const int control_flow_nesting_depth_limit =
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index f076b04c..eb6caf0b 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -21,11 +21,13 @@
#include <utility>
#include <vector>
+#include "source/binary.h"
#include "source/diagnostic.h"
#include "source/opcode.h"
#include "source/spirv_constant.h"
#include "source/spirv_target_env.h"
#include "source/spirv_validator_options.h"
+#include "source/util/string_utils.h"
#include "source/val/validate_scopes.h"
#include "source/val/validation_state.h"
@@ -96,6 +98,14 @@ bool isBuiltInStruct(uint32_t struct_id, ValidationState_t& vstate) {
});
}
+// Returns true if the given structure type has a Block decoration.
+bool isBlock(uint32_t struct_id, ValidationState_t& vstate) {
+ const auto& decorations = vstate.id_decorations(struct_id);
+ return std::any_of(
+ decorations.begin(), decorations.end(),
+ [](const Decoration& d) { return SpvDecorationBlock == d.dec_type(); });
+}
+
// Returns true if the given ID has the Import LinkageAttributes decoration.
bool hasImportLinkageAttribute(uint32_t id, ValidationState_t& vstate) {
const auto& decorations = vstate.id_decorations(id);
@@ -129,18 +139,30 @@ std::vector<uint32_t> getStructMembers(uint32_t struct_id, SpvOp type,
// Returns whether the given structure is missing Offset decoration for any
// member. Handles also nested structures.
bool isMissingOffsetInStruct(uint32_t struct_id, ValidationState_t& vstate) {
- std::vector<bool> hasOffset(getStructMembers(struct_id, vstate).size(),
- false);
- // Check offsets of member decorations
- for (auto& decoration : vstate.id_decorations(struct_id)) {
- if (SpvDecorationOffset == decoration.dec_type() &&
- Decoration::kInvalidMember != decoration.struct_member_index()) {
- hasOffset[decoration.struct_member_index()] = true;
+ const auto* inst = vstate.FindDef(struct_id);
+ std::vector<bool> hasOffset;
+ std::vector<uint32_t> struct_members;
+ if (inst->opcode() == SpvOpTypeStruct) {
+ // Check offsets of member decorations.
+ struct_members = getStructMembers(struct_id, vstate);
+ hasOffset.resize(struct_members.size(), false);
+
+ for (auto& decoration : vstate.id_decorations(struct_id)) {
+ if (SpvDecorationOffset == decoration.dec_type() &&
+ Decoration::kInvalidMember != decoration.struct_member_index()) {
+ // Offset 0xffffffff is not valid so ignore it for simplicity's sake.
+ if (decoration.params()[0] == 0xffffffff) return true;
+ hasOffset[decoration.struct_member_index()] = true;
+ }
}
+ } else if (inst->opcode() == SpvOpTypeArray ||
+ inst->opcode() == SpvOpTypeRuntimeArray) {
+ hasOffset.resize(1, true);
+ struct_members.push_back(inst->GetOperandAs<uint32_t>(1u));
}
- // Check also nested structures
+ // Look through nested structs (which may be in an array).
bool nestedStructsMissingOffset = false;
- for (auto id : getStructMembers(struct_id, SpvOpTypeStruct, vstate)) {
+ for (auto id : struct_members) {
if (isMissingOffsetInStruct(id, vstate)) {
nestedStructsMissingOffset = true;
break;
@@ -443,7 +465,7 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
return lhs.offset < rhs.offset;
});
- // Now scan from lowest offest to highest offset.
+ // Now scan from lowest offset to highest offset.
uint32_t nextValidOffset = 0;
for (size_t ordered_member_idx = 0;
ordered_member_idx < member_offsets.size(); ordered_member_idx++) {
@@ -506,12 +528,10 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
return recursive_status;
// Check matrix stride.
if (SpvOpTypeMatrix == opcode) {
- for (auto& decoration : vstate.id_decorations(id)) {
- if (SpvDecorationMatrixStride == decoration.dec_type() &&
- !IsAlignedTo(decoration.params()[0], alignment))
- return fail(memberIdx)
- << "is a matrix with stride " << decoration.params()[0]
- << " not satisfying alignment to " << alignment;
+ const auto stride = constraint.matrix_stride;
+ if (!IsAlignedTo(stride, alignment)) {
+ return fail(memberIdx) << "is a matrix with stride " << stride
+ << " not satisfying alignment to " << alignment;
}
}
@@ -549,17 +569,23 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
// limitation to this check if the array size is a spec constant or is a
// runtime array then we will only check a single element. This means
// some improper straddles might be missed.
- for (uint32_t i = 0; i < num_elements; ++i) {
- uint32_t next_offset = i * array_stride + offset;
- if (SpvOpTypeStruct == element_inst->opcode() &&
- SPV_SUCCESS != (recursive_status = checkLayout(
- typeId, storage_class_str, decoration_str,
- blockRules, scalar_block_layout,
- next_offset, constraints, vstate)))
- return recursive_status;
- // If offsets accumulate up to a 16-byte multiple stop checking since
- // it will just repeat.
- if (i > 0 && (next_offset % 16 == 0)) break;
+ if (SpvOpTypeStruct == element_inst->opcode()) {
+ std::vector<bool> seen(16, false);
+ for (uint32_t i = 0; i < num_elements; ++i) {
+ uint32_t next_offset = i * array_stride + offset;
+ // Stop checking if offsets repeat in terms of 16-byte multiples.
+ if (seen[next_offset % 16]) {
+ break;
+ }
+
+ if (SPV_SUCCESS !=
+ (recursive_status = checkLayout(
+ typeId, storage_class_str, decoration_str, blockRules,
+ scalar_block_layout, next_offset, constraints, vstate)))
+ return recursive_status;
+
+ seen[next_offset % 16] = true;
+ }
}
// Proceed to the element in case it is an array.
@@ -686,7 +712,7 @@ spv_result_t CheckBuiltInVariable(uint32_t var_id, ValidationState_t& vstate) {
if (d.dec_type() == SpvDecorationLocation ||
d.dec_type() == SpvDecorationComponent) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
- << "A BuiltIn variable (id " << var_id
+ << vstate.VkErrorID(4915) << "A BuiltIn variable (id " << var_id
<< ") cannot have any Location or Component decorations";
}
}
@@ -694,12 +720,12 @@ spv_result_t CheckBuiltInVariable(uint32_t var_id, ValidationState_t& vstate) {
return SPV_SUCCESS;
}
-// Checks whether proper decorations have been appied to the entry points.
+// Checks whether proper decorations have been applied to the entry points.
spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
for (uint32_t entry_point : vstate.entry_points()) {
const auto& descs = vstate.entry_point_descriptions(entry_point);
- int num_builtin_inputs = 0;
- int num_builtin_outputs = 0;
+ int num_builtin_block_inputs = 0;
+ int num_builtin_block_outputs = 0;
int num_workgroup_variables = 0;
int num_workgroup_variables_with_block = 0;
int num_workgroup_variables_with_aliased = 0;
@@ -750,9 +776,20 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
Instruction* type_instr = vstate.FindDef(type_id);
if (type_instr && SpvOpTypeStruct == type_instr->opcode() &&
isBuiltInStruct(type_id, vstate)) {
- if (storage_class == SpvStorageClassInput) ++num_builtin_inputs;
- if (storage_class == SpvStorageClassOutput) ++num_builtin_outputs;
- if (num_builtin_inputs > 1 || num_builtin_outputs > 1) break;
+ if (!isBlock(type_id, vstate)) {
+ return vstate.diag(SPV_ERROR_INVALID_DATA, vstate.FindDef(type_id))
+ << vstate.VkErrorID(4919)
+ << "Interface struct has no Block decoration but has "
+ "BuiltIn members. "
+ "Location decorations must be used on each member of "
+ "OpVariable with a structure type that is a block not "
+ "decorated with Location.";
+ }
+ if (storage_class == SpvStorageClassInput) ++num_builtin_block_inputs;
+ if (storage_class == SpvStorageClassOutput)
+ ++num_builtin_block_outputs;
+ if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1)
+ break;
if (auto error = CheckBuiltInVariable(interface, vstate))
return error;
} else if (isBuiltInVar(interface, vstate)) {
@@ -770,7 +807,7 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
}
}
}
- if (num_builtin_inputs > 1 || num_builtin_outputs > 1) {
+ if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1) {
return vstate.diag(SPV_ERROR_INVALID_BINARY,
vstate.FindDef(entry_point))
<< "There must be at most one object per Storage Class that can "
@@ -782,8 +819,8 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
// targeted by an OpEntryPoint instruction
for (auto& decoration : vstate.id_decorations(entry_point)) {
if (SpvDecorationLinkageAttributes == decoration.dec_type()) {
- const char* linkage_name =
- reinterpret_cast<const char*>(&decoration.params()[0]);
+ const std::string linkage_name =
+ spvtools::utils::MakeString(decoration.params());
return vstate.diag(SPV_ERROR_INVALID_BINARY,
vstate.FindDef(entry_point))
<< "The LinkageAttributes Decoration (Linkage name: "
@@ -981,7 +1018,9 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
const bool phys_storage_buffer =
storageClass == SpvStorageClassPhysicalStorageBufferEXT;
- const bool workgroup = storageClass == SpvStorageClassWorkgroup;
+ const bool workgroup =
+ storageClass == SpvStorageClassWorkgroup &&
+ vstate.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayoutKHR);
if (uniform || push_constant || storage_buffer || phys_storage_buffer ||
workgroup) {
const auto ptrInst = vstate.FindDef(words[1]);
diff --git a/source/val/validate_derivatives.cpp b/source/val/validate_derivatives.cpp
index 067cc964..25b941ab 100644
--- a/source/val/validate_derivatives.cpp
+++ b/source/val/validate_derivatives.cpp
@@ -79,11 +79,13 @@ spv_result_t DerivativesPass(ValidationState_t& _, const Instruction* inst) {
std::string* message) {
const auto* models = state.GetExecutionModels(entry_point->id());
const auto* modes = state.GetExecutionModes(entry_point->id());
- if (models->find(SpvExecutionModelGLCompute) != models->end() &&
- modes->find(SpvExecutionModeDerivativeGroupLinearNV) ==
- modes->end() &&
- modes->find(SpvExecutionModeDerivativeGroupQuadsNV) ==
- modes->end()) {
+ if (models &&
+ models->find(SpvExecutionModelGLCompute) != models->end() &&
+ (!modes ||
+ (modes->find(SpvExecutionModeDerivativeGroupLinearNV) ==
+ modes->end() &&
+ modes->find(SpvExecutionModeDerivativeGroupQuadsNV) ==
+ modes->end()))) {
if (message) {
*message = std::string(
"Derivative instructions require "
diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp
index a7167fc1..01cbcd25 100644
--- a/source/val/validate_extensions.cpp
+++ b/source/val/validate_extensions.cpp
@@ -20,7 +20,9 @@
#include "spirv/unified1/NonSemanticClspvReflection.h"
+#include "NonSemanticShaderDebugInfo100.h"
#include "OpenCLDebugInfo100.h"
+#include "source/common_debug_info.h"
#include "source/diagnostic.h"
#include "source/enum_string_mapping.h"
#include "source/extensions.h"
@@ -45,6 +47,34 @@ uint32_t GetSizeTBitWidth(const ValidationState_t& _) {
return 0;
}
+bool IsIntScalar(ValidationState_t& _, uint32_t id, bool must_len32,
+ bool must_unsigned) {
+ auto type = _.FindDef(id);
+ if (!type || type->opcode() != SpvOpTypeInt) {
+ return false;
+ }
+
+ if (must_len32 && type->GetOperandAs<uint32_t>(1) != 32) {
+ return false;
+ }
+
+ return !must_unsigned || type->GetOperandAs<uint32_t>(2) == 0;
+}
+
+bool IsUint32Constant(ValidationState_t& _, uint32_t id) {
+ auto inst = _.FindDef(id);
+ if (!inst || inst->opcode() != SpvOpConstant) {
+ return false;
+ }
+
+ return IsIntScalar(_, inst->type_id(), true, true);
+}
+
+uint32_t GetUint32Constant(ValidationState_t& _, uint32_t id) {
+ auto inst = _.FindDef(id);
+ return inst->word(3);
+}
+
// Check that the operand of a debug info instruction |inst| at |word_index|
// is a result id of an instruction with |expected_opcode|.
spv_result_t ValidateOperandForDebugInfo(
@@ -68,6 +98,22 @@ spv_result_t ValidateOperandForDebugInfo(
return SPV_SUCCESS;
}
+// For NonSemantic.Shader.DebugInfo.100 check that the operand of a debug info
+// instruction |inst| at |word_index| is a result id of a 32-bit integer
+// OpConstant instruction. For OpenCL.DebugInfo.100 the parameter is a literal
+// word so cannot be validated.
+spv_result_t ValidateUint32ConstantOperandForDebugInfo(
+ ValidationState_t& _, const std::string& operand_name,
+ const Instruction* inst, uint32_t word_index,
+ const std::function<std::string()>& ext_inst_name) {
+ if (!IsUint32Constant(_, inst->word(word_index))) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": expected operand " << operand_name
+ << " must be a result id of 32-bit unsigned OpConstant";
+ }
+ return SPV_SUCCESS;
+}
+
#define CHECK_OPERAND(NAME, opcode, index) \
do { \
auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index, \
@@ -75,18 +121,27 @@ spv_result_t ValidateOperandForDebugInfo(
if (result != SPV_SUCCESS) return result; \
} while (0)
+#define CHECK_CONST_UINT_OPERAND(NAME, index) \
+ if (vulkanDebugInfo) { \
+ auto result = ValidateUint32ConstantOperandForDebugInfo( \
+ _, NAME, inst, index, ext_inst_name); \
+ if (result != SPV_SUCCESS) return result; \
+ }
+
// True if the operand of a debug info instruction |inst| at |word_index|
-// satisifies |expectation| that is given as a function. Otherwise,
+// satisfies |expectation| that is given as a function. Otherwise,
// returns false.
bool DoesDebugInfoOperandMatchExpectation(
const ValidationState_t& _,
- const std::function<bool(OpenCLDebugInfo100Instructions)>& expectation,
+ const std::function<bool(CommonDebugInfoInstructions)>& 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_OPENCL_DEBUGINFO_100 ||
- !expectation(OpenCLDebugInfo100Instructions(debug_inst->word(4)))) {
+ (debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 &&
+ debug_inst->ext_inst_type() !=
+ SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) ||
+ !expectation(CommonDebugInfoInstructions(debug_inst->word(4)))) {
return false;
}
return true;
@@ -97,20 +152,18 @@ bool DoesDebugInfoOperandMatchExpectation(
// is |expected_debug_inst|.
spv_result_t ValidateDebugInfoOperand(
ValidationState_t& _, const std::string& debug_inst_name,
- OpenCLDebugInfo100Instructions expected_debug_inst, const Instruction* inst,
+ CommonDebugInfoInstructions expected_debug_inst, const Instruction* inst,
uint32_t word_index, const std::function<std::string()>& ext_inst_name) {
- std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
- [expected_debug_inst](OpenCLDebugInfo100Instructions dbg_inst) {
+ std::function<bool(CommonDebugInfoInstructions)> expectation =
+ [expected_debug_inst](CommonDebugInfoInstructions dbg_inst) {
return dbg_inst == expected_debug_inst;
};
if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
return SPV_SUCCESS;
spv_ext_inst_desc desc = nullptr;
- _.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
- expected_debug_inst, &desc);
- if (_.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
- expected_debug_inst, &desc) != SPV_SUCCESS ||
+ if (_.grammar().lookupExtInst(inst->ext_inst_type(), expected_debug_inst,
+ &desc) != SPV_SUCCESS ||
!desc) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
@@ -134,9 +187,8 @@ spv_result_t ValidateDebugInfoOperand(
spv_result_t ValidateOperandBaseType(
ValidationState_t& _, const Instruction* inst, uint32_t word_index,
const std::function<std::string()>& ext_inst_name) {
- return ValidateDebugInfoOperand(_, "Base Type",
- OpenCLDebugInfo100DebugTypeBasic, inst,
- word_index, ext_inst_name);
+ return ValidateDebugInfoOperand(_, "Base Type", CommonDebugInfoDebugTypeBasic,
+ inst, word_index, ext_inst_name);
}
// Check that the operand of a debug info instruction |inst| at |word_index|
@@ -147,12 +199,12 @@ spv_result_t ValidateOperandLexicalScope(
ValidationState_t& _, const std::string& debug_inst_name,
const Instruction* inst, uint32_t word_index,
const std::function<std::string()>& ext_inst_name) {
- std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
- [](OpenCLDebugInfo100Instructions dbg_inst) {
- return dbg_inst == OpenCLDebugInfo100DebugCompilationUnit ||
- dbg_inst == OpenCLDebugInfo100DebugFunction ||
- dbg_inst == OpenCLDebugInfo100DebugLexicalBlock ||
- dbg_inst == OpenCLDebugInfo100DebugTypeComposite;
+ std::function<bool(CommonDebugInfoInstructions)> expectation =
+ [](CommonDebugInfoInstructions dbg_inst) {
+ return dbg_inst == CommonDebugInfoDebugCompilationUnit ||
+ dbg_inst == CommonDebugInfoDebugFunction ||
+ dbg_inst == CommonDebugInfoDebugLexicalBlock ||
+ dbg_inst == CommonDebugInfoDebugTypeComposite;
};
if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
return SPV_SUCCESS;
@@ -171,16 +223,15 @@ spv_result_t ValidateOperandDebugType(
const Instruction* inst, uint32_t word_index,
const std::function<std::string()>& ext_inst_name,
bool allow_template_param) {
- std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
- [&allow_template_param](OpenCLDebugInfo100Instructions dbg_inst) {
+ std::function<bool(CommonDebugInfoInstructions)> expectation =
+ [&allow_template_param](CommonDebugInfoInstructions dbg_inst) {
if (allow_template_param &&
- (dbg_inst == OpenCLDebugInfo100DebugTypeTemplateParameter ||
- dbg_inst ==
- OpenCLDebugInfo100DebugTypeTemplateTemplateParameter)) {
+ (dbg_inst == CommonDebugInfoDebugTypeTemplateParameter ||
+ dbg_inst == CommonDebugInfoDebugTypeTemplateTemplateParameter)) {
return true;
}
- return OpenCLDebugInfo100DebugTypeBasic <= dbg_inst &&
- dbg_inst <= OpenCLDebugInfo100DebugTypeTemplate;
+ return CommonDebugInfoDebugTypeBasic <= dbg_inst &&
+ dbg_inst <= CommonDebugInfoDebugTypeTemplate;
};
if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
return SPV_SUCCESS;
@@ -191,28 +242,6 @@ spv_result_t ValidateOperandDebugType(
<< " is not a valid debug type";
}
-bool IsUint32Constant(ValidationState_t& _, uint32_t id) {
- auto inst = _.FindDef(id);
- if (!inst || inst->opcode() != SpvOpConstant) {
- return false;
- }
-
- auto type = _.FindDef(inst->type_id());
- if (!type || type->opcode() != SpvOpTypeInt) {
- return false;
- }
-
- if (type->GetOperandAs<uint32_t>(1) != 32) {
- return false;
- }
-
- if (type->GetOperandAs<uint32_t>(2) != 0) {
- return false;
- }
-
- return true;
-}
-
spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _,
const Instruction* inst) {
const auto kernel_id = inst->GetOperandAs<uint32_t>(4);
@@ -251,8 +280,7 @@ spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _,
return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString";
}
- const std::string name_str = reinterpret_cast<const char*>(
- name->words().data() + name->operands()[1].offset);
+ const std::string name_str = name->GetOperandAs<std::string>(1);
bool found = false;
for (auto& desc : _.entry_point_descriptions(kernel_id)) {
if (name_str == desc.name) {
@@ -667,18 +695,26 @@ bool IsDebugVariableWithIntScalarType(ValidationState_t& _,
const Instruction* inst,
uint32_t word_index) {
auto* dbg_int_scalar_var = _.FindDef(inst->word(word_index));
- if (OpenCLDebugInfo100Instructions(dbg_int_scalar_var->word(4)) ==
- OpenCLDebugInfo100DebugLocalVariable ||
- OpenCLDebugInfo100Instructions(dbg_int_scalar_var->word(4)) ==
- OpenCLDebugInfo100DebugGlobalVariable) {
+ if (CommonDebugInfoInstructions(dbg_int_scalar_var->word(4)) ==
+ CommonDebugInfoDebugLocalVariable ||
+ CommonDebugInfoInstructions(dbg_int_scalar_var->word(4)) ==
+ CommonDebugInfoDebugGlobalVariable) {
auto* dbg_type = _.FindDef(dbg_int_scalar_var->word(6));
- if (OpenCLDebugInfo100Instructions(dbg_type->word(4)) ==
- OpenCLDebugInfo100DebugTypeBasic &&
- (OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(dbg_type->word(7)) ==
- OpenCLDebugInfo100Signed ||
- OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(dbg_type->word(7)) ==
- OpenCLDebugInfo100Unsigned)) {
- return true;
+ if (CommonDebugInfoInstructions(dbg_type->word(4)) ==
+ CommonDebugInfoDebugTypeBasic) {
+ const spv_ext_inst_type_t ext_inst_type =
+ spv_ext_inst_type_t(inst->ext_inst_type());
+ const bool vulkanDebugInfo =
+ ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100;
+ uint32_t encoding = dbg_type->word(7);
+ if (!vulkanDebugInfo || IsUint32Constant(_, encoding)) {
+ auto ocl_encoding = OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(
+ vulkanDebugInfo ? GetUint32Constant(_, encoding) : encoding);
+ if (ocl_encoding == OpenCLDebugInfo100Signed ||
+ ocl_encoding == OpenCLDebugInfo100Unsigned) {
+ return true;
+ }
+ }
}
}
return false;
@@ -703,9 +739,9 @@ spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
spv_result_t ValidateExtInstImport(ValidationState_t& _,
const Instruction* inst) {
const auto name_id = 1;
- if (!_.HasExtension(kSPV_KHR_non_semantic_info)) {
- const std::string name(reinterpret_cast<const char*>(
- inst->words().data() + inst->operands()[name_id].offset));
+ if (_.version() <= SPV_SPIRV_VERSION_WORD(1, 5) &&
+ !_.HasExtension(kSPV_KHR_non_semantic_info)) {
+ const std::string name = inst->GetOperandAs<std::string>(name_id);
if (name.find("NonSemantic.") == 0) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "NonSemantic extended instruction sets cannot be declared "
@@ -737,7 +773,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
assert(import_inst);
std::ostringstream ss;
- ss << reinterpret_cast<const char*>(import_inst->words().data() + 2);
+ ss << import_inst->GetOperandAs<std::string>(1);
ss << " ";
ss << desc->name;
@@ -812,7 +848,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
for (uint32_t operand_index = 4; operand_index < num_operands;
++operand_index) {
const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
- if (!_.IsIntScalarOrVectorType(operand_type)) {
+ if (!operand_type || !_.IsIntScalarOrVectorType(operand_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected all operands to be int scalars or vectors";
@@ -2668,7 +2704,9 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
break;
}
}
- } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+ } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
+ ext_inst_type ==
+ SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
if (!_.IsVoidType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
@@ -2676,451 +2714,556 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
<< "OpTypeVoid";
}
- auto num_words = inst->words().size();
+ const bool vulkanDebugInfo =
+ ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100;
- const OpenCLDebugInfo100Instructions ext_inst_key =
- OpenCLDebugInfo100Instructions(ext_inst_index);
- switch (ext_inst_key) {
- case OpenCLDebugInfo100DebugInfoNone:
- case OpenCLDebugInfo100DebugNoScope:
- case OpenCLDebugInfo100DebugOperation:
- // The binary parser validates the opcode for DebugInfoNone,
- // DebugNoScope, DebugOperation, and the literal values don't need
- // further checks.
- break;
- case OpenCLDebugInfo100DebugCompilationUnit: {
- CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
- break;
- }
- case OpenCLDebugInfo100DebugSource: {
- CHECK_OPERAND("File", SpvOpString, 5);
- if (num_words == 7) CHECK_OPERAND("Text", SpvOpString, 6);
- break;
- }
- case OpenCLDebugInfo100DebugTypeBasic: {
- CHECK_OPERAND("Name", SpvOpString, 5);
- CHECK_OPERAND("Size", SpvOpConstant, 6);
- // "Encoding" param is already validated by the binary parsing stage.
- break;
- }
- case OpenCLDebugInfo100DebugTypePointer:
- case OpenCLDebugInfo100DebugTypeQualifier: {
- auto validate_base_type =
- ValidateOperandBaseType(_, inst, 5, ext_inst_name);
- if (validate_base_type != SPV_SUCCESS) return validate_base_type;
- break;
- }
- case OpenCLDebugInfo100DebugTypeVector: {
- auto validate_base_type =
- ValidateOperandBaseType(_, inst, 5, ext_inst_name);
- if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+ auto num_words = inst->words().size();
- uint32_t component_count = inst->word(6);
- if (!component_count || component_count > 4) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << ext_inst_name() << ": Component Count must be positive "
- << "integer less than or equal to 4";
- }
- break;
- }
- case OpenCLDebugInfo100DebugTypeArray: {
- auto validate_base_type = ValidateOperandDebugType(
- _, "Base Type", inst, 5, ext_inst_name, false);
- if (validate_base_type != SPV_SUCCESS) return validate_base_type;
- for (uint32_t i = 6; i < num_words; ++i) {
- 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)) {
- invalid = true;
+ // Handle any non-common OpenCL insts, then common
+ if (ext_inst_type != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
+ OpenCLDebugInfo100Instructions(ext_inst_index) !=
+ OpenCLDebugInfo100DebugModuleINTEL) {
+ const CommonDebugInfoInstructions ext_inst_key =
+ CommonDebugInfoInstructions(ext_inst_index);
+ switch (ext_inst_key) {
+ case CommonDebugInfoDebugInfoNone:
+ case CommonDebugInfoDebugNoScope:
+ break;
+ // The binary parser validates the opcode for DebugInfoNone,
+ // DebugNoScope, DebugOperation. We just check the parameters to
+ // DebugOperation are properly constants for vulkan debug info.
+ case CommonDebugInfoDebugOperation: {
+ CHECK_CONST_UINT_OPERAND("Operation", 5);
+ for (uint32_t i = 6; i < num_words; ++i) {
+ CHECK_CONST_UINT_OPERAND("Operand", i);
+ }
+ break;
+ }
+ case CommonDebugInfoDebugCompilationUnit: {
+ CHECK_CONST_UINT_OPERAND("Version", 5);
+ CHECK_CONST_UINT_OPERAND("DWARF Version", 6);
+ CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+ CHECK_CONST_UINT_OPERAND("Language", 8);
+ break;
+ }
+ case CommonDebugInfoDebugSource: {
+ CHECK_OPERAND("File", SpvOpString, 5);
+ if (num_words == 7) CHECK_OPERAND("Text", SpvOpString, 6);
+ break;
+ }
+ case CommonDebugInfoDebugTypeBasic: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ CHECK_OPERAND("Size", SpvOpConstant, 6);
+ CHECK_CONST_UINT_OPERAND("Encoding", 7);
+ break;
+ }
+ case CommonDebugInfoDebugTypePointer: {
+ auto validate_base_type =
+ ValidateOperandBaseType(_, inst, 5, ext_inst_name);
+ if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+ CHECK_CONST_UINT_OPERAND("Storage Class", 6);
+ CHECK_CONST_UINT_OPERAND("Flags", 7);
+ break;
+ }
+ case CommonDebugInfoDebugTypeQualifier: {
+ auto validate_base_type =
+ ValidateOperandBaseType(_, inst, 5, ext_inst_name);
+ if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+ CHECK_CONST_UINT_OPERAND("Type Qualifier", 6);
+ break;
+ }
+ case CommonDebugInfoDebugTypeVector: {
+ auto validate_base_type =
+ ValidateOperandBaseType(_, inst, 5, ext_inst_name);
+ if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+
+ CHECK_CONST_UINT_OPERAND("Component Count", 6);
+ uint32_t component_count = inst->word(6);
+ if (vulkanDebugInfo) {
+ uint64_t const_val;
+ if (!_.GetConstantValUint64(component_count, &const_val)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name()
+ << ": Component Count must be 32-bit integer OpConstant";
}
- } else if (component_count->words().size() > 6 &&
- (OpenCLDebugInfo100Instructions(component_count->word(
- 4)) == OpenCLDebugInfo100DebugLocalVariable ||
- OpenCLDebugInfo100Instructions(component_count->word(
- 4)) == OpenCLDebugInfo100DebugGlobalVariable)) {
- auto* component_count_type = _.FindDef(component_count->word(6));
- if (component_count_type->words().size() > 7) {
- if (OpenCLDebugInfo100Instructions(component_count_type->word(
- 4)) != OpenCLDebugInfo100DebugTypeBasic ||
- OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(
- component_count_type->word(7)) !=
- OpenCLDebugInfo100Unsigned) {
+ component_count = const_val & 0xffffffff;
+ }
+
+ if (!component_count || component_count > 4) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": Component Count must be positive "
+ << "integer less than or equal to 4";
+ }
+ break;
+ }
+ case CommonDebugInfoDebugTypeArray: {
+ auto validate_base_type = ValidateOperandDebugType(
+ _, "Base Type", inst, 5, ext_inst_name, false);
+ if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+ for (uint32_t i = 6; i < num_words; ++i) {
+ 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)) {
invalid = true;
- } else {
- // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable
- // must have Unsigned encoding and 32 or 64 as its size in bits.
- Instruction* size_in_bits =
- _.FindDef(component_count_type->word(6));
- if (!_.IsIntScalarType(size_in_bits->type_id()) ||
- (size_in_bits->word(3) != 32 &&
- size_in_bits->word(3) != 64)) {
+ }
+ } else if (component_count->words().size() > 6 &&
+ (CommonDebugInfoInstructions(component_count->word(4)) ==
+ CommonDebugInfoDebugLocalVariable ||
+ CommonDebugInfoInstructions(component_count->word(4)) ==
+ CommonDebugInfoDebugGlobalVariable)) {
+ auto* component_count_type = _.FindDef(component_count->word(6));
+ if (component_count_type->words().size() > 7) {
+ uint32_t encoding = component_count_type->word(7);
+ if (CommonDebugInfoInstructions(component_count_type->word(
+ 4)) != CommonDebugInfoDebugTypeBasic ||
+ (vulkanDebugInfo && !IsUint32Constant(_, encoding)) ||
+ OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(
+ vulkanDebugInfo
+ ? GetUint32Constant(_, encoding)
+ : encoding) != OpenCLDebugInfo100Unsigned) {
invalid = true;
+ } else {
+ // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable
+ // must have Unsigned encoding and 32 or 64 as its size in
+ // bits.
+ Instruction* size_in_bits =
+ _.FindDef(component_count_type->word(6));
+ if (!_.IsIntScalarType(size_in_bits->type_id()) ||
+ (size_in_bits->word(3) != 32 &&
+ size_in_bits->word(3) != 64)) {
+ invalid = true;
+ }
}
+ } else {
+ invalid = true;
}
} else {
invalid = true;
}
- } else {
- invalid = true;
+ if (invalid) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": 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";
+ }
}
- if (invalid) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << ext_inst_name() << ": 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";
+ break;
+ }
+ case CommonDebugInfoDebugTypedef: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ auto validate_base_type =
+ ValidateOperandBaseType(_, inst, 6, ext_inst_name);
+ if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+ CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+ CHECK_CONST_UINT_OPERAND("Line", 8);
+ CHECK_CONST_UINT_OPERAND("Column", 9);
+ auto validate_parent =
+ ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+ if (validate_parent != SPV_SUCCESS) return validate_parent;
+ break;
+ }
+ case CommonDebugInfoDebugTypeFunction: {
+ CHECK_CONST_UINT_OPERAND("Flags", 5);
+ auto* return_type = _.FindDef(inst->word(6));
+ // TODO: We need a spec discussion that we have to allow return and
+ // parameter types of a DebugTypeFunction to have template parameter.
+ if (return_type->opcode() != SpvOpTypeVoid) {
+ auto validate_return = ValidateOperandDebugType(
+ _, "Return Type", inst, 6, ext_inst_name, true);
+ if (validate_return != SPV_SUCCESS) return validate_return;
}
+ for (uint32_t word_index = 7; word_index < num_words; ++word_index) {
+ auto validate_param = ValidateOperandDebugType(
+ _, "Parameter Types", inst, word_index, ext_inst_name, true);
+ if (validate_param != SPV_SUCCESS) return validate_param;
+ }
+ break;
}
- break;
- }
- case OpenCLDebugInfo100DebugTypedef: {
- CHECK_OPERAND("Name", SpvOpString, 5);
- auto validate_base_type =
- ValidateOperandBaseType(_, inst, 6, ext_inst_name);
- if (validate_base_type != SPV_SUCCESS) return validate_base_type;
- CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
- auto validate_parent =
- ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
- if (validate_parent != SPV_SUCCESS) return validate_parent;
- break;
- }
- case OpenCLDebugInfo100DebugTypeFunction: {
- auto* return_type = _.FindDef(inst->word(6));
- // TODO: We need a spec discussion that we have to allow return and
- // parameter types of a DebugTypeFunction to have template parameter.
- if (return_type->opcode() != SpvOpTypeVoid) {
- auto validate_return = ValidateOperandDebugType(
- _, "Return Type", inst, 6, ext_inst_name, true);
- if (validate_return != SPV_SUCCESS) return validate_return;
- }
- for (uint32_t word_index = 7; word_index < num_words; ++word_index) {
- auto validate_param = ValidateOperandDebugType(
- _, "Parameter Types", inst, word_index, ext_inst_name, true);
- if (validate_param != SPV_SUCCESS) return validate_param;
- }
- break;
- }
- case OpenCLDebugInfo100DebugTypeEnum: {
- CHECK_OPERAND("Name", SpvOpString, 5);
- if (!DoesDebugInfoOperandMatchExpectation(
- _,
- [](OpenCLDebugInfo100Instructions dbg_inst) {
- return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
- },
- inst, 6)) {
- auto validate_underlying_type = ValidateOperandDebugType(
- _, "Underlying Types", inst, 6, ext_inst_name, false);
- if (validate_underlying_type != SPV_SUCCESS)
- return validate_underlying_type;
- }
- CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
- auto validate_parent =
- ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
- if (validate_parent != SPV_SUCCESS) return validate_parent;
- CHECK_OPERAND("Size", SpvOpConstant, 11);
- auto* size = _.FindDef(inst->word(11));
- if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << ext_inst_name() << ": expected operand Size is a "
- << "positive integer";
- }
- for (uint32_t word_index = 13; word_index + 1 < num_words;
- word_index += 2) {
- CHECK_OPERAND("Value", SpvOpConstant, word_index);
- CHECK_OPERAND("Name", SpvOpString, word_index + 1);
- }
- break;
- }
- case OpenCLDebugInfo100DebugTypeComposite: {
- CHECK_OPERAND("Name", SpvOpString, 5);
- CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
- auto validate_parent =
- ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
- if (validate_parent != SPV_SUCCESS) return validate_parent;
- CHECK_OPERAND("Linkage Name", SpvOpString, 11);
- if (!DoesDebugInfoOperandMatchExpectation(
- _,
- [](OpenCLDebugInfo100Instructions dbg_inst) {
- return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
- },
- inst, 12)) {
- CHECK_OPERAND("Size", SpvOpConstant, 12);
- }
- for (uint32_t word_index = 14; word_index < num_words; ++word_index) {
+ case CommonDebugInfoDebugTypeEnum: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
if (!DoesDebugInfoOperandMatchExpectation(
_,
- [](OpenCLDebugInfo100Instructions dbg_inst) {
- return dbg_inst == OpenCLDebugInfo100DebugTypeMember ||
- dbg_inst == OpenCLDebugInfo100DebugFunction ||
- dbg_inst == OpenCLDebugInfo100DebugTypeInheritance;
+ [](CommonDebugInfoInstructions dbg_inst) {
+ return dbg_inst == CommonDebugInfoDebugInfoNone;
},
- inst, word_index)) {
+ inst, 6)) {
+ auto validate_underlying_type = ValidateOperandDebugType(
+ _, "Underlying Types", inst, 6, ext_inst_name, false);
+ if (validate_underlying_type != SPV_SUCCESS)
+ return validate_underlying_type;
+ }
+ CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+ CHECK_CONST_UINT_OPERAND("Line", 8);
+ CHECK_CONST_UINT_OPERAND("Column", 9);
+ auto validate_parent =
+ ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+ if (validate_parent != SPV_SUCCESS) return validate_parent;
+ CHECK_OPERAND("Size", SpvOpConstant, 11);
+ auto* size = _.FindDef(inst->word(11));
+ if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << ext_inst_name() << ": "
- << "expected operand Members "
- << "must be DebugTypeMember, DebugFunction, or "
- "DebugTypeInheritance";
+ << ext_inst_name() << ": expected operand Size is a "
+ << "positive integer";
}
- }
- break;
- }
- case OpenCLDebugInfo100DebugTypeMember: {
- CHECK_OPERAND("Name", SpvOpString, 5);
- // TODO: We need a spec discussion that we have to allow member types
- // to have template parameter.
- auto validate_type =
- ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true);
- if (validate_type != SPV_SUCCESS) return validate_type;
- CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
- CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 10);
- CHECK_OPERAND("Offset", SpvOpConstant, 11);
- CHECK_OPERAND("Size", SpvOpConstant, 12);
- if (num_words == 15) CHECK_OPERAND("Value", SpvOpConstant, 14);
- break;
- }
- case OpenCLDebugInfo100DebugTypeInheritance: {
- CHECK_DEBUG_OPERAND("Child", OpenCLDebugInfo100DebugTypeComposite, 5);
- auto* debug_inst = _.FindDef(inst->word(5));
- auto composite_type =
- OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
- if (composite_type != OpenCLDebugInfo100Class &&
- composite_type != OpenCLDebugInfo100Structure) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << ext_inst_name() << ": "
- << "expected operand Child must be class or struct debug type";
- }
- CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 6);
- debug_inst = _.FindDef(inst->word(6));
- composite_type =
- OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
- if (composite_type != OpenCLDebugInfo100Class &&
- composite_type != OpenCLDebugInfo100Structure) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << ext_inst_name() << ": "
- << "expected operand Parent must be class or struct debug "
- "type";
- }
- CHECK_OPERAND("Offset", SpvOpConstant, 7);
- CHECK_OPERAND("Size", SpvOpConstant, 8);
- break;
- }
- case OpenCLDebugInfo100DebugFunction: {
- CHECK_OPERAND("Name", SpvOpString, 5);
- auto validate_type =
- ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false);
- if (validate_type != SPV_SUCCESS) return validate_type;
- CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
- auto validate_parent =
- ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
- if (validate_parent != SPV_SUCCESS) return validate_parent;
- CHECK_OPERAND("Linkage Name", SpvOpString, 11);
- if (!DoesDebugInfoOperandMatchExpectation(
- _,
- [](OpenCLDebugInfo100Instructions dbg_inst) {
- return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
- },
- inst, 14)) {
- CHECK_OPERAND("Function", SpvOpFunction, 14);
- }
- if (num_words == 16) {
- CHECK_DEBUG_OPERAND("Declaration",
- OpenCLDebugInfo100DebugFunctionDeclaration, 15);
- }
- break;
- }
- case OpenCLDebugInfo100DebugFunctionDeclaration: {
- CHECK_OPERAND("Name", SpvOpString, 5);
- auto validate_type =
- ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false);
- if (validate_type != SPV_SUCCESS) return validate_type;
- CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
- auto validate_parent =
- ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
- if (validate_parent != SPV_SUCCESS) return validate_parent;
- CHECK_OPERAND("Linkage Name", SpvOpString, 11);
- break;
- }
- case OpenCLDebugInfo100DebugLexicalBlock: {
- CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 5);
- auto validate_parent =
- ValidateOperandLexicalScope(_, "Parent", inst, 8, ext_inst_name);
- if (validate_parent != SPV_SUCCESS) return validate_parent;
- if (num_words == 10) CHECK_OPERAND("Name", SpvOpString, 9);
- break;
- }
- case OpenCLDebugInfo100DebugScope: {
- auto validate_scope =
- ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name);
- if (validate_scope != SPV_SUCCESS) return validate_scope;
- if (num_words == 7) {
- CHECK_DEBUG_OPERAND("Inlined At", OpenCLDebugInfo100DebugInlinedAt,
- 6);
- }
- break;
- }
- case OpenCLDebugInfo100DebugLocalVariable: {
- CHECK_OPERAND("Name", SpvOpString, 5);
- // TODO: We need a spec discussion that we have to allow local variable
- // types to have template parameter.
- auto validate_type =
- ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true);
- if (validate_type != SPV_SUCCESS) return validate_type;
- CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
- auto validate_parent =
- ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
- if (validate_parent != SPV_SUCCESS) return validate_parent;
- break;
- }
- case OpenCLDebugInfo100DebugDeclare: {
- CHECK_DEBUG_OPERAND("Local Variable",
- OpenCLDebugInfo100DebugLocalVariable, 5);
- auto* operand = _.FindDef(inst->word(6));
- if (operand->opcode() != SpvOpVariable &&
- operand->opcode() != SpvOpFunctionParameter) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << ext_inst_name() << ": "
- << "expected operand Variable must be a result id of "
- "OpVariable or OpFunctionParameter";
- }
-
- CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7);
- break;
- }
- case OpenCLDebugInfo100DebugExpression: {
- for (uint32_t word_index = 5; word_index < num_words; ++word_index) {
- CHECK_DEBUG_OPERAND("Operation", OpenCLDebugInfo100DebugOperation,
- word_index);
- }
- break;
- }
- case OpenCLDebugInfo100DebugTypeTemplate: {
- if (!DoesDebugInfoOperandMatchExpectation(
- _,
- [](OpenCLDebugInfo100Instructions dbg_inst) {
- return dbg_inst == OpenCLDebugInfo100DebugTypeComposite ||
- dbg_inst == OpenCLDebugInfo100DebugFunction;
- },
- inst, 5)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << ext_inst_name() << ": "
- << "expected operand Target must be DebugTypeComposite "
- << "or DebugFunction";
- }
- for (uint32_t word_index = 6; word_index < num_words; ++word_index) {
+ CHECK_CONST_UINT_OPERAND("Flags", 12);
+ for (uint32_t word_index = 13; word_index + 1 < num_words;
+ word_index += 2) {
+ CHECK_OPERAND("Value", SpvOpConstant, word_index);
+ CHECK_OPERAND("Name", SpvOpString, word_index + 1);
+ }
+ break;
+ }
+ case CommonDebugInfoDebugTypeComposite: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+ CHECK_CONST_UINT_OPERAND("Line", 8);
+ CHECK_CONST_UINT_OPERAND("Column", 9);
+ auto validate_parent =
+ ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+ if (validate_parent != SPV_SUCCESS) return validate_parent;
+ CHECK_OPERAND("Linkage Name", SpvOpString, 11);
if (!DoesDebugInfoOperandMatchExpectation(
_,
- [](OpenCLDebugInfo100Instructions dbg_inst) {
- return dbg_inst ==
- OpenCLDebugInfo100DebugTypeTemplateParameter ||
- dbg_inst ==
- OpenCLDebugInfo100DebugTypeTemplateTemplateParameter;
+ [](CommonDebugInfoInstructions dbg_inst) {
+ return dbg_inst == CommonDebugInfoDebugInfoNone;
},
- inst, word_index)) {
+ inst, 12)) {
+ CHECK_OPERAND("Size", SpvOpConstant, 12);
+ }
+ CHECK_CONST_UINT_OPERAND("Flags", 13);
+ for (uint32_t word_index = 14; word_index < num_words; ++word_index) {
+ if (!DoesDebugInfoOperandMatchExpectation(
+ _,
+ [](CommonDebugInfoInstructions dbg_inst) {
+ return dbg_inst == CommonDebugInfoDebugTypeMember ||
+ dbg_inst == CommonDebugInfoDebugFunction ||
+ dbg_inst == CommonDebugInfoDebugTypeInheritance;
+ },
+ inst, word_index)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": "
+ << "expected operand Members "
+ << "must be DebugTypeMember, DebugFunction, or "
+ "DebugTypeInheritance";
+ }
+ }
+ break;
+ }
+ case CommonDebugInfoDebugTypeMember: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ // TODO: We need a spec discussion that we have to allow member types
+ // to have template parameter.
+ auto validate_type =
+ ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true);
+ if (validate_type != SPV_SUCCESS) return validate_type;
+ CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+ CHECK_CONST_UINT_OPERAND("Line", 8);
+ CHECK_CONST_UINT_OPERAND("Column", 9);
+ // NonSemantic.Shader.DebugInfo doesn't have the Parent operand
+ if (vulkanDebugInfo) {
+ CHECK_OPERAND("Offset", SpvOpConstant, 10);
+ CHECK_OPERAND("Size", SpvOpConstant, 11);
+ CHECK_CONST_UINT_OPERAND("Flags", 12);
+ if (num_words == 14) CHECK_OPERAND("Value", SpvOpConstant, 13);
+ } else {
+ CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite,
+ 10);
+ CHECK_OPERAND("Offset", SpvOpConstant, 11);
+ CHECK_OPERAND("Size", SpvOpConstant, 12);
+ CHECK_CONST_UINT_OPERAND("Flags", 13);
+ if (num_words == 15) CHECK_OPERAND("Value", SpvOpConstant, 14);
+ }
+ break;
+ }
+ case CommonDebugInfoDebugTypeInheritance: {
+ CHECK_DEBUG_OPERAND("Child", CommonDebugInfoDebugTypeComposite, 5);
+ auto* debug_inst = _.FindDef(inst->word(5));
+ auto composite_type =
+ OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
+ if (composite_type != OpenCLDebugInfo100Class &&
+ composite_type != OpenCLDebugInfo100Structure) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": "
+ << "expected operand Child must be class or struct debug "
+ "type";
+ }
+ CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, 6);
+ debug_inst = _.FindDef(inst->word(6));
+ composite_type =
+ OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
+ if (composite_type != OpenCLDebugInfo100Class &&
+ composite_type != OpenCLDebugInfo100Structure) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
- << "expected operand Parameters must be "
- << "DebugTypeTemplateParameter or "
- << "DebugTypeTemplateTemplateParameter";
+ << "expected operand Parent must be class or struct debug "
+ "type";
}
+ CHECK_OPERAND("Offset", SpvOpConstant, 7);
+ CHECK_OPERAND("Size", SpvOpConstant, 8);
+ CHECK_CONST_UINT_OPERAND("Flags", 9);
+ break;
+ }
+ case CommonDebugInfoDebugFunction: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6,
+ ext_inst_name, false);
+ if (validate_type != SPV_SUCCESS) return validate_type;
+ CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+ CHECK_CONST_UINT_OPERAND("Line", 8);
+ CHECK_CONST_UINT_OPERAND("Column", 9);
+ auto validate_parent =
+ ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+ if (validate_parent != SPV_SUCCESS) return validate_parent;
+ CHECK_OPERAND("Linkage Name", SpvOpString, 11);
+ CHECK_CONST_UINT_OPERAND("Flags", 12);
+ CHECK_CONST_UINT_OPERAND("Scope Line", 13);
+ // NonSemantic.Shader.DebugInfo.100 doesn't include a reference to the
+ // OpFunction
+ if (vulkanDebugInfo) {
+ if (num_words == 15) {
+ CHECK_DEBUG_OPERAND("Declaration",
+ CommonDebugInfoDebugFunctionDeclaration, 14);
+ }
+ } else {
+ if (!DoesDebugInfoOperandMatchExpectation(
+ _,
+ [](CommonDebugInfoInstructions dbg_inst) {
+ return dbg_inst == CommonDebugInfoDebugInfoNone;
+ },
+ inst, 14)) {
+ CHECK_OPERAND("Function", SpvOpFunction, 14);
+ }
+ if (num_words == 16) {
+ CHECK_DEBUG_OPERAND("Declaration",
+ CommonDebugInfoDebugFunctionDeclaration, 15);
+ }
+ }
+ break;
+ }
+ case CommonDebugInfoDebugFunctionDeclaration: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6,
+ ext_inst_name, false);
+ if (validate_type != SPV_SUCCESS) return validate_type;
+ CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+ CHECK_CONST_UINT_OPERAND("Line", 8);
+ CHECK_CONST_UINT_OPERAND("Column", 9);
+ auto validate_parent =
+ ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+ if (validate_parent != SPV_SUCCESS) return validate_parent;
+ CHECK_OPERAND("Linkage Name", SpvOpString, 11);
+ CHECK_CONST_UINT_OPERAND("Flags", 12);
+ break;
+ }
+ case CommonDebugInfoDebugLexicalBlock: {
+ CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5);
+ CHECK_CONST_UINT_OPERAND("Line", 6);
+ CHECK_CONST_UINT_OPERAND("Column", 7);
+ auto validate_parent =
+ ValidateOperandLexicalScope(_, "Parent", inst, 8, ext_inst_name);
+ if (validate_parent != SPV_SUCCESS) return validate_parent;
+ if (num_words == 10) CHECK_OPERAND("Name", SpvOpString, 9);
+ break;
+ }
+ case CommonDebugInfoDebugScope: {
+ auto validate_scope =
+ ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name);
+ if (validate_scope != SPV_SUCCESS) return validate_scope;
+ if (num_words == 7) {
+ CHECK_DEBUG_OPERAND("Inlined At", CommonDebugInfoDebugInlinedAt, 6);
+ }
+ break;
+ }
+ case CommonDebugInfoDebugLocalVariable: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ // TODO: We need a spec discussion that we have to allow local
+ // variable types to have template parameter.
+ auto validate_type =
+ ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true);
+ if (validate_type != SPV_SUCCESS) return validate_type;
+ CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+ CHECK_CONST_UINT_OPERAND("Line", 8);
+ CHECK_CONST_UINT_OPERAND("Column", 9);
+ auto validate_parent =
+ ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+ if (validate_parent != SPV_SUCCESS) return validate_parent;
+ CHECK_CONST_UINT_OPERAND("Flags", 11);
+ if (num_words == 13) {
+ CHECK_CONST_UINT_OPERAND("ArgNumber", 12);
+ }
+ break;
}
- break;
- }
- case OpenCLDebugInfo100DebugTypeTemplateParameter: {
- CHECK_OPERAND("Name", SpvOpString, 5);
- auto validate_actual_type = ValidateOperandDebugType(
- _, "Actual Type", inst, 6, ext_inst_name, false);
- if (validate_actual_type != SPV_SUCCESS) return validate_actual_type;
- if (!DoesDebugInfoOperandMatchExpectation(
- _,
- [](OpenCLDebugInfo100Instructions dbg_inst) {
- return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
- },
- inst, 7)) {
- CHECK_OPERAND("Value", SpvOpConstant, 7);
- }
- CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 8);
- break;
- }
- case OpenCLDebugInfo100DebugGlobalVariable: {
- CHECK_OPERAND("Name", SpvOpString, 5);
- auto validate_type =
- ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false);
- if (validate_type != SPV_SUCCESS) return validate_type;
- CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
- auto validate_scope =
- ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name);
- if (validate_scope != SPV_SUCCESS) return validate_scope;
- CHECK_OPERAND("Linkage Name", SpvOpString, 11);
- if (!DoesDebugInfoOperandMatchExpectation(
- _,
- [](OpenCLDebugInfo100Instructions dbg_inst) {
- return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
- },
- inst, 12)) {
- auto* operand = _.FindDef(inst->word(12));
+ case CommonDebugInfoDebugDeclare: {
+ CHECK_DEBUG_OPERAND("Local Variable",
+ CommonDebugInfoDebugLocalVariable, 5);
+ auto* operand = _.FindDef(inst->word(6));
if (operand->opcode() != SpvOpVariable &&
- operand->opcode() != SpvOpConstant) {
+ operand->opcode() != SpvOpFunctionParameter) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand Variable must be a result id of "
- "OpVariable or OpConstant or DebugInfoNone";
+ "OpVariable or OpFunctionParameter";
}
+
+ CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7);
+
+ if (vulkanDebugInfo) {
+ for (uint32_t word_index = 8; word_index < num_words;
+ ++word_index) {
+ auto index_inst = _.FindDef(inst->word(word_index));
+ auto type_id = index_inst != nullptr ? index_inst->type_id() : 0;
+ if (type_id == 0 || !IsIntScalar(_, type_id, false, false))
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": "
+ << "expected index must be scalar integer";
+ }
+ }
+ break;
}
- if (num_words == 15) {
- CHECK_DEBUG_OPERAND("Static Member Declaration",
- OpenCLDebugInfo100DebugTypeMember, 14);
- }
- break;
- }
- case OpenCLDebugInfo100DebugInlinedAt: {
- auto validate_scope =
- ValidateOperandLexicalScope(_, "Scope", inst, 6, ext_inst_name);
- if (validate_scope != SPV_SUCCESS) return validate_scope;
- if (num_words == 8) {
- CHECK_DEBUG_OPERAND("Inlined", OpenCLDebugInfo100DebugInlinedAt, 7);
+ case CommonDebugInfoDebugExpression: {
+ for (uint32_t word_index = 5; word_index < num_words; ++word_index) {
+ CHECK_DEBUG_OPERAND("Operation", CommonDebugInfoDebugOperation,
+ word_index);
+ }
+ break;
}
- break;
- }
- case OpenCLDebugInfo100DebugValue: {
- CHECK_DEBUG_OPERAND("Local Variable",
- OpenCLDebugInfo100DebugLocalVariable, 5);
- CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7);
-
- for (uint32_t word_index = 8; word_index < num_words; ++word_index) {
- // TODO: The following code simply checks if it is a const int scalar
- // or a DebugLocalVariable or DebugGlobalVariable, but we have to
- // check it using the same validation for Indexes of OpAccessChain.
- if (!IsConstWithIntScalarType(_, inst, word_index) &&
- !IsDebugVariableWithIntScalarType(_, inst, word_index)) {
+ case CommonDebugInfoDebugTypeTemplate: {
+ if (!DoesDebugInfoOperandMatchExpectation(
+ _,
+ [](CommonDebugInfoInstructions dbg_inst) {
+ return dbg_inst == CommonDebugInfoDebugTypeComposite ||
+ dbg_inst == CommonDebugInfoDebugFunction;
+ },
+ inst, 5)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << ext_inst_name() << ": expected operand Indexes is "
- << "OpConstant, DebugGlobalVariable, or "
- << "type is OpConstant with an integer scalar type";
+ << ext_inst_name() << ": "
+ << "expected operand Target must be DebugTypeComposite "
+ << "or DebugFunction";
+ }
+ for (uint32_t word_index = 6; word_index < num_words; ++word_index) {
+ if (!DoesDebugInfoOperandMatchExpectation(
+ _,
+ [](CommonDebugInfoInstructions dbg_inst) {
+ return dbg_inst ==
+ CommonDebugInfoDebugTypeTemplateParameter ||
+ dbg_inst ==
+ CommonDebugInfoDebugTypeTemplateTemplateParameter;
+ },
+ inst, word_index)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": "
+ << "expected operand Parameters must be "
+ << "DebugTypeTemplateParameter or "
+ << "DebugTypeTemplateTemplateParameter";
+ }
}
+ break;
}
- break;
+ case CommonDebugInfoDebugTypeTemplateParameter: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ auto validate_actual_type = ValidateOperandDebugType(
+ _, "Actual Type", inst, 6, ext_inst_name, false);
+ if (validate_actual_type != SPV_SUCCESS) return validate_actual_type;
+ if (!DoesDebugInfoOperandMatchExpectation(
+ _,
+ [](CommonDebugInfoInstructions dbg_inst) {
+ return dbg_inst == CommonDebugInfoDebugInfoNone;
+ },
+ inst, 7)) {
+ CHECK_OPERAND("Value", SpvOpConstant, 7);
+ }
+ CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 8);
+ CHECK_CONST_UINT_OPERAND("Line", 9);
+ CHECK_CONST_UINT_OPERAND("Column", 10);
+ break;
+ }
+ case CommonDebugInfoDebugGlobalVariable: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6,
+ ext_inst_name, false);
+ if (validate_type != SPV_SUCCESS) return validate_type;
+ CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+ CHECK_CONST_UINT_OPERAND("Line", 8);
+ CHECK_CONST_UINT_OPERAND("Column", 9);
+ auto validate_scope =
+ ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name);
+ if (validate_scope != SPV_SUCCESS) return validate_scope;
+ CHECK_OPERAND("Linkage Name", SpvOpString, 11);
+ if (!DoesDebugInfoOperandMatchExpectation(
+ _,
+ [](CommonDebugInfoInstructions dbg_inst) {
+ return dbg_inst == CommonDebugInfoDebugInfoNone;
+ },
+ inst, 12)) {
+ auto* operand = _.FindDef(inst->word(12));
+ if (operand->opcode() != SpvOpVariable &&
+ operand->opcode() != SpvOpConstant) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": "
+ << "expected operand Variable must be a result id of "
+ "OpVariable or OpConstant or DebugInfoNone";
+ }
+ }
+ if (num_words == 15) {
+ CHECK_DEBUG_OPERAND("Static Member Declaration",
+ CommonDebugInfoDebugTypeMember, 14);
+ }
+ break;
+ }
+ case CommonDebugInfoDebugInlinedAt: {
+ CHECK_CONST_UINT_OPERAND("Line", 5);
+ auto validate_scope =
+ ValidateOperandLexicalScope(_, "Scope", inst, 6, ext_inst_name);
+ if (validate_scope != SPV_SUCCESS) return validate_scope;
+ if (num_words == 8) {
+ CHECK_DEBUG_OPERAND("Inlined", CommonDebugInfoDebugInlinedAt, 7);
+ }
+ break;
+ }
+ case CommonDebugInfoDebugValue: {
+ CHECK_DEBUG_OPERAND("Local Variable",
+ CommonDebugInfoDebugLocalVariable, 5);
+ CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7);
+
+ for (uint32_t word_index = 8; word_index < num_words; ++word_index) {
+ // TODO: The following code simply checks if it is a const int
+ // scalar or a DebugLocalVariable or DebugGlobalVariable, but we
+ // have to check it using the same validation for Indexes of
+ // OpAccessChain.
+ if (!IsConstWithIntScalarType(_, inst, word_index) &&
+ !IsDebugVariableWithIntScalarType(_, inst, word_index)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": expected operand Indexes is "
+ << "OpConstant, DebugGlobalVariable, or "
+ << "type is OpConstant with an integer scalar type";
+ }
+ }
+ break;
+ }
+
+ // TODO: Add validation rules for remaining cases as well.
+ case CommonDebugInfoDebugTypePtrToMember:
+ case CommonDebugInfoDebugTypeTemplateTemplateParameter:
+ case CommonDebugInfoDebugTypeTemplateParameterPack:
+ case CommonDebugInfoDebugLexicalBlockDiscriminator:
+ case CommonDebugInfoDebugInlinedVariable:
+ case CommonDebugInfoDebugMacroDef:
+ case CommonDebugInfoDebugMacroUndef:
+ case CommonDebugInfoDebugImportedEntity:
+ break;
+ case CommonDebugInfoInstructionsMax:
+ assert(0);
+ break;
}
-
- // TODO: Add validation rules for remaining cases as well.
- case OpenCLDebugInfo100DebugTypePtrToMember:
- case OpenCLDebugInfo100DebugTypeTemplateTemplateParameter:
- case OpenCLDebugInfo100DebugTypeTemplateParameterPack:
- case OpenCLDebugInfo100DebugLexicalBlockDiscriminator:
- case OpenCLDebugInfo100DebugInlinedVariable:
- case OpenCLDebugInfo100DebugMacroDef:
- case OpenCLDebugInfo100DebugMacroUndef:
- case OpenCLDebugInfo100DebugImportedEntity:
- break;
- case OpenCLDebugInfo100InstructionsMax:
- assert(0);
- break;
}
} else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) {
auto import_inst = _.FindDef(inst->GetOperandAs<uint32_t>(2));
- const std::string name(reinterpret_cast<const char*>(
- import_inst->words().data() + import_inst->operands()[1].offset));
+ const std::string name = import_inst->GetOperandAs<std::string>(1);
const std::string reflection = "NonSemantic.ClspvReflection.";
char* end_ptr;
auto version_string = name.substr(reflection.size());
diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp
index e5968d06..b12d1e82 100644
--- a/source/val/validate_image.cpp
+++ b/source/val/validate_image.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Google Inc.
+// Copyright (c) 2017 Google Inc.
// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
// reserved.
//
@@ -20,6 +20,7 @@
#include "source/diagnostic.h"
#include "source/opcode.h"
+#include "source/spirv_constant.h"
#include "source/spirv_target_env.h"
#include "source/util/bitutils.h"
#include "source/val/instruction.h"
@@ -66,6 +67,12 @@ bool CheckAllImageOperandsHandled() {
case SpvImageOperandsVolatileTexelKHRMask:
case SpvImageOperandsSignExtendMask:
case SpvImageOperandsZeroExtendMask:
+ // TODO(jaebaek): Move this line properly after handling image offsets
+ // operand. This line temporarily fixes CI failure that
+ // blocks other PRs.
+ // https://github.com/KhronosGroup/SPIRV-Tools/issues/4565
+ case SpvImageOperandsOffsetsMask:
+ case SpvImageOperandsNontemporalMask:
return true;
}
return false;
@@ -253,7 +260,8 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
mask & ~uint32_t(SpvImageOperandsNonPrivateTexelKHRMask |
SpvImageOperandsVolatileTexelKHRMask |
SpvImageOperandsSignExtendMask |
- SpvImageOperandsZeroExtendMask);
+ SpvImageOperandsZeroExtendMask |
+ SpvImageOperandsNontemporalMask);
size_t expected_num_image_operand_words =
spvtools::utils::CountSetBits(mask_bits_having_operands);
if (mask & SpvImageOperandsGradMask) {
@@ -281,13 +289,14 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
// the module to be invalid.
if (mask == 0) return SPV_SUCCESS;
- if (spvtools::utils::CountSetBits(
- mask & (SpvImageOperandsOffsetMask | SpvImageOperandsConstOffsetMask |
- SpvImageOperandsConstOffsetsMask)) > 1) {
+ if (spvtools::utils::CountSetBits(mask & (SpvImageOperandsOffsetMask |
+ SpvImageOperandsConstOffsetMask |
+ SpvImageOperandsConstOffsetsMask |
+ SpvImageOperandsOffsetsMask)) > 1) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< _.VkErrorID(4662)
- << "Image Operands Offset, ConstOffset, ConstOffsets cannot be used "
- << "together";
+ << "Image Operands Offset, ConstOffset, ConstOffsets, Offsets "
+ "cannot be used together";
}
const bool is_implicit_lod = IsImplicitLod(opcode);
@@ -494,7 +503,7 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
if (!_.IsIntVectorType(component_type) ||
_.GetDimension(component_type) != 2) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Image Operand ConstOffsets array componenets to be "
+ << "Expected Image Operand ConstOffsets array components to be "
"int vectors of size 2";
}
@@ -620,6 +629,14 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
// setup.
}
+ if (mask & SpvImageOperandsOffsetsMask) {
+ // TODO: add validation
+ }
+
+ if (mask & SpvImageOperandsNontemporalMask) {
+ // Checked elsewhere: SPIR-V 1.6 version or later.
+ }
+
return SPV_SUCCESS;
}
@@ -905,6 +922,13 @@ spv_result_t ValidateTypeSampledImage(ValidationState_t& _,
"operand set to 0 or 1";
}
+ // This covers both OpTypeSampledImage and OpSampledImage.
+ if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) && info.dim == SpvDimBuffer) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "In SPIR-V 1.6 or later, sampled image dimension must not be "
+ "Buffer";
+ }
+
return SPV_SUCCESS;
}
@@ -1020,7 +1044,7 @@ spv_result_t ValidateSampledImage(ValidationState_t& _,
<< "Result <id> from OpSampledImage instruction must not appear "
"as operand for Op"
<< spvOpcodeString(static_cast<SpvOp>(consumer_opcode))
- << ", since it is not specificed as taking an "
+ << ", since it is not specified as taking an "
<< "OpTypeSampledImage."
<< " Found result <id> '" << _.getIdName(inst->id())
<< "' as an operand of <id> '"
@@ -1649,7 +1673,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 explicitely say what the type
+ // TODO(atgoo@github.com) The spec doesn't explicitly say what the type
// of texel should be.
const uint32_t texel_type = _.GetOperandTypeId(inst, 2);
if (!_.IsIntScalarOrVectorType(texel_type) &&
@@ -2058,11 +2082,13 @@ spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst) {
std::string* message) {
const auto* models = state.GetExecutionModels(entry_point->id());
const auto* modes = state.GetExecutionModes(entry_point->id());
- if (models->find(SpvExecutionModelGLCompute) != models->end() &&
- modes->find(SpvExecutionModeDerivativeGroupLinearNV) ==
- modes->end() &&
- modes->find(SpvExecutionModeDerivativeGroupQuadsNV) ==
- modes->end()) {
+ if (models &&
+ models->find(SpvExecutionModelGLCompute) != models->end() &&
+ (!modes ||
+ (modes->find(SpvExecutionModeDerivativeGroupLinearNV) ==
+ modes->end() &&
+ modes->find(SpvExecutionModeDerivativeGroupQuadsNV) ==
+ modes->end()))) {
if (message) {
*message =
std::string(
diff --git a/source/val/validate_instruction.cpp b/source/val/validate_instruction.cpp
index 9d395fb4..3edf1637 100644
--- a/source/val/validate_instruction.cpp
+++ b/source/val/validate_instruction.cpp
@@ -297,7 +297,7 @@ spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) {
}
// OpTerminateInvocation is special because it is enabled by Shader
- // capability, but also requries a extension and/or version check.
+ // capability, but also requires an extension and/or version check.
const bool capability_check_is_sufficient =
inst->opcode() != SpvOpTerminateInvocation;
@@ -318,10 +318,9 @@ spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) {
if (module_version < min_version) {
return _.diag(SPV_ERROR_WRONG_VERSION, inst)
- << spvOpcodeString(opcode) << " requires "
- << spvTargetEnvDescription(
- static_cast<spv_target_env>(min_version))
- << " at minimum.";
+ << spvOpcodeString(opcode) << " requires SPIR-V version "
+ << SPV_SPIRV_VERSION_MAJOR_PART(min_version) << "."
+ << SPV_SPIRV_VERSION_MINOR_PART(min_version) << " at minimum.";
}
} else if (!_.HasAnyOfExtensions(exts)) {
// Otherwise, we only error out when no enabling extensions are
@@ -407,7 +406,7 @@ spv_result_t LimitCheckSwitch(ValidationState_t& _, const Instruction* inst) {
// The instruction syntax is as follows:
// OpSwitch <selector ID> <Default ID> literal label literal label ...
// literal,label pairs come after the first 2 operands.
- // It is guaranteed at this point that num_operands is an even numner.
+ // It is guaranteed at this point that num_operands is an even number.
size_t num_pairs = (inst->operands().size() - 2) / 2;
const unsigned int num_pairs_limit =
_.options()->universal_limits_.max_switch_branches;
diff --git a/source/val/validate_interfaces.cpp b/source/val/validate_interfaces.cpp
index d16d48e2..adf2e472 100644
--- a/source/val/validate_interfaces.cpp
+++ b/source/val/validate_interfaces.cpp
@@ -27,6 +27,10 @@ namespace spvtools {
namespace val {
namespace {
+// Limit the number of checked locations to 4096. Multiplied by 4 to represent
+// all the components. This limit is set to be well beyond practical use cases.
+const uint32_t kMaxLocations = 4096 * 4;
+
// Returns true if \c inst is an input or output variable.
bool is_interface_variable(const Instruction* inst, bool is_spv_1_4) {
if (is_spv_1_4) {
@@ -150,7 +154,7 @@ spv_result_t NumConsumedLocations(ValidationState_t& _, const Instruction* type,
// Members cannot have location decorations at this point.
if (_.HasDecoration(type->id(), SpvDecorationLocation)) {
return _.diag(SPV_ERROR_INVALID_DATA, type)
- << "Members cannot be assigned a location";
+ << _.VkErrorID(4918) << "Members cannot be assigned a location";
}
// Structs consume locations equal to the sum of the locations consumed
@@ -195,6 +199,10 @@ uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) {
NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1)));
num_components *= type->GetOperandAs<uint32_t>(2);
break;
+ case SpvOpTypeArray:
+ // Skip the array.
+ return NumConsumedComponents(_,
+ _.FindDef(type->GetOperandAs<uint32_t>(1)));
default:
// This is an error that is validated elsewhere.
break;
@@ -318,8 +326,9 @@ spv_result_t GetLocationsForVariable(
// Only block-decorated structs don't need a location on the variable.
const bool is_block = _.HasDecoration(type_id, SpvDecorationBlock);
if (!has_location && !is_block) {
+ const auto vuid = (type->opcode() == SpvOpTypeStruct) ? 4917 : 4916;
return _.diag(SPV_ERROR_INVALID_DATA, variable)
- << "Variable must be decorated with a location";
+ << _.VkErrorID(vuid) << "Variable must be decorated with a location";
}
const std::string storage_class = is_output ? "output" : "input";
@@ -347,6 +356,11 @@ spv_result_t GetLocationsForVariable(
uint32_t num_components = NumConsumedComponents(_, sub_type);
uint32_t array_location = location + (num_locations * array_idx);
uint32_t start = array_location * 4;
+ if (kMaxLocations <= start) {
+ // Too many locations, give up.
+ break;
+ }
+
uint32_t end = (array_location + num_locations) * 4;
if (num_components != 0) {
start += component;
@@ -398,7 +412,7 @@ spv_result_t GetLocationsForVariable(
auto where = member_locations.find(i - 1);
if (where == member_locations.end()) {
return _.diag(SPV_ERROR_INVALID_DATA, type)
- << "Member index " << i - 1
+ << _.VkErrorID(4919) << "Member index " << i - 1
<< " is missing a location assignment";
}
@@ -416,17 +430,41 @@ spv_result_t GetLocationsForVariable(
}
uint32_t start = location * 4;
- uint32_t end = (location + num_locations) * 4;
- if (num_components != 0) {
- start += component;
- end = location * 4 + component + num_components;
+ if (kMaxLocations <= start) {
+ // Too many locations, give up.
+ continue;
}
- for (uint32_t l = start; l < end; ++l) {
- if (!locations->insert(l).second) {
- return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
- << "Entry-point has conflicting " << storage_class
- << " location assignment at location " << l / 4
- << ", component " << l % 4;
+
+ if (member->opcode() == SpvOpTypeArray && num_components >= 1 &&
+ num_components < 4) {
+ // When an array has an element that takes less than a location in
+ // size, calculate the used locations in a strided manner.
+ for (uint32_t l = location; l < num_locations + location; ++l) {
+ for (uint32_t c = component; c < component + num_components; ++c) {
+ uint32_t check = 4 * l + c;
+ if (!locations->insert(check).second) {
+ return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
+ << "Entry-point has conflicting " << storage_class
+ << " location assignment at location " << l
+ << ", component " << c;
+ }
+ }
+ }
+ } else {
+ // TODO: There is a hole here is the member is an array of 3- or
+ // 4-element vectors of 64-bit types.
+ uint32_t end = (location + num_locations) * 4;
+ if (num_components != 0) {
+ start += component;
+ end = location * 4 + component + num_components;
+ }
+ for (uint32_t l = start; l < end; ++l) {
+ if (!locations->insert(l).second) {
+ return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
+ << "Entry-point has conflicting " << storage_class
+ << " location assignment at location " << l / 4
+ << ", component " << l % 4;
+ }
}
}
}
@@ -439,6 +477,9 @@ spv_result_t ValidateLocations(ValidationState_t& _,
const Instruction* entry_point) {
// According to Vulkan 14.1 only the following execution models have
// locations assigned.
+ // TODO(dneto): SPV_NV_ray_tracing also uses locations on interface variables,
+ // in other shader stages. Similarly, the *provisional* version of
+ // SPV_KHR_ray_tracing did as well, but not the final version.
switch (entry_point->GetOperandAs<SpvExecutionModel>(0)) {
case SpvExecutionModelVertex:
case SpvExecutionModelTessellationControl:
@@ -454,6 +495,7 @@ spv_result_t ValidateLocations(ValidationState_t& _,
std::unordered_set<uint32_t> input_locations;
std::unordered_set<uint32_t> output_locations_index0;
std::unordered_set<uint32_t> output_locations_index1;
+ std::unordered_set<uint32_t> seen;
for (uint32_t i = 3; i < entry_point->operands().size(); ++i) {
auto interface_id = entry_point->GetOperandAs<uint32_t>(i);
auto interface_var = _.FindDef(interface_id);
@@ -462,6 +504,11 @@ spv_result_t ValidateLocations(ValidationState_t& _,
storage_class != SpvStorageClassOutput) {
continue;
}
+ if (!seen.insert(interface_id).second) {
+ // Pre-1.4 an interface variable could be listed multiple times in an
+ // entry point. Validation for 1.4 or later is done elsewhere.
+ continue;
+ }
auto locations = (storage_class == SpvStorageClassInput)
? &input_locations
diff --git a/source/val/validate_layout.cpp b/source/val/validate_layout.cpp
index b53f991e..d5823219 100644
--- a/source/val/validate_layout.cpp
+++ b/source/val/validate_layout.cpp
@@ -17,6 +17,7 @@
#include <cassert>
#include "DebugInfo.h"
+#include "NonSemanticShaderDebugInfo100.h"
#include "OpenCLDebugInfo100.h"
#include "source/diagnostic.h"
#include "source/opcode.h"
@@ -37,17 +38,7 @@ spv_result_t ModuleScopedInstructions(ValidationState_t& _,
const Instruction* inst, SpvOp opcode) {
switch (opcode) {
case SpvOpExtInst:
- if (spvExtInstIsNonSemantic(inst->ext_inst_type())) {
- // non-semantic extinst opcodes are allowed beginning in the types
- // section, but since they must name a return type they cannot be the
- // first instruction in the types section. Therefore check that we are
- // already in it.
- if (_.current_layout_section() < kLayoutTypes) {
- return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
- << "Non-semantic OpExtInst must not appear before types "
- << "section";
- }
- } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) {
+ if (spvExtInstIsDebugInfo(inst->ext_inst_type())) {
const uint32_t ext_inst_index = inst->word(4);
bool local_debug_info = false;
if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
@@ -59,6 +50,20 @@ spv_result_t ModuleScopedInstructions(ValidationState_t& _,
ext_inst_key == OpenCLDebugInfo100DebugValue) {
local_debug_info = true;
}
+ } else if (inst->ext_inst_type() ==
+ SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
+ const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
+ NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
+ if (ext_inst_key == NonSemanticShaderDebugInfo100DebugScope ||
+ ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope ||
+ ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare ||
+ ext_inst_key == NonSemanticShaderDebugInfo100DebugValue ||
+ ext_inst_key == NonSemanticShaderDebugInfo100DebugLine ||
+ ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine ||
+ ext_inst_key ==
+ NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
+ local_debug_info = true;
+ }
} else {
const DebugInfoInstructions ext_inst_key =
DebugInfoInstructions(ext_inst_index);
@@ -94,6 +99,16 @@ spv_result_t ModuleScopedInstructions(ValidationState_t& _,
<< "declarations)";
}
}
+ } else if (spvExtInstIsNonSemantic(inst->ext_inst_type())) {
+ // non-semantic extinst opcodes are allowed beginning in the types
+ // section, but since they must name a return type they cannot be the
+ // first instruction in the types section. Therefore check that we are
+ // already in it.
+ if (_.current_layout_section() < kLayoutTypes) {
+ return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+ << "Non-semantic OpExtInst must not appear before types "
+ << "section";
+ }
} else {
// otherwise they must be used in a block
if (_.current_layout_section() < kLayoutFunctionDefinitions) {
@@ -230,20 +245,7 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _,
break;
case SpvOpExtInst:
- if (spvExtInstIsNonSemantic(inst->ext_inst_type())) {
- // non-semantic extinst opcodes are allowed beginning in the types
- // section, but must either be placed outside a function declaration,
- // or inside a block.
- if (_.current_layout_section() < kLayoutTypes) {
- return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
- << "Non-semantic OpExtInst must not appear before types "
- << "section";
- } else if (_.in_function_body() && _.in_block() == false) {
- return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
- << "Non-semantic OpExtInst within function definition must "
- "appear in a block";
- }
- } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) {
+ if (spvExtInstIsDebugInfo(inst->ext_inst_type())) {
const uint32_t ext_inst_index = inst->word(4);
bool local_debug_info = false;
if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
@@ -255,6 +257,20 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _,
ext_inst_key == OpenCLDebugInfo100DebugValue) {
local_debug_info = true;
}
+ } else if (inst->ext_inst_type() ==
+ SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
+ const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
+ NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
+ if (ext_inst_key == NonSemanticShaderDebugInfo100DebugScope ||
+ ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope ||
+ ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare ||
+ ext_inst_key == NonSemanticShaderDebugInfo100DebugValue ||
+ ext_inst_key == NonSemanticShaderDebugInfo100DebugLine ||
+ ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine ||
+ ext_inst_key ==
+ NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
+ local_debug_info = true;
+ }
} else {
const DebugInfoInstructions ext_inst_key =
DebugInfoInstructions(ext_inst_index);
@@ -290,6 +306,19 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _,
<< "declarations)";
}
}
+ } else if (spvExtInstIsNonSemantic(inst->ext_inst_type())) {
+ // non-semantic extinst opcodes are allowed beginning in the types
+ // section, but must either be placed outside a function declaration,
+ // or inside a block.
+ if (_.current_layout_section() < kLayoutTypes) {
+ return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+ << "Non-semantic OpExtInst must not appear before types "
+ << "section";
+ } else if (_.in_function_body() && _.in_block() == false) {
+ return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+ << "Non-semantic OpExtInst within function definition must "
+ "appear in a block";
+ }
} else {
// otherwise they must be used in a block
if (_.in_block() == false) {
diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp
index a4bc0fab..93b18000 100644
--- a/source/val/validate_memory.cpp
+++ b/source/val/validate_memory.cpp
@@ -893,6 +893,12 @@ spv_result_t ValidateLoad(ValidationState_t& _, const Instruction* inst) {
<< "'s type.";
}
+ if (!_.options()->before_hlsl_legalization &&
+ _.ContainsRuntimeArray(inst->type_id())) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "Cannot load a runtime-sized array";
+ }
+
if (auto error = CheckMemoryAccess(_, inst, 3)) return error;
if (_.HasCapability(SpvCapabilityShader) &&
@@ -1405,7 +1411,7 @@ spv_result_t ValidateArrayLength(ValidationState_t& state,
<< state.getIdName(inst->id()) << "' must be an OpTypeRuntimeArray.";
}
- // The array member must the the index of the last element (the run time
+ // The array member must the index of the last element (the run time
// array).
if (inst->GetOperandAs<uint32_t>(3) != num_of_members - 1) {
return state.diag(SPV_ERROR_INVALID_ID, inst)
diff --git a/source/val/validate_mode_setting.cpp b/source/val/validate_mode_setting.cpp
index 79f82d8d..96352687 100644
--- a/source/val/validate_mode_setting.cpp
+++ b/source/val/validate_mode_setting.cpp
@@ -225,14 +225,21 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
}
}
}
+ if (i.opcode() == SpvOpExecutionModeId) {
+ const auto mode = i.GetOperandAs<SpvExecutionMode>(1);
+ if (mode == SpvExecutionModeLocalSizeId) {
+ ok = true;
+ break;
+ }
+ }
}
if (!ok) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << _.VkErrorID(4683)
+ << _.VkErrorID(6426)
<< "In the Vulkan environment, GLCompute execution model "
- "entry points require either the LocalSize execution "
- "mode or an object decorated with WorkgroupSize must be "
- "specified.";
+ "entry points require either the LocalSize or "
+ "LocalSizeId execution mode or an object decorated with "
+ "WorkgroupSize must be specified.";
}
}
break;
@@ -429,6 +436,10 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _,
break;
case SpvExecutionModeLocalSize:
case SpvExecutionModeLocalSizeId:
+ if (mode == SpvExecutionModeLocalSizeId && !_.IsLocalSizeIdAllowed())
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "LocalSizeId mode is not allowed by the current environment.";
+
if (!std::all_of(models->begin(), models->end(),
[&_](const SpvExecutionModel& model) {
switch (model) {
diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp
index 29ba5831..1c5f70a3 100644
--- a/source/val/validate_scopes.cpp
+++ b/source/val/validate_scopes.cpp
@@ -225,7 +225,7 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
<< _.VkErrorID(4638) << spvOpcodeString(opcode)
<< ": in Vulkan environment, Memory Scope cannot be CrossDevice";
}
- // Vulkan 1.0 specifc rules
+ // Vulkan 1.0 specific rules
if (_.context()->target_env == SPV_ENV_VULKAN_1_0 &&
value != SpvScopeDevice && value != SpvScopeWorkgroup &&
value != SpvScopeInvocation) {
@@ -234,7 +234,7 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
<< ": in Vulkan 1.0 environment Memory Scope is limited to "
<< "Device, Workgroup and Invocation";
}
- // Vulkan 1.1 specifc rules
+ // 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 &&
diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp
index 612fc5c2..4376b52c 100644
--- a/source/val/validate_type.cpp
+++ b/source/val/validate_type.cpp
@@ -596,7 +596,7 @@ spv_result_t ValidateTypeCooperativeMatrixNV(ValidationState_t& _,
if (!cols || !_.IsIntScalarType(cols->type_id()) ||
!spvOpcodeIsConstant(cols->opcode())) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "OpTypeCooperativeMatrixNV Cols <id> '" << _.getIdName(rows_id)
+ << "OpTypeCooperativeMatrixNV Cols <id> '" << _.getIdName(cols_id)
<< "' is not a constant instruction with scalar integer type.";
}
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index 52821636..6f97321f 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -175,6 +175,19 @@ ValidationState_t::ValidationState_t(const spv_const_context ctx,
}
}
+ // LocalSizeId is only disallowed prior to Vulkan 1.3 without maintenance4.
+ switch (env) {
+ case SPV_ENV_VULKAN_1_0:
+ case SPV_ENV_VULKAN_1_1:
+ case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
+ case SPV_ENV_VULKAN_1_2:
+ features_.env_allow_localsizeid = false;
+ break;
+ default:
+ features_.env_allow_localsizeid = true;
+ break;
+ }
+
// Only attempt to count if we have words, otherwise let the other validation
// fail and generate an error.
if (num_words > 0) {
@@ -386,6 +399,15 @@ void ValidationState_t::RegisterCapability(SpvCapability cap) {
features_.variable_pointers_storage_buffer = true;
break;
default:
+ // TODO(dneto): For now don't validate SPV_NV_ray_tracing, which uses
+ // capability SpvCapabilityRayTracingNV.
+ // SpvCapabilityRayTracingProvisionalKHR would need the same treatment.
+ // One of the differences going from SPV_KHR_ray_tracing from
+ // provisional to final spec was the provisional spec uses Locations
+ // for variables in certain storage classes, just like the
+ // SPV_NV_ray_tracing extension. So it mimics the NVIDIA extension.
+ // The final SPV_KHR_ray_tracing uses a different capability token
+ // number, so it doesn't fall into this case.
break;
}
}
@@ -476,7 +498,7 @@ spv_result_t ValidationState_t::RegisterFunctionEnd() {
"inside of another function");
assert(in_block() == false &&
"RegisterFunctionParameter can only be called when parsing the binary "
- "ouside of a block");
+ "outside of a block");
current_function().RegisterFunctionEnd();
in_function_ = false;
return SPV_SUCCESS;
@@ -494,15 +516,13 @@ void ValidationState_t::RegisterDebugInstruction(const Instruction* inst) {
switch (inst->opcode()) {
case SpvOpName: {
const auto target = inst->GetOperandAs<uint32_t>(0);
- const auto* str = reinterpret_cast<const char*>(inst->words().data() +
- inst->operand(1).offset);
+ const std::string str = inst->GetOperandAs<std::string>(1);
AssignNameToId(target, str);
break;
}
case SpvOpMemberName: {
const auto target = inst->GetOperandAs<uint32_t>(0);
- const auto* str = reinterpret_cast<const char*>(inst->words().data() +
- inst->operand(2).offset);
+ const std::string str = inst->GetOperandAs<std::string>(2);
AssignNameToId(target, str);
break;
}
@@ -590,7 +610,7 @@ void ValidationState_t::RegisterStorageClassConsumer(
if (message) {
*message =
errorVUID +
- "in Vulkan evironment, Output Storage Class must not be "
+ "in Vulkan environment, Output Storage Class must not be "
"used in GLCompute, RayGenerationKHR, IntersectionKHR, "
"AnyHitKHR, ClosestHitKHR, MissKHR, or CallableKHR "
"execution models";
@@ -612,7 +632,7 @@ void ValidationState_t::RegisterStorageClassConsumer(
if (message) {
*message =
errorVUID +
- "in Vulkan evironment, Workgroup Storage Class is limited "
+ "in Vulkan environment, Workgroup Storage Class is limited "
"to MeshNV, TaskNV, and GLCompute execution model";
}
return false;
@@ -729,19 +749,19 @@ uint32_t ValidationState_t::GetBitWidth(uint32_t id) const {
bool ValidationState_t::IsVoidType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
- return inst->opcode() == SpvOpTypeVoid;
+ return inst && inst->opcode() == SpvOpTypeVoid;
}
bool ValidationState_t::IsFloatScalarType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
- return inst->opcode() == SpvOpTypeFloat;
+ return inst && inst->opcode() == SpvOpTypeFloat;
}
bool ValidationState_t::IsFloatVectorType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
+ if (!inst) {
+ return false;
+ }
if (inst->opcode() == SpvOpTypeVector) {
return IsFloatScalarType(GetComponentType(id));
@@ -752,7 +772,9 @@ bool ValidationState_t::IsFloatVectorType(uint32_t id) const {
bool ValidationState_t::IsFloatScalarOrVectorType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
+ if (!inst) {
+ return false;
+ }
if (inst->opcode() == SpvOpTypeFloat) {
return true;
@@ -767,13 +789,14 @@ bool ValidationState_t::IsFloatScalarOrVectorType(uint32_t id) const {
bool ValidationState_t::IsIntScalarType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
- return inst->opcode() == SpvOpTypeInt;
+ return inst && inst->opcode() == SpvOpTypeInt;
}
bool ValidationState_t::IsIntVectorType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
+ if (!inst) {
+ return false;
+ }
if (inst->opcode() == SpvOpTypeVector) {
return IsIntScalarType(GetComponentType(id));
@@ -784,7 +807,9 @@ bool ValidationState_t::IsIntVectorType(uint32_t id) const {
bool ValidationState_t::IsIntScalarOrVectorType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
+ if (!inst) {
+ return false;
+ }
if (inst->opcode() == SpvOpTypeInt) {
return true;
@@ -799,13 +824,14 @@ bool ValidationState_t::IsIntScalarOrVectorType(uint32_t id) const {
bool ValidationState_t::IsUnsignedIntScalarType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
- return inst->opcode() == SpvOpTypeInt && inst->word(3) == 0;
+ return inst && inst->opcode() == SpvOpTypeInt && inst->word(3) == 0;
}
bool ValidationState_t::IsUnsignedIntVectorType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
+ if (!inst) {
+ return false;
+ }
if (inst->opcode() == SpvOpTypeVector) {
return IsUnsignedIntScalarType(GetComponentType(id));
@@ -816,13 +842,14 @@ bool ValidationState_t::IsUnsignedIntVectorType(uint32_t id) const {
bool ValidationState_t::IsSignedIntScalarType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
- return inst->opcode() == SpvOpTypeInt && inst->word(3) == 1;
+ return inst && inst->opcode() == SpvOpTypeInt && inst->word(3) == 1;
}
bool ValidationState_t::IsSignedIntVectorType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
+ if (!inst) {
+ return false;
+ }
if (inst->opcode() == SpvOpTypeVector) {
return IsSignedIntScalarType(GetComponentType(id));
@@ -833,13 +860,14 @@ bool ValidationState_t::IsSignedIntVectorType(uint32_t id) const {
bool ValidationState_t::IsBoolScalarType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
- return inst->opcode() == SpvOpTypeBool;
+ return inst && inst->opcode() == SpvOpTypeBool;
}
bool ValidationState_t::IsBoolVectorType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
+ if (!inst) {
+ return false;
+ }
if (inst->opcode() == SpvOpTypeVector) {
return IsBoolScalarType(GetComponentType(id));
@@ -850,7 +878,9 @@ bool ValidationState_t::IsBoolVectorType(uint32_t id) const {
bool ValidationState_t::IsBoolScalarOrVectorType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
+ if (!inst) {
+ return false;
+ }
if (inst->opcode() == SpvOpTypeBool) {
return true;
@@ -865,7 +895,9 @@ bool ValidationState_t::IsBoolScalarOrVectorType(uint32_t id) const {
bool ValidationState_t::IsFloatMatrixType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
+ if (!inst) {
+ return false;
+ }
if (inst->opcode() == SpvOpTypeMatrix) {
return IsFloatScalarType(GetComponentType(id));
@@ -920,8 +952,7 @@ bool ValidationState_t::GetStructMemberTypes(
bool ValidationState_t::IsPointerType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
- return inst->opcode() == SpvOpTypePointer;
+ return inst && inst->opcode() == SpvOpTypePointer;
}
bool ValidationState_t::GetPointerTypeInfo(uint32_t id, uint32_t* data_type,
@@ -939,8 +970,7 @@ bool ValidationState_t::GetPointerTypeInfo(uint32_t id, uint32_t* data_type,
bool ValidationState_t::IsCooperativeMatrixType(uint32_t id) const {
const Instruction* inst = FindDef(id);
- assert(inst);
- return inst->opcode() == SpvOpTypeCooperativeMatrixNV;
+ return inst && inst->opcode() == SpvOpTypeCooperativeMatrixNV;
}
bool ValidationState_t::IsFloatCooperativeMatrixType(uint32_t id) const {
@@ -1262,16 +1292,13 @@ const Instruction* ValidationState_t::TracePointer(
return base_ptr;
}
-bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, SpvOp type,
- uint32_t width) const {
- if (type != SpvOpTypeInt && type != SpvOpTypeFloat) return false;
-
+bool ValidationState_t::ContainsType(
+ uint32_t id, const std::function<bool(const Instruction*)>& f,
+ bool traverse_all_types) const {
const auto inst = FindDef(id);
if (!inst) return false;
- if (inst->opcode() == type) {
- return inst->GetOperandAs<uint32_t>(1u) == width;
- }
+ if (f(inst)) return true;
switch (inst->opcode()) {
case SpvOpTypeArray:
@@ -1281,24 +1308,45 @@ bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, SpvOp type,
case SpvOpTypeImage:
case SpvOpTypeSampledImage:
case SpvOpTypeCooperativeMatrixNV:
- return ContainsSizedIntOrFloatType(inst->GetOperandAs<uint32_t>(1u), type,
- width);
+ return ContainsType(inst->GetOperandAs<uint32_t>(1u), f,
+ traverse_all_types);
case SpvOpTypePointer:
if (IsForwardPointer(id)) return false;
- return ContainsSizedIntOrFloatType(inst->GetOperandAs<uint32_t>(2u), type,
- width);
+ if (traverse_all_types) {
+ return ContainsType(inst->GetOperandAs<uint32_t>(2u), f,
+ traverse_all_types);
+ }
+ break;
case SpvOpTypeFunction:
- case SpvOpTypeStruct: {
+ case SpvOpTypeStruct:
+ if (inst->opcode() == SpvOpTypeFunction && !traverse_all_types) {
+ return false;
+ }
for (uint32_t i = 1; i < inst->operands().size(); ++i) {
- if (ContainsSizedIntOrFloatType(inst->GetOperandAs<uint32_t>(i), type,
- width))
+ if (ContainsType(inst->GetOperandAs<uint32_t>(i), f,
+ traverse_all_types)) {
return true;
+ }
}
- return false;
- }
+ break;
default:
- return false;
+ break;
}
+
+ return false;
+}
+
+bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, SpvOp type,
+ uint32_t width) const {
+ if (type != SpvOpTypeInt && type != SpvOpTypeFloat) return false;
+
+ const auto f = [type, width](const Instruction* inst) {
+ if (inst->opcode() == type) {
+ return inst->GetOperandAs<uint32_t>(1u) == width;
+ }
+ return false;
+ };
+ return ContainsType(id, f);
}
bool ValidationState_t::ContainsLimitedUseIntOrFloatType(uint32_t id) const {
@@ -1313,6 +1361,13 @@ bool ValidationState_t::ContainsLimitedUseIntOrFloatType(uint32_t id) const {
return false;
}
+bool ValidationState_t::ContainsRuntimeArray(uint32_t id) const {
+ const auto f = [](const Instruction* inst) {
+ return inst->opcode() == SpvOpTypeRuntimeArray;
+ };
+ return ContainsType(id, f, /* traverse_all_types = */ false);
+}
+
bool ValidationState_t::IsValidStorageClass(
SpvStorageClass storage_class) const {
if (spvIsVulkanEnv(context()->target_env)) {
@@ -1352,7 +1407,7 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
return "";
}
- // This large switch case is only searched when an error has occured.
+ // This large switch case is only searched when an error has occurred.
// If an id is changed, the old case must be modified or removed. Each string
// here is interpreted as being "implemented"
@@ -1801,14 +1856,16 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
return VUID_WRAP(VUID-StandaloneSpirv-None-04667);
case 4669:
return VUID_WRAP(VUID-StandaloneSpirv-GLSLShared-04669);
+ case 4670:
+ return VUID_WRAP(VUID-StandaloneSpirv-Flat-04670);
case 4675:
return VUID_WRAP(VUID-StandaloneSpirv-FPRoundingMode-04675);
case 4677:
return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677);
case 4682:
return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682);
- case 4683:
- return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-04683);
+ case 6426:
+ return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-06426); // formally 04683
case 4685:
return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685);
case 4686:
@@ -1827,6 +1884,16 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04733);
case 4780:
return VUID_WRAP(VUID-StandaloneSpirv-Result-04780);
+ case 4915:
+ return VUID_WRAP(VUID-StandaloneSpirv-Location-04915);
+ case 4916:
+ return VUID_WRAP(VUID-StandaloneSpirv-Location-04916);
+ case 4917:
+ return VUID_WRAP(VUID-StandaloneSpirv-Location-04917);
+ case 4918:
+ return VUID_WRAP(VUID-StandaloneSpirv-Location-04918);
+ case 4919:
+ return VUID_WRAP(VUID-StandaloneSpirv-Location-04919);
default:
return ""; // unknown id
}
diff --git a/source/val/validation_state.h b/source/val/validation_state.h
index 57634bf4..89834a0d 100644
--- a/source/val/validation_state.h
+++ b/source/val/validation_state.h
@@ -67,7 +67,7 @@ class ValidationState_t {
bool declare_int16_type = false; // Allow OpTypeInt with 16 bit width?
bool declare_float16_type = false; // Allow OpTypeFloat with 16 bit width?
bool free_fp_rounding_mode = false; // Allow the FPRoundingMode decoration
- // and its vaules to be used without
+ // and its values to be used without
// requiring any capability
// Allow functionalities enabled by VariablePointers capability.
@@ -90,23 +90,6 @@ class ValidationState_t {
// conversion opcodes
bool use_int8_type = false;
- // Use scalar block layout. See VK_EXT_scalar_block_layout:
- // Defines scalar alignment:
- // - scalar alignment equals the scalar size in bytes
- // - array alignment is same as its element alignment
- // - array alignment is max alignment of any of its members
- // - vector alignment is same as component alignment
- // - matrix alignment is same as component alignment
- // For struct in Uniform, StorageBuffer, PushConstant:
- // - Offset of a member is multiple of scalar alignment of that member
- // - ArrayStride and MatrixStride are multiples of scalar alignment
- // Members need not be listed in offset order
- bool scalar_block_layout = false;
-
- // Use scalar block layout (as defined above) for Workgroup block
- // variables. See VK_KHR_workgroup_memory_explicit_layout.
- bool workgroup_scalar_block_layout = false;
-
// SPIR-V 1.4 allows us to select between any two composite values
// of the same type.
bool select_between_composites = false;
@@ -121,6 +104,9 @@ class ValidationState_t {
// SPIR-V 1.4 allows Function and Private variables to be NonWritable
bool nonwritable_var_in_function_or_private = false;
+
+ // Whether LocalSizeId execution mode is allowed by the environment.
+ bool env_allow_localsizeid = false;
};
ValidationState_t(const spv_const_context context,
@@ -493,6 +479,12 @@ class ValidationState_t {
return features_.env_relaxed_block_layout || options()->relax_block_layout;
}
+ // Returns true if allowing localsizeid, either because the environment always
+ // allows it, or because it is enabled from the command-line.
+ bool IsLocalSizeIdAllowed() const {
+ return features_.env_allow_localsizeid || options()->allow_localsizeid;
+ }
+
/// Sets the struct nesting depth for a given struct ID
void set_struct_nesting_depth(uint32_t id, uint32_t depth) {
struct_nesting_depth_[id] = depth;
@@ -595,6 +587,17 @@ class ValidationState_t {
// 16-bit float that is not generally enabled for use.
bool ContainsLimitedUseIntOrFloatType(uint32_t id) const;
+ // Returns true if |id| is a type that contains a runtime-sized array.
+ // Does not consider a pointers as contains the array.
+ bool ContainsRuntimeArray(uint32_t id) const;
+
+ // Generic type traversal.
+ // Only traverse pointers and functions if |traverse_all_types| is true.
+ // Recursively tests |f| against the type hierarchy headed by |id|.
+ bool ContainsType(uint32_t id,
+ const std::function<bool(const Instruction*)>& f,
+ bool traverse_all_types = true) const;
+
// Gets value from OpConstant and OpSpecConstant as uint64.
// Returns false on failure (no instruction, wrong instruction, not int).
bool GetConstantValUint64(uint32_t id, uint64_t* val) const;
@@ -794,7 +797,7 @@ class ValidationState_t {
/// IDs that are entry points, ie, arguments to OpEntryPoint.
std::vector<uint32_t> entry_points_;
- /// Maps an entry point id to its desciptions.
+ /// Maps an entry point id to its descriptions.
std::unordered_map<uint32_t, std::vector<EntryPointDescription>>
entry_point_descriptions_;
@@ -841,7 +844,7 @@ class ValidationState_t {
// have the same pointer size (for physical pointer types).
uint32_t pointer_size_and_alignment_;
- /// NOTE: See correspoding getter functions
+ /// NOTE: See corresponding getter functions
bool in_function_;
/// The state of optional features. These are determined by capabilities
diff --git a/source/wasm/README.md b/source/wasm/README.md
new file mode 100644
index 00000000..aca0f70c
--- /dev/null
+++ b/source/wasm/README.md
@@ -0,0 +1,43 @@
+# SPIRV-Tools
+
+Wasm (WebAssembly) build of https://github.com/KhronosGroup/SPIRV-Tools
+
+## Usage
+
+```js
+const spirvTools = require("spirv-tools");
+
+const test = async () => {
+ // Load the library
+ const spv = await spirvTools();
+
+ // assemble
+ const source = `
+ OpCapability Linkage
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpSource GLSL 450
+ OpDecorate %spec SpecId 1
+ %int = OpTypeInt 32 1
+ %spec = OpSpecConstant %int 0
+ %const = OpConstant %int 42`;
+ const asResult = spv.as(
+ source,
+ spv.SPV_ENV_UNIVERSAL_1_3,
+ spv.SPV_TEXT_TO_BINARY_OPTION_NONE
+ );
+ console.log(`as returned ${asResult.byteLength} bytes`);
+
+ // re-disassemble
+ const disResult = spv.dis(
+ asResult,
+ spv.SPV_ENV_UNIVERSAL_1_3,
+ spv.SPV_BINARY_TO_TEXT_OPTION_INDENT |
+ spv.SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES |
+ spv.SPV_BINARY_TO_TEXT_OPTION_COLOR
+ );
+ console.log("dis:\n", disResult);
+};
+
+test();
+```
diff --git a/source/wasm/build.sh b/source/wasm/build.sh
new file mode 100755
index 00000000..f02ae525
--- /dev/null
+++ b/source/wasm/build.sh
@@ -0,0 +1,78 @@
+#!/bin/bash
+
+# Copyright (c) 2020 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.
+
+set -e
+
+NUM_CORES=$(nproc)
+echo "Detected $NUM_CORES cores for building"
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+VERSION=$(sed -n '0,/^v20/ s/^v\(20[0-9.]*\).*/\1/p' $DIR/../../CHANGES).${GITHUB_RUN_NUMBER:-0}
+echo "Version: $VERSION"
+
+build() {
+ type=$1
+ shift
+ args=$@
+ mkdir -p build/$type
+ pushd build/$type
+ echo $args
+ emcmake cmake \
+ -DCMAKE_BUILD_TYPE=Release \
+ $args \
+ ../..
+ emmake make -j $(( $NUM_CORES )) SPIRV-Tools-static
+
+ echo Building js interface
+ emcc \
+ --bind \
+ -I../../include \
+ -std=c++11 \
+ ../../source/wasm/spirv-tools.cpp \
+ source/libSPIRV-Tools.a \
+ -o spirv-tools.js \
+ -s MODULARIZE \
+ -Oz
+
+ popd
+ mkdir -p out/$type
+
+ # copy other js files
+ cp source/wasm/spirv-tools.d.ts out/$type/
+ sed -e 's/\("version"\s*:\s*\).*/\1"'$VERSION'",/' source/wasm/package.json > out/$type/package.json
+ cp source/wasm/README.md out/$type/
+ cp LICENSE out/$type/
+
+ cp build/$type/spirv-tools.js out/$type/
+ gzip -9 -k -f out/$type/spirv-tools.js
+ if [ -e build/$type/spirv-tools.wasm ] ; then
+ cp build/$type/spirv-tools.wasm out/$type/
+ gzip -9 -k -f out/$type/spirv-tools.wasm
+ fi
+}
+
+if [ ! -d external/spirv-headers ] ; then
+ echo "Fetching SPIRV-headers"
+ git clone https://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers
+fi
+
+echo Building ${BASH_REMATCH[1]}
+build web\
+ -DSPIRV_COLOR_TERMINAL=OFF\
+ -DSPIRV_SKIP_TESTS=ON\
+ -DSPIRV_SKIP_EXECUTABLES=ON
+
+wc -c out/*/*
diff --git a/source/wasm/package.json b/source/wasm/package.json
new file mode 100644
index 00000000..78273538
--- /dev/null
+++ b/source/wasm/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "spirv-tools",
+ "version": "VERSION",
+ "license": "Apache-2.0",
+ "main": "spirv-tools",
+ "types": "spirv-tools.d.ts",
+ "files": [
+ "*.wasm",
+ "*.js",
+ "*.d.ts"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/KhronosGroup/SPIRV-Tools"
+ },
+ "homepage": "https://github.com/KhronosGroup/SPIRV-Tools"
+}
diff --git a/source/wasm/spirv-tools.cpp b/source/wasm/spirv-tools.cpp
new file mode 100644
index 00000000..33f2f05f
--- /dev/null
+++ b/source/wasm/spirv-tools.cpp
@@ -0,0 +1,94 @@
+// Copyright (c) 2020 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 "spirv-tools/libspirv.hpp"
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <emscripten/bind.h>
+#include <emscripten/val.h>
+using namespace emscripten;
+
+void print_msg_to_stderr (spv_message_level_t, const char*,
+ const spv_position_t&, const char* m) {
+ std::cerr << "error: " << m << std::endl;
+};
+
+std::string dis(std::string const& buffer, uint32_t env, uint32_t options) {
+ spvtools::SpirvTools core(static_cast<spv_target_env>(env));
+ core.SetMessageConsumer(print_msg_to_stderr);
+
+ std::vector<uint32_t> spirv;
+ const uint32_t* ptr = reinterpret_cast<const uint32_t*>(buffer.data());
+ spirv.assign(ptr, ptr + buffer.size() / 4);
+ std::string disassembly;
+ if (!core.Disassemble(spirv, &disassembly, options)) return "Error";
+ return disassembly;
+}
+
+emscripten::val as(std::string const& source, uint32_t env, uint32_t options) {
+ spvtools::SpirvTools core(static_cast<spv_target_env>(env));
+ core.SetMessageConsumer(print_msg_to_stderr);
+
+ std::vector<uint32_t> spirv;
+ if (!core.Assemble(source, &spirv, options)) spirv.clear();
+ const uint8_t* ptr = reinterpret_cast<const uint8_t*>(spirv.data());
+ return emscripten::val(emscripten::typed_memory_view(spirv.size() * 4,
+ ptr));
+}
+
+EMSCRIPTEN_BINDINGS(my_module) {
+ function("dis", &dis);
+ function("as", &as);
+
+ constant("SPV_ENV_UNIVERSAL_1_0", static_cast<uint32_t>(SPV_ENV_UNIVERSAL_1_0));
+ constant("SPV_ENV_VULKAN_1_0", static_cast<uint32_t>(SPV_ENV_VULKAN_1_0));
+ constant("SPV_ENV_UNIVERSAL_1_1", static_cast<uint32_t>(SPV_ENV_UNIVERSAL_1_1));
+ constant("SPV_ENV_OPENCL_2_1", static_cast<uint32_t>(SPV_ENV_OPENCL_2_1));
+ constant("SPV_ENV_OPENCL_2_2", static_cast<uint32_t>(SPV_ENV_OPENCL_2_2));
+ constant("SPV_ENV_OPENGL_4_0", static_cast<uint32_t>(SPV_ENV_OPENGL_4_0));
+ constant("SPV_ENV_OPENGL_4_1", static_cast<uint32_t>(SPV_ENV_OPENGL_4_1));
+ constant("SPV_ENV_OPENGL_4_2", static_cast<uint32_t>(SPV_ENV_OPENGL_4_2));
+ constant("SPV_ENV_OPENGL_4_3", static_cast<uint32_t>(SPV_ENV_OPENGL_4_3));
+ constant("SPV_ENV_OPENGL_4_5", static_cast<uint32_t>(SPV_ENV_OPENGL_4_5));
+ constant("SPV_ENV_UNIVERSAL_1_2", static_cast<uint32_t>(SPV_ENV_UNIVERSAL_1_2));
+ constant("SPV_ENV_OPENCL_1_2", static_cast<uint32_t>(SPV_ENV_OPENCL_1_2));
+ constant("SPV_ENV_OPENCL_EMBEDDED_1_2", static_cast<uint32_t>(SPV_ENV_OPENCL_EMBEDDED_1_2));
+ constant("SPV_ENV_OPENCL_2_0", static_cast<uint32_t>(SPV_ENV_OPENCL_2_0));
+ constant("SPV_ENV_OPENCL_EMBEDDED_2_0", static_cast<uint32_t>(SPV_ENV_OPENCL_EMBEDDED_2_0));
+ constant("SPV_ENV_OPENCL_EMBEDDED_2_1", static_cast<uint32_t>(SPV_ENV_OPENCL_EMBEDDED_2_1));
+ constant("SPV_ENV_OPENCL_EMBEDDED_2_2", static_cast<uint32_t>(SPV_ENV_OPENCL_EMBEDDED_2_2));
+ constant("SPV_ENV_UNIVERSAL_1_3", static_cast<uint32_t>(SPV_ENV_UNIVERSAL_1_3));
+ constant("SPV_ENV_VULKAN_1_1", static_cast<uint32_t>(SPV_ENV_VULKAN_1_1));
+ constant("SPV_ENV_WEBGPU_0", static_cast<uint32_t>(SPV_ENV_WEBGPU_0));
+ constant("SPV_ENV_UNIVERSAL_1_4", static_cast<uint32_t>(SPV_ENV_UNIVERSAL_1_4));
+ constant("SPV_ENV_VULKAN_1_1_SPIRV_1_4", static_cast<uint32_t>(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+ constant("SPV_ENV_UNIVERSAL_1_5", static_cast<uint32_t>(SPV_ENV_UNIVERSAL_1_5));
+ constant("SPV_ENV_VULKAN_1_2", static_cast<uint32_t>(SPV_ENV_VULKAN_1_2));
+ constant("SPV_ENV_UNIVERSAL_1_6",
+ static_cast<uint32_t>(SPV_ENV_UNIVERSAL_1_6));
+
+ constant("SPV_BINARY_TO_TEXT_OPTION_NONE", static_cast<uint32_t>(SPV_BINARY_TO_TEXT_OPTION_NONE));
+ constant("SPV_BINARY_TO_TEXT_OPTION_PRINT", static_cast<uint32_t>(SPV_BINARY_TO_TEXT_OPTION_PRINT));
+ constant("SPV_BINARY_TO_TEXT_OPTION_COLOR", static_cast<uint32_t>(SPV_BINARY_TO_TEXT_OPTION_COLOR));
+ constant("SPV_BINARY_TO_TEXT_OPTION_INDENT", static_cast<uint32_t>(SPV_BINARY_TO_TEXT_OPTION_INDENT));
+ constant("SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET", static_cast<uint32_t>(SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET));
+ constant("SPV_BINARY_TO_TEXT_OPTION_NO_HEADER", static_cast<uint32_t>(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER));
+ constant("SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES", static_cast<uint32_t>(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES));
+
+ constant("SPV_TEXT_TO_BINARY_OPTION_NONE", static_cast<uint32_t>(SPV_TEXT_TO_BINARY_OPTION_NONE));
+ constant("SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS", static_cast<uint32_t>(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS));
+}
diff --git a/source/wasm/spirv-tools.d.ts b/source/wasm/spirv-tools.d.ts
new file mode 100644
index 00000000..c06bdf18
--- /dev/null
+++ b/source/wasm/spirv-tools.d.ts
@@ -0,0 +1,57 @@
+// Copyright (c) 2020 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.
+
+declare interface SpirvTools {
+ as(input: string, env: number, options: number): Uint8Array;
+ dis(input: Uint8Array, env: number, options: number): string;
+
+ SPV_ENV_UNIVERSAL_1_0: number;
+ SPV_ENV_VULKAN_1_0: number;
+ SPV_ENV_UNIVERSAL_1_1: number;
+ SPV_ENV_OPENCL_2_1: number;
+ SPV_ENV_OPENCL_2_2: number;
+ SPV_ENV_OPENGL_4_0: number;
+ SPV_ENV_OPENGL_4_1: number;
+ SPV_ENV_OPENGL_4_2: number;
+ SPV_ENV_OPENGL_4_3: number;
+ SPV_ENV_OPENGL_4_5: number;
+ SPV_ENV_UNIVERSAL_1_2: number;
+ SPV_ENV_OPENCL_1_2: number;
+ SPV_ENV_OPENCL_EMBEDDED_1_2: number;
+ SPV_ENV_OPENCL_2_0: number;
+ SPV_ENV_OPENCL_EMBEDDED_2_0: number;
+ SPV_ENV_OPENCL_EMBEDDED_2_1: number;
+ SPV_ENV_OPENCL_EMBEDDED_2_2: number;
+ SPV_ENV_UNIVERSAL_1_3: number;
+ SPV_ENV_VULKAN_1_1: number;
+ SPV_ENV_WEBGPU_0: number;
+ SPV_ENV_UNIVERSAL_1_4: number;
+ SPV_ENV_VULKAN_1_1_SPIRV_1_4: number;
+ SPV_ENV_UNIVERSAL_1_5: number;
+ SPV_ENV_VULKAN_1_2: number;
+ SPV_ENV_UNIVERSAL_1_6: number;
+
+ SPV_TEXT_TO_BINARY_OPTION_NONE: number;
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS: number;
+
+ SPV_BINARY_TO_TEXT_OPTION_NONE: number;
+ SPV_BINARY_TO_TEXT_OPTION_PRINT: number;
+ SPV_BINARY_TO_TEXT_OPTION_COLOR: number;
+ SPV_BINARY_TO_TEXT_OPTION_INDENT: number;
+ SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET: number;
+ SPV_BINARY_TO_TEXT_OPTION_NO_HEADER: number;
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES: number;
+}
+
+export default function (): Promise<SpirvTools>;
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 8ede58bf..e88df04d 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -186,9 +186,11 @@ endif()
add_subdirectory(link)
+add_subdirectory(lint)
add_subdirectory(opt)
add_subdirectory(reduce)
add_subdirectory(fuzz)
add_subdirectory(tools)
add_subdirectory(util)
add_subdirectory(val)
+add_subdirectory(fuzzers)
diff --git a/test/binary_header_get_test.cpp b/test/binary_header_get_test.cpp
index 3ce0b63a..f94f0c1a 100644
--- a/test/binary_header_get_test.cpp
+++ b/test/binary_header_get_test.cpp
@@ -51,8 +51,8 @@ TEST_F(BinaryHeaderGet, Default) {
ASSERT_EQ(SPV_SUCCESS, spvBinaryHeaderGet(&const_bin, endian, &header));
ASSERT_EQ(static_cast<uint32_t>(SpvMagicNumber), header.magic);
- // Expect SPIRV-Headers updated to SPIR-V 1.5.
- ASSERT_EQ(0x00010500u, header.version);
+ // Expect SPIRV-Headers updated to SPIR-V 1.6.
+ ASSERT_EQ(0x00010600u, header.version);
ASSERT_EQ(static_cast<uint32_t>(SPV_GENERATOR_CODEPLAY), header.generator);
ASSERT_EQ(1u, header.bound);
ASSERT_EQ(0u, header.schema);
diff --git a/test/binary_parse_test.cpp b/test/binary_parse_test.cpp
index 9a13f22c..f0810a35 100644
--- a/test/binary_parse_test.cpp
+++ b/test/binary_parse_test.cpp
@@ -203,16 +203,7 @@ class BinaryParseTest : public spvtest::TextToBinaryTestBase<::testing::Test> {
void Parse(const SpirvVector& words, spv_result_t expected_result,
bool flip_words = false) {
SpirvVector flipped_words(words);
- SCOPED_TRACE(flip_words ? "Flipped Endianness" : "Normal Endianness");
- if (flip_words) {
- std::transform(flipped_words.begin(), flipped_words.end(),
- flipped_words.begin(), [](const uint32_t raw_word) {
- return spvFixWord(raw_word,
- I32_ENDIAN_HOST == I32_ENDIAN_BIG
- ? SPV_ENDIANNESS_LITTLE
- : SPV_ENDIANNESS_BIG);
- });
- }
+ MaybeFlipWords(flip_words, flipped_words.begin(), flipped_words.end());
EXPECT_EQ(expected_result,
spvBinaryParse(ScopedContext().context, &client_,
flipped_words.data(), flipped_words.size(),
@@ -486,27 +477,27 @@ TEST_F(BinaryParseTest, EarlyReturnWithTwoPassingCallbacks) {
}
TEST_F(BinaryParseTest, InstructionWithStringOperand) {
- const std::string str =
- "the future is already here, it's just not evenly distributed";
- const auto str_words = MakeVector(str);
- const auto instruction = MakeInstruction(SpvOpName, {99}, str_words);
- const auto words = Concatenate({ExpectedHeaderForBound(100), instruction});
- InSequence calls_expected_in_specific_order;
- EXPECT_HEADER(100).WillOnce(Return(SPV_SUCCESS));
- const auto operands = std::vector<spv_parsed_operand_t>{
- MakeSimpleOperand(1, SPV_OPERAND_TYPE_ID),
- MakeLiteralStringOperand(2, static_cast<uint16_t>(str_words.size()))};
- EXPECT_CALL(client_,
- Instruction(ParsedInstruction(spv_parsed_instruction_t{
- instruction.data(), static_cast<uint16_t>(instruction.size()),
- SpvOpName, SPV_EXT_INST_TYPE_NONE, 0 /*type id*/,
- 0 /* No result id for OpName*/, operands.data(),
- static_cast<uint16_t>(operands.size())})))
- .WillOnce(Return(SPV_SUCCESS));
- // Since we are actually checking the output, don't test the
- // endian-swapped version.
- Parse(words, SPV_SUCCESS, false);
- EXPECT_EQ(nullptr, diagnostic_);
+ for (bool endian_swap : kSwapEndians) {
+ const std::string str =
+ "the future is already here, it's just not evenly distributed";
+ const auto str_words = MakeVector(str);
+ const auto instruction = MakeInstruction(SpvOpName, {99}, str_words);
+ const auto words = Concatenate({ExpectedHeaderForBound(100), instruction});
+ InSequence calls_expected_in_specific_order;
+ EXPECT_HEADER(100).WillOnce(Return(SPV_SUCCESS));
+ const auto operands = std::vector<spv_parsed_operand_t>{
+ MakeSimpleOperand(1, SPV_OPERAND_TYPE_ID),
+ MakeLiteralStringOperand(2, static_cast<uint16_t>(str_words.size()))};
+ EXPECT_CALL(client_, Instruction(ParsedInstruction(spv_parsed_instruction_t{
+ instruction.data(),
+ static_cast<uint16_t>(instruction.size()),
+ SpvOpName, SPV_EXT_INST_TYPE_NONE, 0 /*type id*/,
+ 0 /* No result id for OpName*/, operands.data(),
+ static_cast<uint16_t>(operands.size())})))
+ .WillOnce(Return(SPV_SUCCESS));
+ Parse(words, SPV_SUCCESS, endian_swap);
+ EXPECT_EQ(nullptr, diagnostic_);
+ }
}
// Checks for non-zero values for the result_id and ext_inst_type members
@@ -613,7 +604,7 @@ INSTANTIATE_TEST_SUITE_P(
MakeInstruction(SpvOpNop, {42})}),
"Invalid instruction OpNop starting at word 5: expected "
"no more operands after 1 words, but stated word count is 2."},
- // Supply several more unexpectd words.
+ // Supply several more unexpected words.
{Concatenate({ExpectedHeaderForBound(1),
MakeInstruction(SpvOpNop, {42, 43, 44, 45, 46, 47})}),
"Invalid instruction OpNop starting at word 5: expected "
diff --git a/test/binary_to_text.literal_test.cpp b/test/binary_to_text.literal_test.cpp
index 02daac76..5956984b 100644
--- a/test/binary_to_text.literal_test.cpp
+++ b/test/binary_to_text.literal_test.cpp
@@ -27,8 +27,15 @@ using ::testing::Eq;
using RoundTripLiteralsTest =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<std::string>>;
+static const bool kSwapEndians[] = {false, true};
+
TEST_P(RoundTripLiteralsTest, Sample) {
- EXPECT_THAT(EncodeAndDecodeSuccessfully(GetParam()), Eq(GetParam()));
+ for (bool endian_swap : kSwapEndians) {
+ EXPECT_THAT(
+ EncodeAndDecodeSuccessfully(GetParam(), SPV_BINARY_TO_TEXT_OPTION_NONE,
+ SPV_ENV_UNIVERSAL_1_0, endian_swap),
+ Eq(GetParam()));
+ }
}
// clang-format off
@@ -58,8 +65,12 @@ using RoundTripSpecialCaseLiteralsTest = spvtest::TextToBinaryTestBase<
// Test case where the generated disassembly is not the same as the
// assembly passed in.
TEST_P(RoundTripSpecialCaseLiteralsTest, Sample) {
- EXPECT_THAT(EncodeAndDecodeSuccessfully(std::get<0>(GetParam())),
- Eq(std::get<1>(GetParam())));
+ for (bool endian_swap : kSwapEndians) {
+ EXPECT_THAT(EncodeAndDecodeSuccessfully(std::get<0>(GetParam()),
+ SPV_BINARY_TO_TEXT_OPTION_NONE,
+ SPV_ENV_UNIVERSAL_1_0, endian_swap),
+ Eq(std::get<1>(GetParam())));
+ }
}
// clang-format off
diff --git a/test/binary_to_text_test.cpp b/test/binary_to_text_test.cpp
index 9cad9661..df703e5d 100644
--- a/test/binary_to_text_test.cpp
+++ b/test/binary_to_text_test.cpp
@@ -386,7 +386,7 @@ INSTANTIATE_TEST_SUITE_P(
::testing::ValuesIn(std::vector<std::string>{
"OpExecutionModeId %1 SubgroupsPerWorkgroupId %2\n",
"OpExecutionModeId %1 LocalSizeId %2 %3 %4\n",
- "OpExecutionModeId %1 LocalSizeHintId %2\n",
+ "OpExecutionModeId %1 LocalSizeHintId %2 %3 %4\n",
"OpDecorateId %1 AlignmentId %2\n",
"OpDecorateId %1 MaxByteOffsetId %2\n",
})));
diff --git a/test/c_interface_test.cpp b/test/c_interface_test.cpp
index 841bb2c6..1562057f 100644
--- a/test/c_interface_test.cpp
+++ b/test/c_interface_test.cpp
@@ -117,7 +117,7 @@ TEST(CInterface, SpecifyConsumerNullDiagnosticForAssembling) {
const spv_position_t& position, const char* message) {
++invocation;
EXPECT_EQ(SPV_MSG_ERROR, level);
- // The error happens at scanning the begining of second line.
+ // The error happens at scanning the beginning of second line.
EXPECT_STREQ("input", source);
EXPECT_EQ(1u, position.line);
EXPECT_EQ(0u, position.column);
diff --git a/test/ext_inst.cldebug100_test.cpp b/test/ext_inst.cldebug100_test.cpp
index 4f1e1067..0bbdd3a9 100644
--- a/test/ext_inst.cldebug100_test.cpp
+++ b/test/ext_inst.cldebug100_test.cpp
@@ -581,7 +581,7 @@ INSTANTIATE_TEST_SUITE_P(
OpenCLDebugInfo100DebugSource, ExtInstCLDebugInfo100RoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
// TODO(dneto): Should this be a list of sourc texts,
- // to accomodate length limits?
+ // to accommodate length limits?
CASE_I(Source),
CASE_II(Source),
})));
diff --git a/test/ext_inst.opencl_test.cpp b/test/ext_inst.opencl_test.cpp
index 7547d922..d80a9bd7 100644
--- a/test/ext_inst.opencl_test.cpp
+++ b/test/ext_inst.opencl_test.cpp
@@ -233,7 +233,7 @@ INSTANTIATE_TEST_SUITE_P(
CASE3(UMad_hi, u_mad_hi), // enum value 204
})));
-// OpenCL.std: 2.3 Common instrucitons
+// OpenCL.std: 2.3 Common instructions
INSTANTIATE_TEST_SUITE_P(
OpenCLCommon, ExtInstOpenCLStdRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 8acebde6..56af0b9d 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -122,6 +122,7 @@ if (${SPIRV_BUILD_FUZZER})
transformation_vector_shuffle_test.cpp
transformation_wrap_early_terminator_in_function_test.cpp
transformation_wrap_region_in_selection_test.cpp
+ transformation_wrap_vector_synonym_test.cpp
uniform_buffer_element_descriptor_test.cpp)
if (${SPIRV_ENABLE_LONG_FUZZER_TESTS})
diff --git a/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp b/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp
index 734f47af..1045f8a1 100644
--- a/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp
+++ b/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp
@@ -134,7 +134,7 @@ TEST(FuzzerPassAddOpPhiSynonymsTest, HelperFunctions) {
FuzzerPassAddOpPhiSynonyms fuzzer_pass(context.get(), &transformation_context,
&fuzzer_context,
- &transformation_sequence);
+ &transformation_sequence, false);
SetUpIdSynonyms(transformation_context.GetFactManager());
diff --git a/test/fuzz/fuzzer_pass_construct_composites_test.cpp b/test/fuzz/fuzzer_pass_construct_composites_test.cpp
index a02176b2..a858e4ce 100644
--- a/test/fuzz/fuzzer_pass_construct_composites_test.cpp
+++ b/test/fuzz/fuzzer_pass_construct_composites_test.cpp
@@ -92,7 +92,7 @@ TEST(FuzzerPassConstructCompositesTest, IsomorphicStructs) {
FuzzerPassConstructComposites fuzzer_pass(
context.get(), &transformation_context, &fuzzer_context,
- &transformation_sequence);
+ &transformation_sequence, false);
fuzzer_pass.Apply();
@@ -173,7 +173,7 @@ TEST(FuzzerPassConstructCompositesTest, IsomorphicArrays) {
FuzzerPassConstructComposites fuzzer_pass(
context.get(), &transformation_context, &fuzzer_context,
- &transformation_sequence);
+ &transformation_sequence, false);
fuzzer_pass.Apply();
diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp
index f11885d4..fe8e671d 100644
--- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp
+++ b/test/fuzz/fuzzer_pass_donate_modules_test.cpp
@@ -210,7 +210,7 @@ TEST(FuzzerPassDonateModulesTest, BasicDonation) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), false);
@@ -291,7 +291,7 @@ TEST(FuzzerPassDonateModulesTest, DonationWithUniforms) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), false);
@@ -422,7 +422,7 @@ TEST(FuzzerPassDonateModulesTest, DonationWithInputAndOutputVariables) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), false);
@@ -517,7 +517,7 @@ TEST(FuzzerPassDonateModulesTest, DonateFunctionTypeWithDifferentPointers) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), false);
@@ -531,6 +531,7 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) {
std::string recipient_shader = R"(
OpCapability Shader
OpCapability ImageQuery
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
@@ -548,6 +549,7 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) {
std::string donor_shader = R"(
OpCapability Shader
OpCapability ImageQuery
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
@@ -587,7 +589,7 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), false);
@@ -715,7 +717,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImages) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), false);
@@ -811,7 +813,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesSampler) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), false);
@@ -943,7 +945,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageStructField) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), false);
@@ -1079,7 +1081,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageFunctionParameter) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), false);
@@ -1161,7 +1163,7 @@ TEST(FuzzerPassDonateModulesTest, DonateShaderWithImageStorageClass) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), true);
@@ -1248,7 +1250,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArray) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), false);
@@ -1352,7 +1354,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArrayLivesafe) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), true);
@@ -1424,7 +1426,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithWorkgroupVariables) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), true);
@@ -1534,7 +1536,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithAtomics) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), true);
@@ -1718,7 +1720,7 @@ TEST(FuzzerPassDonateModulesTest, Miscellaneous1) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), false);
@@ -1790,7 +1792,7 @@ TEST(FuzzerPassDonateModulesTest, OpSpecConstantInstructions) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), false);
@@ -1947,7 +1949,7 @@ TEST(FuzzerPassDonateModulesTest, DonationSupportsOpTypeRuntimeArray) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), false);
@@ -2020,7 +2022,7 @@ TEST(FuzzerPassDonateModulesTest, HandlesCapabilities) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
ASSERT_TRUE(donor_context->get_feature_mgr()->HasCapability(
SpvCapabilityVariablePointersStorageBuffer));
@@ -2253,7 +2255,7 @@ TEST(FuzzerPassDonateModulesTest, HandlesOpPhisInMergeBlock) {
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
- &transformation_sequence, {});
+ &transformation_sequence, false, {});
fuzzer_pass.DonateSingleModule(donor_context.get(), true);
diff --git a/test/fuzz/fuzzer_pass_outline_functions_test.cpp b/test/fuzz/fuzzer_pass_outline_functions_test.cpp
index 0d2c5bf7..a088e17b 100644
--- a/test/fuzz/fuzzer_pass_outline_functions_test.cpp
+++ b/test/fuzz/fuzzer_pass_outline_functions_test.cpp
@@ -130,7 +130,7 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryIsAlreadySuitable) {
FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context,
&fuzzer_context,
- &transformation_sequence);
+ &transformation_sequence, false);
// Block 28
auto suitable_entry_block =
@@ -173,7 +173,7 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryHasOpVariable) {
FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context,
&fuzzer_context,
- &transformation_sequence);
+ &transformation_sequence, false);
// Block 20
auto suitable_entry_block =
@@ -297,7 +297,7 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryBlockIsHeader) {
FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context,
&fuzzer_context,
- &transformation_sequence);
+ &transformation_sequence, false);
// Block 21
auto suitable_entry_block =
@@ -464,7 +464,7 @@ TEST(FuzzerPassOutlineFunctionsTest, ExitBlock) {
FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context,
&fuzzer_context,
- &transformation_sequence);
+ &transformation_sequence, false);
// Block 39 is not a merge block, so it is already suitable.
auto suitable_exit_block = fuzzer_pass.MaybeGetExitBlockSuitableForOutlining(
diff --git a/test/fuzz/fuzzer_pass_test.cpp b/test/fuzz/fuzzer_pass_test.cpp
index b035de74..31b8582a 100644
--- a/test/fuzz/fuzzer_pass_test.cpp
+++ b/test/fuzz/fuzzer_pass_test.cpp
@@ -29,7 +29,7 @@ class FuzzerPassMock : public FuzzerPass {
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
- transformations) {}
+ transformations, false) {}
~FuzzerPassMock() override = default;
diff --git a/test/fuzz/fuzzer_replayer_test.cpp b/test/fuzz/fuzzer_replayer_test.cpp
index 6dc7ffb1..2a22e6a5 100644
--- a/test/fuzz/fuzzer_replayer_test.cpp
+++ b/test/fuzz/fuzzer_replayer_test.cpp
@@ -1596,6 +1596,23 @@ const std::string kTestShader6 = R"(
OpFunctionEnd
)";
+// A virtually empty piece of SPIR-V.
+
+const std::string kTestShader7 = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
void AddConstantUniformFact(protobufs::FactSequence* facts,
uint32_t descriptor_set, uint32_t binding,
std::vector<uint32_t>&& indices, uint32_t value) {
@@ -1668,7 +1685,7 @@ void RunFuzzerAndReplayer(const std::string& shader,
Fuzzer fuzzer(std::move(ir_context), std::move(transformation_context),
std::move(fuzzer_context), kConsoleMessageConsumer,
donor_suppliers, enable_all_passes,
- strategies[strategy_index], true, validator_options);
+ strategies[strategy_index], true, validator_options, false);
auto fuzzer_result = fuzzer.Run(0);
// Cycle the repeated pass strategy so that we try a different one next time
@@ -1760,6 +1777,13 @@ TEST(FuzzerReplayerTest, Miscellaneous6) {
kNumFuzzerRuns);
}
+TEST(FuzzerReplayerTest, Miscellaneous7) {
+ // Do some fuzzer runs, starting from an initial seed of 1 (seed value chosen
+ // arbitrarily).
+ RunFuzzerAndReplayer(kTestShader7, protobufs::FactSequence(), 1,
+ kNumFuzzerRuns);
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools
diff --git a/test/fuzz/fuzzer_shrinker_test.cpp b/test/fuzz/fuzzer_shrinker_test.cpp
index e7921169..acee03c7 100644
--- a/test/fuzz/fuzzer_shrinker_test.cpp
+++ b/test/fuzz/fuzzer_shrinker_test.cpp
@@ -1068,7 +1068,7 @@ void RunFuzzerAndShrinker(const std::string& shader,
Fuzzer fuzzer(std::move(ir_context), std::move(transformation_context),
std::move(fuzzer_context), kConsoleMessageConsumer,
donor_suppliers, enable_all_passes, repeated_pass_strategy,
- true, validator_options);
+ true, validator_options, false);
auto fuzzer_result = fuzzer.Run(0);
ASSERT_NE(Fuzzer::Status::kFuzzerPassLedToInvalidModule,
fuzzer_result.status);
diff --git a/test/fuzz/fuzzerutil_test.cpp b/test/fuzz/fuzzerutil_test.cpp
index 7ff9b4d8..1286d38d 100644
--- a/test/fuzz/fuzzerutil_test.cpp
+++ b/test/fuzz/fuzzerutil_test.cpp
@@ -70,7 +70,7 @@ TEST(FuzzerUtilMaybeFindBlockTest, BasicTest) {
ASSERT_TRUE(fuzzerutil::MaybeFindBlock(ir_context, block_id2) != nullptr);
// Block with id 13 cannot be found.
ASSERT_FALSE(fuzzerutil::MaybeFindBlock(ir_context, block_id3) != nullptr);
- // Block with id 8 exisits but don't not of type OpLabel.
+ // Block with id 8 exists but don't not of type OpLabel.
ASSERT_FALSE(fuzzerutil::MaybeFindBlock(ir_context, block_id4) != nullptr);
}
@@ -196,8 +196,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetBoolTypeTest) {
%50 = OpTypePointer Private %7
%34 = OpTypeBool
%35 = OpConstantFalse %34
- %60 = OpConstantNull %50
- %61 = OpUndef %51
%52 = OpVariable %50 Private
%53 = OpVariable %51 Private
%80 = OpConstantComposite %8 %21 %24
@@ -507,8 +505,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetFloatTypeTest) {
%50 = OpTypePointer Private %7
%34 = OpTypeBool
%35 = OpConstantFalse %34
- %60 = OpConstantNull %50
- %61 = OpUndef %51
%52 = OpVariable %50 Private
%53 = OpVariable %51 Private
%80 = OpConstantComposite %8 %21 %24
@@ -799,8 +795,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetIntegerTypeTest) {
%50 = OpTypePointer Private %7
%34 = OpTypeBool
%35 = OpConstantFalse %34
- %60 = OpConstantNull %50
- %61 = OpUndef %51
%52 = OpVariable %50 Private
%53 = OpVariable %51 Private
%80 = OpConstantComposite %8 %21 %24
@@ -894,8 +888,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetPointerTypeTest) {
%50 = OpTypePointer Private %7
%34 = OpTypeBool
%35 = OpConstantFalse %34
- %60 = OpConstantNull %50
- %61 = OpUndef %51
%52 = OpVariable %50 Private
%53 = OpVariable %51 Private
%80 = OpConstantComposite %8 %21 %24
@@ -973,7 +965,7 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetPointerTypeTest) {
ASSERT_EQ(
91, fuzzerutil::MaybeGetPointerType(ir_context, 90, input_storage_class));
- // A pointer with id=91 and pointee type 90 exisits, but the type should be
+ // A pointer with id=91 and pointee type 90 exists, but the type should be
// input.
ASSERT_EQ(0, fuzzerutil::MaybeGetPointerType(ir_context, 90,
function_storage_class));
@@ -1165,8 +1157,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetStructTypeTest) {
%50 = OpTypePointer Private %7
%34 = OpTypeBool
%35 = OpConstantFalse %34
- %60 = OpConstantNull %50
- %61 = OpUndef %51
%52 = OpVariable %50 Private
%53 = OpVariable %51 Private
%80 = OpConstantComposite %8 %21 %24
@@ -1261,8 +1251,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetVectorTypeTest) {
%50 = OpTypePointer Private %7
%34 = OpTypeBool
%35 = OpConstantFalse %34
- %60 = OpConstantNull %50
- %61 = OpUndef %51
%52 = OpVariable %50 Private
%53 = OpVariable %51 Private
%80 = OpConstantComposite %8 %21 %24
@@ -1362,8 +1350,6 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetVoidTypeTest) {
%50 = OpTypePointer Private %7
%34 = OpTypeBool
%35 = OpConstantFalse %34
- %60 = OpConstantNull %50
- %61 = OpUndef %51
%52 = OpVariable %50 Private
%53 = OpVariable %51 Private
%80 = OpConstantComposite %8 %21 %24
@@ -1563,6 +1549,259 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetZeroConstantTest) {
float_ids.end());
}
+TEST(FuzzerutilTest, TypesAreCompatible) {
+ const std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %9 = OpTypeInt 32 0
+ %8 = OpTypeStruct %6
+ %10 = OpTypePointer StorageBuffer %8
+ %11 = OpVariable %10 StorageBuffer
+ %86 = OpTypeStruct %9
+ %87 = OpTypePointer Workgroup %86
+ %88 = OpVariable %87 Workgroup
+ %89 = OpTypePointer Workgroup %9
+ %19 = OpConstant %9 0
+ %18 = OpConstant %9 1
+ %12 = OpConstant %6 0
+ %13 = OpTypePointer StorageBuffer %6
+ %15 = OpConstant %6 2
+ %16 = OpConstant %6 7
+ %20 = OpConstant %9 64
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %14 = OpAccessChain %13 %11 %12
+ %90 = OpAccessChain %89 %88 %19
+ %21 = OpAtomicLoad %6 %14 %15 %20
+ %22 = OpAtomicExchange %6 %14 %15 %20 %16
+ %23 = OpAtomicCompareExchange %6 %14 %15 %20 %12 %16 %15
+ %24 = OpAtomicIIncrement %6 %14 %15 %20
+ %25 = OpAtomicIDecrement %6 %14 %15 %20
+ %26 = OpAtomicIAdd %6 %14 %15 %20 %16
+ %27 = OpAtomicISub %6 %14 %15 %20 %16
+ %28 = OpAtomicSMin %6 %14 %15 %20 %16
+ %29 = OpAtomicUMin %9 %90 %15 %20 %18
+ %30 = OpAtomicSMax %6 %14 %15 %20 %15
+ %31 = OpAtomicUMax %9 %90 %15 %20 %18
+ %32 = OpAtomicAnd %6 %14 %15 %20 %16
+ %33 = OpAtomicOr %6 %14 %15 %20 %16
+ %34 = OpAtomicXor %6 %14 %15 %20 %16
+ OpAtomicStore %14 %15 %20 %16
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ spvtools::ValidatorOptions validator_options;
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+
+ const uint32_t int_type = 6; // The id of OpTypeInt 32 1
+ const uint32_t uint_type = 9; // The id of OpTypeInt 32 0
+
+ // OpAtomicLoad
+#ifndef NDEBUG
+ ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 0,
+ int_type, uint_type),
+ "Signedness check should not occur on a pointer operand.");
+#endif
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 1,
+ int_type, uint_type));
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 2,
+ int_type, uint_type));
+
+ // OpAtomicExchange
+#ifndef NDEBUG
+ ASSERT_DEATH(fuzzerutil::TypesAreCompatible(
+ context.get(), SpvOpAtomicExchange, 0, int_type, uint_type),
+ "Signedness check should not occur on a pointer operand.");
+#endif
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicExchange,
+ 1, int_type, uint_type));
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicExchange,
+ 2, int_type, uint_type));
+ ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
+ context.get(), SpvOpAtomicExchange, 3, int_type, uint_type));
+
+ // OpAtomicStore
+#ifndef NDEBUG
+ ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore,
+ 0, int_type, uint_type),
+ "Signedness check should not occur on a pointer operand.");
+#endif
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 1,
+ int_type, uint_type));
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 2,
+ int_type, uint_type));
+ ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore,
+ 3, int_type, uint_type));
+
+ // OpAtomicCompareExchange
+#ifndef NDEBUG
+ ASSERT_DEATH(
+ fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicCompareExchange,
+ 0, int_type, uint_type),
+ "Signedness check should not occur on a pointer operand.");
+#endif
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+ context.get(), SpvOpAtomicCompareExchange, 1, int_type, uint_type));
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+ context.get(), SpvOpAtomicCompareExchange, 2, int_type, uint_type));
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+ context.get(), SpvOpAtomicCompareExchange, 3, int_type, uint_type));
+ ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
+ context.get(), SpvOpAtomicCompareExchange, 4, int_type, uint_type));
+
+ // OpAtomicIIncrement
+#ifndef NDEBUG
+ ASSERT_DEATH(
+ fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIIncrement, 0,
+ int_type, uint_type),
+ "Signedness check should not occur on a pointer operand.");
+#endif
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+ context.get(), SpvOpAtomicIIncrement, 1, int_type, uint_type));
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
+ context.get(), SpvOpAtomicIIncrement, 2, int_type, uint_type));
+
+// OpAtomicIDecrement
+#ifndef NDEBUG
+ ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore,
+ 0, int_type, uint_type),
+ "Signedness check should not occur on a pointer operand.");
+#endif
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 1,
+ int_type, uint_type));
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 2,
+ int_type, uint_type));
+
+// OpAtomicIAdd
+#ifndef NDEBUG
+ ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 0,
+ int_type, uint_type),
+ "Signedness check should not occur on a pointer operand.");
+#endif
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 1,
+ int_type, uint_type));
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 2,
+ int_type, uint_type));
+ ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 3,
+ int_type, uint_type));
+
+// OpAtomicISub
+#ifndef NDEBUG
+ ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 0,
+ int_type, uint_type),
+ "Signedness check should not occur on a pointer operand.");
+#endif
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 1,
+ int_type, uint_type));
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 2,
+ int_type, uint_type));
+ ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 3,
+ int_type, uint_type));
+
+// OpAtomicSMin
+#ifndef NDEBUG
+ ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 0,
+ int_type, uint_type),
+ "Signedness check should not occur on a pointer operand.");
+#endif
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 1,
+ int_type, uint_type));
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 2,
+ int_type, uint_type));
+ ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 3,
+ int_type, uint_type));
+
+// OpAtomicUMin
+#ifndef NDEBUG
+ ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 0,
+ int_type, uint_type),
+ "Signedness check should not occur on a pointer operand.");
+#endif
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 1,
+ int_type, uint_type));
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 2,
+ int_type, uint_type));
+ ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 3,
+ int_type, uint_type));
+
+// OpAtomicSMax
+#ifndef NDEBUG
+ ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 0,
+ int_type, uint_type),
+ "Signedness check should not occur on a pointer operand.");
+#endif
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 1,
+ int_type, uint_type));
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 2,
+ int_type, uint_type));
+ ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 3,
+ int_type, uint_type));
+
+// OpAtomicUMax
+#ifndef NDEBUG
+ ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 0,
+ int_type, uint_type),
+ "Signedness check should not occur on a pointer operand.");
+#endif
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 1,
+ int_type, uint_type));
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 2,
+ int_type, uint_type));
+ ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 3,
+ int_type, uint_type));
+
+// OpAtomicAnd
+#ifndef NDEBUG
+ ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 0,
+ int_type, uint_type),
+ "Signedness check should not occur on a pointer operand.");
+#endif
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 1,
+ int_type, uint_type));
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 2,
+ int_type, uint_type));
+ ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 3,
+ int_type, uint_type));
+
+// OpAtomicOr
+#ifndef NDEBUG
+ ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 0,
+ int_type, uint_type),
+ "Signedness check should not occur on a pointer operand.");
+#endif
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 1,
+ int_type, uint_type));
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 2,
+ int_type, uint_type));
+ ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 3,
+ int_type, uint_type));
+
+// OpAtomicXor
+#ifndef NDEBUG
+ ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 0,
+ int_type, uint_type),
+ "Signedness check should not occur on a pointer operand.");
+#endif
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 1,
+ int_type, uint_type));
+ ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 2,
+ int_type, uint_type));
+ ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 3,
+ int_type, uint_type));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools
diff --git a/test/fuzz/shrinker_test.cpp b/test/fuzz/shrinker_test.cpp
index 447ebecd..942de29a 100644
--- a/test/fuzz/shrinker_test.cpp
+++ b/test/fuzz/shrinker_test.cpp
@@ -171,7 +171,7 @@ TEST(ShrinkerTest, ReduceAddedFunctions) {
protobufs::TransformationSequence transformations;
FuzzerPassDonateModules pass(variant_ir_context.get(),
&transformation_context, &fuzzer_context,
- &transformations, {});
+ &transformations, false, {});
pass.DonateSingleModule(donor_ir_context.get(), true);
protobufs::FactSequence no_facts;
@@ -349,7 +349,7 @@ TEST(ShrinkerTest, HitStepLimitWhenReducingAddedFunctions) {
protobufs::TransformationSequence transformations;
FuzzerPassDonateModules pass(variant_ir_context.get(),
&transformation_context, &fuzzer_context,
- &transformations, {});
+ &transformations, false, {});
pass.DonateSingleModule(donor_ir_context.get(), true);
protobufs::FactSequence no_facts;
diff --git a/test/fuzz/transformation_access_chain_test.cpp b/test/fuzz/transformation_access_chain_test.cpp
index e7919816..bddcf5fe 100644
--- a/test/fuzz/transformation_access_chain_test.cpp
+++ b/test/fuzz/transformation_access_chain_test.cpp
@@ -26,6 +26,7 @@ namespace {
TEST(TransformationAccessChainTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %48 %54
@@ -63,7 +64,6 @@ TEST(TransformationAccessChainTest, BasicTest) {
%85 = OpConstant %10 5
%52 = OpTypeArray %50 %51
%53 = OpTypePointer Private %52
- %45 = OpUndef %9
%46 = OpConstantNull %9
%47 = OpTypePointer Private %8
%48 = OpVariable %47 Private
@@ -204,15 +204,6 @@ TEST(TransformationAccessChainTest, BasicTest) {
#ifndef NDEBUG
// Bad: pointer is null
ASSERT_DEATH(
- TransformationAccessChain(100, 45, {80},
- MakeInstructionDescriptor(24, SpvOpLoad, 0))
- .IsApplicable(context.get(), transformation_context),
- "Access chains should not be created from null/undefined pointers");
-#endif
-
-#ifndef NDEBUG
- // Bad: pointer is undef
- ASSERT_DEATH(
TransformationAccessChain(100, 46, {80},
MakeInstructionDescriptor(24, SpvOpLoad, 0))
.IsApplicable(context.get(), transformation_context),
@@ -331,6 +322,7 @@ TEST(TransformationAccessChainTest, BasicTest) {
std::string after_transformation = R"(
OpCapability Shader
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %48 %54
@@ -368,7 +360,6 @@ TEST(TransformationAccessChainTest, BasicTest) {
%85 = OpConstant %10 5
%52 = OpTypeArray %50 %51
%53 = OpTypePointer Private %52
- %45 = OpUndef %9
%46 = OpConstantNull %9
%47 = OpTypePointer Private %8
%48 = OpVariable %47 Private
diff --git a/test/fuzz/transformation_add_copy_memory_test.cpp b/test/fuzz/transformation_add_copy_memory_test.cpp
index 642a5561..ff8ac72e 100644
--- a/test/fuzz/transformation_add_copy_memory_test.cpp
+++ b/test/fuzz/transformation_add_copy_memory_test.cpp
@@ -26,6 +26,7 @@ namespace {
TEST(TransformationAddCopyMemoryTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
@@ -64,7 +65,6 @@ TEST(TransformationAddCopyMemoryTest, BasicTest) {
%67 = OpTypePointer Function %66
%83 = OpTypePointer Private %66
%86 = OpVariable %79 Private %20
- %87 = OpUndef %79
%88 = OpConstantNull %79
%4 = OpFunction %2 None %3
%5 = OpLabel
@@ -182,12 +182,6 @@ TEST(TransformationAddCopyMemoryTest, BasicTest) {
90, 40, SpvStorageClassPrivate, 0)
.IsApplicable(context.get(), transformation_context));
- // Source instruction is OpUndef.
- ASSERT_FALSE(
- TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0),
- 90, 87, SpvStorageClassPrivate, 0)
- .IsApplicable(context.get(), transformation_context));
-
// Source instruction is OpConstantNull.
ASSERT_FALSE(
TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0),
@@ -255,6 +249,7 @@ TEST(TransformationAddCopyMemoryTest, BasicTest) {
std::string expected = R"(
OpCapability Shader
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
@@ -293,7 +288,6 @@ TEST(TransformationAddCopyMemoryTest, BasicTest) {
%67 = OpTypePointer Function %66
%83 = OpTypePointer Private %66
%86 = OpVariable %79 Private %20
- %87 = OpUndef %79
%88 = OpConstantNull %79
%90 = OpVariable %79 Private %20
%92 = OpVariable %78 Private %25
diff --git a/test/fuzz/transformation_add_parameter_test.cpp b/test/fuzz/transformation_add_parameter_test.cpp
index 2ae60c9d..7f069b55 100644
--- a/test/fuzz/transformation_add_parameter_test.cpp
+++ b/test/fuzz/transformation_add_parameter_test.cpp
@@ -367,7 +367,7 @@ TEST(TransformationAddParameterTest, NonPointerNotApplicableTest) {
transformation_bad_3.IsApplicable(context.get(), transformation_context));
// Function with id 14 does not have any callers.
- // Bad: Id 18 is not a vaild type.
+ // Bad: Id 18 is not a valid type.
TransformationAddParameter transformation_bad_4(14, 50, 18, {{}}, 51);
ASSERT_FALSE(
transformation_bad_4.IsApplicable(context.get(), transformation_context));
diff --git a/test/fuzz/transformation_add_synonym_test.cpp b/test/fuzz/transformation_add_synonym_test.cpp
index 314b0039..ffcf1c9b 100644
--- a/test/fuzz/transformation_add_synonym_test.cpp
+++ b/test/fuzz/transformation_add_synonym_test.cpp
@@ -1242,9 +1242,10 @@ TEST(TransformationAddSynonymTest, MiscellaneousCopies) {
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
-TEST(TransformationAddSynonymTest, DoNotCopyNullOrUndefPointers) {
+TEST(TransformationAddSynonymTest, DoNotCopyNullPointers) {
std::string shader = R"(
OpCapability Shader
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
@@ -1255,7 +1256,6 @@ TEST(TransformationAddSynonymTest, DoNotCopyNullOrUndefPointers) {
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpConstantNull %7
- %9 = OpUndef %7
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
@@ -1275,12 +1275,6 @@ TEST(TransformationAddSynonymTest, DoNotCopyNullOrUndefPointers) {
8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
MakeInstructionDescriptor(5, SpvOpReturn, 0))
.IsApplicable(context.get(), transformation_context));
-
- // Illegal to copy an OpUndef of pointer type.
- ASSERT_FALSE(TransformationAddSynonym(
- 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
- MakeInstructionDescriptor(5, SpvOpReturn, 0))
- .IsApplicable(context.get(), transformation_context));
}
TEST(TransformationAddSynonymTest, PropagateIrrelevantPointeeFact) {
diff --git a/test/fuzz/transformation_add_type_int_test.cpp b/test/fuzz/transformation_add_type_int_test.cpp
index ed8e00a6..4cbfed0f 100644
--- a/test/fuzz/transformation_add_type_int_test.cpp
+++ b/test/fuzz/transformation_add_type_int_test.cpp
@@ -87,7 +87,7 @@ TEST(TransformationAddTypeIntTest, IsApplicable) {
transformation.IsApplicable(context.get(), transformation_context));
// By default SPIR-V does not support 16-bit integers.
- // Below we add such capability, so the test should now be succesful.
+ // Below we add such capability, so the test should now be successful.
context.get()->get_feature_mgr()->AddCapability(SpvCapabilityInt16);
ASSERT_TRUE(TransformationAddTypeInt(7, 16, true)
.IsApplicable(context.get(), transformation_context));
diff --git a/test/fuzz/transformation_adjust_branch_weights_test.cpp b/test/fuzz/transformation_adjust_branch_weights_test.cpp
index 1bf2c593..5984a3e9 100644
--- a/test/fuzz/transformation_adjust_branch_weights_test.cpp
+++ b/test/fuzz/transformation_adjust_branch_weights_test.cpp
@@ -106,7 +106,7 @@ TEST(TransformationAdjustBranchWeightsTest, IsApplicableTest) {
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
- // Tests OpBranchConditional instruction with weigths.
+ // Tests OpBranchConditional instruction with weights.
auto instruction_descriptor =
MakeInstructionDescriptor(33, SpvOpBranchConditional, 0);
auto transformation =
diff --git a/test/fuzz/transformation_load_test.cpp b/test/fuzz/transformation_load_test.cpp
index a03ffdd6..370826eb 100644
--- a/test/fuzz/transformation_load_test.cpp
+++ b/test/fuzz/transformation_load_test.cpp
@@ -26,6 +26,7 @@ namespace {
TEST(TransformationLoadTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
@@ -49,7 +50,6 @@ TEST(TransformationLoadTest, BasicTest) {
%34 = OpTypeBool
%35 = OpConstantFalse %34
%60 = OpConstantNull %50
- %61 = OpUndef %51
%52 = OpVariable %50 Private
%53 = OpVariable %51 Private
%4 = OpFunction %2 None %3
@@ -129,65 +129,69 @@ TEST(TransformationLoadTest, BasicTest) {
// Pointers that cannot be used:
// 60 - null
- // 61 - undefined
// Bad: id is not fresh
- ASSERT_FALSE(TransformationLoad(
- 33, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationLoad(33, 33, false, 0, 0,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: attempt to load from 11 from outside its function
- ASSERT_FALSE(TransformationLoad(
- 100, 11, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationLoad(100, 11, false, 0, 0,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: pointer is not available
- ASSERT_FALSE(TransformationLoad(
- 100, 33, MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationLoad(100, 33, false, 0, 0,
+ MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: attempt to insert before OpVariable
- ASSERT_FALSE(TransformationLoad(
- 100, 27, MakeInstructionDescriptor(27, SpvOpVariable, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationLoad(100, 27, false, 0, 0,
+ MakeInstructionDescriptor(27, SpvOpVariable, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: pointer id does not exist
ASSERT_FALSE(
- TransformationLoad(100, 1000,
+ TransformationLoad(100, 1000, false, 0, 0,
MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
.IsApplicable(context.get(), transformation_context));
// Bad: pointer id exists but does not have a type
- ASSERT_FALSE(TransformationLoad(
- 100, 5, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationLoad(100, 5, false, 0, 0,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: pointer id exists and has a type, but is not a pointer
- ASSERT_FALSE(TransformationLoad(
- 100, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationLoad(100, 24, false, 0, 0,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: attempt to load from null pointer
- ASSERT_FALSE(TransformationLoad(
- 100, 60, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
-
- // Bad: attempt to load from undefined pointer
- ASSERT_FALSE(TransformationLoad(
- 100, 61, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
- // Bad: %40 is not available at the program point
ASSERT_FALSE(
- TransformationLoad(100, 40, MakeInstructionDescriptor(37, SpvOpReturn, 0))
+ TransformationLoad(100, 60, false, 0, 0,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
.IsApplicable(context.get(), transformation_context));
- // Bad: The described instruction does not exist
- ASSERT_FALSE(TransformationLoad(
- 100, 33, MakeInstructionDescriptor(1000, SpvOpReturn, 0))
+ // Bad: %40 is not available at the program point
+ ASSERT_FALSE(TransformationLoad(100, 40, false, 0, 0,
+ MakeInstructionDescriptor(37, SpvOpReturn, 0))
.IsApplicable(context.get(), transformation_context));
+ // Bad: The described instruction does not exist
+ ASSERT_FALSE(
+ TransformationLoad(100, 33, false, 0, 0,
+ MakeInstructionDescriptor(1000, SpvOpReturn, 0))
+ .IsApplicable(context.get(), transformation_context));
+
{
TransformationLoad transformation(
- 100, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
+ 100, 33, false, 0, 0,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
@@ -198,7 +202,8 @@ TEST(TransformationLoadTest, BasicTest) {
{
TransformationLoad transformation(
- 101, 46, MakeInstructionDescriptor(16, SpvOpReturnValue, 0));
+ 101, 46, false, 0, 0,
+ MakeInstructionDescriptor(16, SpvOpReturnValue, 0));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
@@ -209,7 +214,8 @@ TEST(TransformationLoadTest, BasicTest) {
{
TransformationLoad transformation(
- 102, 16, MakeInstructionDescriptor(16, SpvOpReturnValue, 0));
+ 102, 16, false, 0, 0,
+ MakeInstructionDescriptor(16, SpvOpReturnValue, 0));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
@@ -220,7 +226,8 @@ TEST(TransformationLoadTest, BasicTest) {
{
TransformationLoad transformation(
- 103, 40, MakeInstructionDescriptor(43, SpvOpAccessChain, 0));
+ 103, 40, false, 0, 0,
+ MakeInstructionDescriptor(43, SpvOpAccessChain, 0));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
@@ -231,6 +238,7 @@ TEST(TransformationLoadTest, BasicTest) {
std::string after_transformation = R"(
OpCapability Shader
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
@@ -254,7 +262,6 @@ TEST(TransformationLoadTest, BasicTest) {
%34 = OpTypeBool
%35 = OpConstantFalse %34
%60 = OpConstantNull %50
- %61 = OpUndef %51
%52 = OpVariable %50 Private
%53 = OpVariable %51 Private
%4 = OpFunction %2 None %3
@@ -293,6 +300,296 @@ TEST(TransformationLoadTest, BasicTest) {
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
+TEST(TransformationLoadTest, AtomicLoadTestCase) {
+ const std::string shader = R"(
+ OpCapability Shader
+ OpCapability Int8
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 8 1
+ %9 = OpTypeInt 32 0
+ %26 = OpTypeFloat 32
+ %8 = OpTypeStruct %6
+ %10 = OpTypePointer StorageBuffer %8
+ %11 = OpVariable %10 StorageBuffer
+ %19 = OpConstant %26 0
+ %18 = OpConstant %9 1
+ %12 = OpConstant %6 0
+ %13 = OpTypePointer StorageBuffer %6
+ %15 = OpConstant %6 4
+ %16 = OpConstant %6 7
+ %17 = OpConstant %7 4
+ %20 = OpConstant %9 64
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %14 = OpAccessChain %13 %11 %12
+ %24 = OpAccessChain %13 %11 %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ spvtools::ValidatorOptions validator_options;
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+
+ // Bad: id is not fresh.
+ ASSERT_FALSE(
+ TransformationLoad(14, 14, true, 15, 20,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: id 100 of memory scope instruction does not exist.
+ ASSERT_FALSE(
+ TransformationLoad(21, 14, true, 100, 20,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+ // Bad: id 100 of memory semantics instruction does not exist.
+ ASSERT_FALSE(
+ TransformationLoad(21, 14, true, 15, 100,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+ // Bad: memory scope should be |OpConstant| opcode.
+ ASSERT_FALSE(
+ TransformationLoad(21, 14, true, 5, 20,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+ // Bad: memory semantics should be |OpConstant| opcode.
+ ASSERT_FALSE(
+ TransformationLoad(21, 14, true, 15, 5,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: The memory scope instruction must have an Integer operand.
+ ASSERT_FALSE(
+ TransformationLoad(21, 14, true, 15, 19,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+ // Bad: The memory memory semantics instruction must have an Integer operand.
+ ASSERT_FALSE(
+ TransformationLoad(21, 14, true, 19, 20,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: Integer size of the memory scope must be equal to 32 bits.
+ ASSERT_FALSE(
+ TransformationLoad(21, 14, true, 17, 20,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: Integer size of memory semantics must be equal to 32 bits.
+ ASSERT_FALSE(
+ TransformationLoad(21, 14, true, 15, 17,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: memory scope value must be 4 (SpvScopeInvocation).
+ ASSERT_FALSE(
+ TransformationLoad(21, 14, true, 16, 20,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: memory semantics value must be either:
+ // 64 (SpvMemorySemanticsUniformMemoryMask)
+ // 256 (SpvMemorySemanticsWorkgroupMemoryMask)
+ ASSERT_FALSE(
+ TransformationLoad(21, 14, true, 15, 16,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: The described instruction does not exist
+ ASSERT_FALSE(
+ TransformationLoad(21, 14, false, 15, 20,
+ MakeInstructionDescriptor(150, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Successful transformations.
+ {
+ TransformationLoad transformation(
+ 21, 14, true, 15, 20,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0));
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(transformation, context.get(),
+ &transformation_context);
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+ context.get(), validator_options, kConsoleMessageConsumer));
+ }
+
+ const std::string after_transformation = R"(
+ OpCapability Shader
+ OpCapability Int8
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 8 1
+ %9 = OpTypeInt 32 0
+ %26 = OpTypeFloat 32
+ %8 = OpTypeStruct %6
+ %10 = OpTypePointer StorageBuffer %8
+ %11 = OpVariable %10 StorageBuffer
+ %19 = OpConstant %26 0
+ %18 = OpConstant %9 1
+ %12 = OpConstant %6 0
+ %13 = OpTypePointer StorageBuffer %6
+ %15 = OpConstant %6 4
+ %16 = OpConstant %6 7
+ %17 = OpConstant %7 4
+ %20 = OpConstant %9 64
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %14 = OpAccessChain %13 %11 %12
+ %21 = OpAtomicLoad %6 %14 %15 %20
+ %24 = OpAccessChain %13 %11 %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationLoadTest, AtomicLoadTestCaseForWorkgroupMemory) {
+ std::string shader = R"(
+ OpCapability Shader
+ OpCapability Int8
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %26 = OpTypeFloat 32
+ %27 = OpTypeInt 8 1
+ %7 = OpTypeInt 32 0 ; 0 means unsigned
+ %8 = OpConstant %7 0
+ %17 = OpConstant %27 4
+ %19 = OpConstant %26 0
+ %9 = OpTypePointer Function %6
+ %13 = OpTypeStruct %6
+ %12 = OpTypePointer Workgroup %13
+ %11 = OpVariable %12 Workgroup
+ %14 = OpConstant %6 0
+ %15 = OpTypePointer Function %6
+ %51 = OpTypePointer Private %6
+ %21 = OpConstant %6 4
+ %23 = OpConstant %6 256
+ %25 = OpTypePointer Function %7
+ %50 = OpTypePointer Workgroup %6
+ %34 = OpTypeBool
+ %35 = OpConstantFalse %34
+ %53 = OpVariable %51 Private
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpSelectionMerge %37 None
+ OpBranchConditional %35 %36 %37
+ %36 = OpLabel
+ %38 = OpAccessChain %50 %11 %14
+ %40 = OpAccessChain %50 %11 %14
+ OpBranch %37
+ %37 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ spvtools::ValidatorOptions validator_options;
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+
+ // Bad: Can't insert OpAccessChain before the id 23 of memory scope.
+ ASSERT_FALSE(
+ TransformationLoad(60, 38, true, 21, 23,
+ MakeInstructionDescriptor(23, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: Can't insert OpAccessChain before the id 23 of memory semantics.
+ ASSERT_FALSE(
+ TransformationLoad(60, 38, true, 21, 23,
+ MakeInstructionDescriptor(21, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Successful transformations.
+ {
+ TransformationLoad transformation(
+ 60, 38, true, 21, 23,
+ MakeInstructionDescriptor(40, SpvOpAccessChain, 0));
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(transformation, context.get(),
+ &transformation_context);
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+ context.get(), validator_options, kConsoleMessageConsumer));
+ }
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ OpCapability Int8
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %26 = OpTypeFloat 32
+ %27 = OpTypeInt 8 1
+ %7 = OpTypeInt 32 0 ; 0 means unsigned
+ %8 = OpConstant %7 0
+ %17 = OpConstant %27 4
+ %19 = OpConstant %26 0
+ %9 = OpTypePointer Function %6
+ %13 = OpTypeStruct %6
+ %12 = OpTypePointer Workgroup %13
+ %11 = OpVariable %12 Workgroup
+ %14 = OpConstant %6 0
+ %15 = OpTypePointer Function %6
+ %51 = OpTypePointer Private %6
+ %21 = OpConstant %6 4
+ %23 = OpConstant %6 256
+ %25 = OpTypePointer Function %7
+ %50 = OpTypePointer Workgroup %6
+ %34 = OpTypeBool
+ %35 = OpConstantFalse %34
+ %53 = OpVariable %51 Private
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpSelectionMerge %37 None
+ OpBranchConditional %35 %36 %37
+ %36 = OpLabel
+ %38 = OpAccessChain %50 %11 %14
+ %60 = OpAtomicLoad %6 %38 %21 %23
+ %40 = OpAccessChain %50 %11 %14
+ OpBranch %37
+ %37 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools
diff --git a/test/fuzz/transformation_mutate_pointer_test.cpp b/test/fuzz/transformation_mutate_pointer_test.cpp
index ae423100..e869efa6 100644
--- a/test/fuzz/transformation_mutate_pointer_test.cpp
+++ b/test/fuzz/transformation_mutate_pointer_test.cpp
@@ -26,6 +26,7 @@ namespace {
TEST(TransformationMutatePointerTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
@@ -60,7 +61,6 @@ TEST(TransformationMutatePointerTest, BasicTest) {
%23 = OpTypePointer Output %6
%24 = OpVariable %23 Output
%27 = OpTypeFunction %2 %13
- %32 = OpUndef %16
%33 = OpConstantNull %16
%4 = OpFunction %2 None %3
%5 = OpLabel
@@ -110,10 +110,6 @@ TEST(TransformationMutatePointerTest, BasicTest) {
ASSERT_FALSE(TransformationMutatePointer(11, 70, insert_before)
.IsApplicable(context.get(), transformation_context));
- // |pointer_id| is a result id of OpUndef.
- ASSERT_FALSE(TransformationMutatePointer(32, 70, insert_before)
- .IsApplicable(context.get(), transformation_context));
-
// |pointer_id| is a result id of OpConstantNull.
ASSERT_FALSE(TransformationMutatePointer(33, 70, insert_before)
.IsApplicable(context.get(), transformation_context));
@@ -163,6 +159,7 @@ TEST(TransformationMutatePointerTest, BasicTest) {
std::string after_transformation = R"(
OpCapability Shader
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
@@ -197,7 +194,6 @@ TEST(TransformationMutatePointerTest, BasicTest) {
%23 = OpTypePointer Output %6
%24 = OpVariable %23 Output
%27 = OpTypeFunction %2 %13
- %32 = OpUndef %16
%33 = OpConstantNull %16
%4 = OpFunction %2 None %3
%5 = OpLabel
diff --git a/test/fuzz/transformation_push_id_through_variable_test.cpp b/test/fuzz/transformation_push_id_through_variable_test.cpp
index 7487cab9..b0fff588 100644
--- a/test/fuzz/transformation_push_id_through_variable_test.cpp
+++ b/test/fuzz/transformation_push_id_through_variable_test.cpp
@@ -26,6 +26,7 @@ namespace {
TEST(TransformationPushIdThroughVariableTest, IsApplicable) {
std::string reference_shader = R"(
OpCapability Shader
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %92 %52 %53
@@ -50,7 +51,6 @@ TEST(TransformationPushIdThroughVariableTest, IsApplicable) {
%34 = OpTypeBool
%35 = OpConstantFalse %34
%60 = OpConstantNull %50
- %61 = OpUndef %51
%52 = OpVariable %50 Private
%53 = OpVariable %51 Private
%80 = OpConstantComposite %8 %21 %24
@@ -257,6 +257,7 @@ TEST(TransformationPushIdThroughVariableTest, IsApplicable) {
TEST(TransformationPushIdThroughVariableTest, Apply) {
std::string reference_shader = R"(
OpCapability Shader
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %92 %52 %53
@@ -281,7 +282,6 @@ TEST(TransformationPushIdThroughVariableTest, Apply) {
%34 = OpTypeBool
%35 = OpConstantFalse %34
%60 = OpConstantNull %50
- %61 = OpUndef %51
%52 = OpVariable %50 Private
%53 = OpVariable %51 Private
%80 = OpConstantComposite %8 %21 %24
@@ -419,6 +419,7 @@ TEST(TransformationPushIdThroughVariableTest, Apply) {
std::string variant_shader = R"(
OpCapability Shader
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %92 %52 %53 %109 %111
@@ -443,7 +444,6 @@ TEST(TransformationPushIdThroughVariableTest, Apply) {
%34 = OpTypeBool
%35 = OpConstantFalse %34
%60 = OpConstantNull %50
- %61 = OpUndef %51
%52 = OpVariable %50 Private
%53 = OpVariable %51 Private
%80 = OpConstantComposite %8 %21 %24
@@ -519,6 +519,7 @@ TEST(TransformationPushIdThroughVariableTest, Apply) {
TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds) {
std::string reference_shader = R"(
OpCapability Shader
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %92 %52 %53
@@ -543,7 +544,6 @@ TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds) {
%34 = OpTypeBool
%35 = OpConstantFalse %34
%60 = OpConstantNull %50
- %61 = OpUndef %51
%52 = OpVariable %50 Private
%53 = OpVariable %51 Private
%80 = OpConstantComposite %8 %21 %24
@@ -620,6 +620,7 @@ TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds) {
TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsForIrrelevantIds) {
std::string reference_shader = R"(
OpCapability Shader
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %92 %52 %53
@@ -644,7 +645,6 @@ TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsForIrrelevantIds) {
%34 = OpTypeBool
%35 = OpConstantFalse %34
%60 = OpConstantNull %50
- %61 = OpUndef %51
%52 = OpVariable %50 Private
%53 = OpVariable %51 Private
%80 = OpConstantComposite %8 %21 %24
diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
index 629a00ee..b33dd48c 100644
--- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp
+++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
@@ -1796,6 +1796,383 @@ TEST(TransformationReplaceIdWithSynonymTest, IncompatibleTypes) {
.IsApplicable(context.get(), transformation_context));
}
+TEST(TransformationReplaceIdWithSynonymTest,
+ AtomicScopeAndMemorySemanticsMustBeConstant) {
+ const std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 320
+ OpSourceExtension "GL_KHR_memory_scope_semantics"
+ OpMemberDecorate %9 0 Offset 0
+ OpDecorate %9 Block
+ OpDecorate %11 DescriptorSet 0
+ OpDecorate %11 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %17 = OpTypeInt 32 0
+ %7 = OpTypePointer Function %6
+ %9 = OpTypeStruct %6
+ %10 = OpTypePointer StorageBuffer %9
+ %11 = OpVariable %10 StorageBuffer
+ %86 = OpTypeStruct %17
+ %87 = OpTypePointer Workgroup %86
+ %88 = OpVariable %87 Workgroup
+ %12 = OpConstant %6 0
+ %13 = OpTypePointer StorageBuffer %6
+ %15 = OpConstant %6 2
+ %16 = OpConstant %6 64
+ %89 = OpTypePointer Workgroup %17
+ %18 = OpConstant %17 1
+ %19 = OpConstant %17 0
+ %20 = OpConstant %17 64
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %100 = OpCopyObject %6 %15 ; A non-constant version of %15
+ %101 = OpCopyObject %17 %20 ; A non-constant version of %20
+ %14 = OpAccessChain %13 %11 %12
+ %90 = OpAccessChain %89 %88 %19
+ %21 = OpAtomicLoad %6 %14 %15 %20
+ %22 = OpAtomicExchange %6 %14 %15 %20 %16
+ %23 = OpAtomicCompareExchange %6 %14 %15 %20 %12 %16 %15
+ %24 = OpAtomicIIncrement %6 %14 %15 %20
+ %25 = OpAtomicIDecrement %6 %14 %15 %20
+ %26 = OpAtomicIAdd %6 %14 %15 %20 %16
+ %27 = OpAtomicISub %6 %14 %15 %20 %16
+ %28 = OpAtomicSMin %6 %14 %15 %20 %16
+ %29 = OpAtomicUMin %17 %90 %15 %20 %18
+ %30 = OpAtomicSMax %6 %14 %15 %20 %15
+ %31 = OpAtomicUMax %17 %90 %15 %20 %18
+ %32 = OpAtomicAnd %6 %14 %15 %20 %16
+ %33 = OpAtomicOr %6 %14 %15 %20 %16
+ %34 = OpAtomicXor %6 %14 %15 %20 %16
+ OpStore %8 %21
+ OpAtomicStore %14 %15 %20 %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ spvtools::ValidatorOptions validator_options;
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+
+ // Tell the fact manager that %100 and %15 are synonymous
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(100, {}), MakeDataDescriptor(15, {}));
+
+ // Tell the fact manager that %101 and %20 are synonymous
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(101, {}), MakeDataDescriptor(20, {}));
+ // OpAtomicLoad
+ const auto& scope_operand = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(21), 1);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand, 100)
+ .IsApplicable(context.get(), transformation_context));
+
+ const auto& semantics_operand = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(21), 2);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand, 101)
+ .IsApplicable(context.get(), transformation_context));
+ // OpAtomicExchange.
+ const auto& scope_operand2 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(22), 1);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand2, 100)
+ .IsApplicable(context.get(), transformation_context));
+
+ const auto& semantics_operand2 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(22), 2);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand2, 101)
+ .IsApplicable(context.get(), transformation_context));
+ // OpAtomicCompareExchange.
+ const auto& scope_operand3 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(23), 1);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand3, 100)
+ .IsApplicable(context.get(), transformation_context));
+
+ const auto& semantics_equal_operand3 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(23), 2);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_equal_operand3, 101)
+ .IsApplicable(context.get(), transformation_context));
+ const auto& semantics_unequal_operand3 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(23), 3);
+ ASSERT_FALSE(
+ TransformationReplaceIdWithSynonym(semantics_unequal_operand3, 101)
+ .IsApplicable(context.get(), transformation_context));
+ // OpAtomicIIncrement.
+ const auto& scope_operand4 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(24), 1);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand4, 100)
+ .IsApplicable(context.get(), transformation_context));
+
+ const auto& semantics_operand4 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(24), 2);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand4, 101)
+ .IsApplicable(context.get(), transformation_context));
+
+ // OpAtomicIDecrement.
+ const auto& scope_operand5 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(25), 1);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand5, 100)
+ .IsApplicable(context.get(), transformation_context));
+
+ const auto& semantics_operand5 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(25), 2);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand5, 101)
+ .IsApplicable(context.get(), transformation_context));
+
+ // OpAtomicIAdd.
+ const auto& scope_operand6 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(26), 1);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand6, 100)
+ .IsApplicable(context.get(), transformation_context));
+
+ const auto& semantics_operand6 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(26), 2);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand6, 101)
+ .IsApplicable(context.get(), transformation_context));
+ // OpAtomicISub
+ const auto& scope_operand8 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(27), 1);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand8, 100)
+ .IsApplicable(context.get(), transformation_context));
+
+ const auto& semantics_operand8 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(27), 2);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand8, 101)
+ .IsApplicable(context.get(), transformation_context));
+
+ // OpAtomicSMin
+ const auto& scope_operand9 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(28), 1);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand9, 100)
+ .IsApplicable(context.get(), transformation_context));
+
+ const auto& semantics_operand9 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(28), 2);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand9, 101)
+ .IsApplicable(context.get(), transformation_context));
+ // OpAtomicUMin
+ const auto& scope_operand10 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(29), 1);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand10, 100)
+ .IsApplicable(context.get(), transformation_context));
+
+ const auto& semantics_operand10 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(29), 2);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand10, 101)
+ .IsApplicable(context.get(), transformation_context));
+
+ // OpAtomicSMax
+ const auto& scope_operand11 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(30), 1);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand11, 100)
+ .IsApplicable(context.get(), transformation_context));
+
+ const auto& semantics_operand11 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(30), 2);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand11, 101)
+ .IsApplicable(context.get(), transformation_context));
+ // OpAtomicUMax
+ const auto& scope_operand12 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(31), 1);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand12, 100)
+ .IsApplicable(context.get(), transformation_context));
+
+ const auto& semantics_operand12 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(31), 2);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand12, 101)
+ .IsApplicable(context.get(), transformation_context));
+
+ // OpAtomicAnd
+ const auto& scope_operand13 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(32), 1);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand13, 100)
+ .IsApplicable(context.get(), transformation_context));
+
+ const auto& semantics_operand13 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(32), 2);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand13, 101)
+ .IsApplicable(context.get(), transformation_context));
+
+ // OpAtomicOr
+ const auto& scope_operand14 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(33), 1);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand14, 100)
+ .IsApplicable(context.get(), transformation_context));
+
+ const auto& semantics_operand14 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(33), 2);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand14, 101)
+ .IsApplicable(context.get(), transformation_context));
+
+ // OpAtomicXor
+ const auto& scope_operand15 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(34), 1);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(scope_operand15, 100)
+ .IsApplicable(context.get(), transformation_context));
+
+ const auto& semantics_operand15 = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(34), 2);
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(semantics_operand15, 101)
+ .IsApplicable(context.get(), transformation_context));
+}
+
+// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): Improve this
+// test so that it covers more atomic operations, and enable the test once the
+// issue is fixed.
+TEST(TransformationReplaceIdWithSynonymTest,
+ DISABLED_SignOfAtomicScopeAndMemorySemanticsDoesNotMatter) {
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): both the
+ // GLSL comment and the corresponding SPIR-V should be updated to cover a
+ // larger number of atomic operations.
+ // The following SPIR-V came from this GLSL, edited to add some synonyms:
+ //
+ // #version 320 es
+ //
+ // #extension GL_KHR_memory_scope_semantics : enable
+ //
+ // layout(set = 0, binding = 0) buffer Buf {
+ // int x;
+ // };
+ //
+ // void main() {
+ // int tmp = atomicLoad(x,
+ // gl_ScopeWorkgroup,
+ // gl_StorageSemanticsBuffer,
+ // gl_SemanticsRelaxed);
+ // }
+ const std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 320
+ OpSourceExtension "GL_KHR_memory_scope_semantics"
+ OpMemberDecorate %9 0 Offset 0
+ OpDecorate %9 Block
+ OpDecorate %11 DescriptorSet 0
+ OpDecorate %11 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpTypeStruct %6
+ %10 = OpTypePointer StorageBuffer %9
+ %11 = OpVariable %10 StorageBuffer
+ %12 = OpConstant %6 0
+ %13 = OpTypePointer StorageBuffer %6
+ %15 = OpConstant %6 2
+ %16 = OpConstant %6 64
+ %17 = OpTypeInt 32 0
+ %100 = OpConstant %17 2 ; The same as %15, but with unsigned int type
+ %18 = OpConstant %17 1
+ %19 = OpConstant %17 0
+ %20 = OpConstant %17 64
+ %101 = OpConstant %6 64 ; The same as %20, but with signed int type
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %14 = OpAccessChain %13 %11 %12
+ %21 = OpAtomicLoad %6 %14 %15 %20
+ OpStore %8 %21
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ spvtools::ValidatorOptions validator_options;
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+
+ // Tell the fact manager that %100 and %15 are synonymous
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(100, {}), MakeDataDescriptor(15, {}));
+
+ // Tell the fact manager that %101 and %20 are synonymous
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(101, {}), MakeDataDescriptor(20, {}));
+
+ {
+ const auto& scope_operand = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(21), 1);
+ TransformationReplaceIdWithSynonym replace_scope(scope_operand, 100);
+ ASSERT_TRUE(
+ replace_scope.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(replace_scope, context.get(),
+ &transformation_context);
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+ context.get(), validator_options, kConsoleMessageConsumer));
+ }
+
+ {
+ const auto& semantics_operand = MakeIdUseDescriptorFromUse(
+ context.get(), context->get_def_use_mgr()->GetDef(21), 2);
+ TransformationReplaceIdWithSynonym replace_semantics(semantics_operand,
+ 101);
+ ASSERT_TRUE(
+ replace_semantics.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(replace_semantics, context.get(),
+ &transformation_context);
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+ context.get(), validator_options, kConsoleMessageConsumer));
+ }
+
+ const std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %4 "main"
+ OpExecutionMode %4 LocalSize 1 1 1
+ OpSource ESSL 320
+ OpSourceExtension "GL_KHR_memory_scope_semantics"
+ OpMemberDecorate %9 0 Offset 0
+ OpDecorate %9 Block
+ OpDecorate %11 DescriptorSet 0
+ OpDecorate %11 Binding 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpTypeStruct %6
+ %10 = OpTypePointer StorageBuffer %9
+ %11 = OpVariable %10 StorageBuffer
+ %12 = OpConstant %6 0
+ %13 = OpTypePointer StorageBuffer %6
+ %15 = OpConstant %6 2
+ %16 = OpConstant %6 64
+ %17 = OpTypeInt 32 0
+ %100 = OpConstant %17 2
+ %18 = OpConstant %17 1
+ %19 = OpConstant %17 0
+ %20 = OpConstant %17 64
+ %101 = OpConstant %6 64
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %14 = OpAccessChain %13 %11 %12
+ %21 = OpAtomicLoad %6 %14 %100 %101
+ OpStore %8 %21
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools
diff --git a/test/fuzz/transformation_store_test.cpp b/test/fuzz/transformation_store_test.cpp
index 93257d0d..dd653e28 100644
--- a/test/fuzz/transformation_store_test.cpp
+++ b/test/fuzz/transformation_store_test.cpp
@@ -26,6 +26,7 @@ namespace {
TEST(TransformationStoreTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %92 %52 %53
@@ -50,7 +51,6 @@ TEST(TransformationStoreTest, BasicTest) {
%34 = OpTypeBool
%35 = OpConstantFalse %34
%60 = OpConstantNull %50
- %61 = OpUndef %51
%52 = OpVariable %50 Private
%53 = OpVariable %51 Private
%80 = OpConstantComposite %8 %21 %24
@@ -148,90 +148,107 @@ TEST(TransformationStoreTest, BasicTest) {
// 61 - undefined
// Bad: attempt to store to 11 from outside its function
- ASSERT_FALSE(TransformationStore(
- 11, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationStore(11, false, 0, 0, 80,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: pointer is not available
- ASSERT_FALSE(TransformationStore(
- 81, 80, MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationStore(81, false, 0, 0, 80,
+ MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: attempt to insert before OpVariable
- ASSERT_FALSE(TransformationStore(
- 52, 24, MakeInstructionDescriptor(27, SpvOpVariable, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationStore(52, false, 0, 0, 24,
+ MakeInstructionDescriptor(27, SpvOpVariable, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: pointer id does not exist
- ASSERT_FALSE(TransformationStore(
- 1000, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationStore(1000, false, 0, 0, 24,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: pointer id exists but does not have a type
- ASSERT_FALSE(TransformationStore(
- 5, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationStore(5, false, 0, 0, 24,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: pointer id exists and has a type, but is not a pointer
- ASSERT_FALSE(TransformationStore(
- 24, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationStore(24, false, 0, 0, 24,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: attempt to store to a null pointer
- ASSERT_FALSE(TransformationStore(
- 60, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationStore(60, false, 0, 0, 24,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: attempt to store to an undefined pointer
- ASSERT_FALSE(TransformationStore(
- 61, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationStore(61, false, 0, 0, 21,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: %82 is not available at the program point
ASSERT_FALSE(
- TransformationStore(82, 80, MakeInstructionDescriptor(37, SpvOpReturn, 0))
+ TransformationStore(82, false, 0, 0, 80,
+ MakeInstructionDescriptor(37, SpvOpReturn, 0))
.IsApplicable(context.get(), transformation_context));
// Bad: value id does not exist
- ASSERT_FALSE(TransformationStore(
- 27, 1000, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationStore(27, false, 0, 0, 1000,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: value id exists but does not have a type
- ASSERT_FALSE(TransformationStore(
- 27, 15, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationStore(27, false, 0, 0, 15,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: value id exists but has the wrong type
- ASSERT_FALSE(TransformationStore(
- 27, 14, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationStore(27, false, 0, 0, 14,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: attempt to store to read-only variable
- ASSERT_FALSE(TransformationStore(
- 92, 93, MakeInstructionDescriptor(40, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationStore(92, false, 0, 0, 93,
+ MakeInstructionDescriptor(40, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: value is not available
- ASSERT_FALSE(TransformationStore(
- 27, 95, MakeInstructionDescriptor(40, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationStore(27, false, 0, 0, 95,
+ MakeInstructionDescriptor(40, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
// Bad: variable being stored to does not have an irrelevant pointee value,
// and the store is not in a dead block.
- ASSERT_FALSE(TransformationStore(
- 20, 95, MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationStore(20, false, 0, 0, 95,
+ MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
+ .IsApplicable(context.get(), transformation_context));
// The described instruction does not exist.
- ASSERT_FALSE(TransformationStore(
- 27, 80, MakeInstructionDescriptor(1000, SpvOpAccessChain, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationStore(27, false, 0, 0, 80,
+ MakeInstructionDescriptor(1000, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
{
// Store to irrelevant variable from dead block.
TransformationStore transformation(
- 27, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
+ 27, false, 0, 0, 80,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
@@ -243,7 +260,8 @@ TEST(TransformationStoreTest, BasicTest) {
{
// Store to irrelevant variable from live block.
TransformationStore transformation(
- 11, 95, MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
+ 11, false, 0, 0, 95,
+ MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
@@ -255,7 +273,8 @@ TEST(TransformationStoreTest, BasicTest) {
{
// Store to irrelevant variable from live block.
TransformationStore transformation(
- 46, 80, MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
+ 46, false, 0, 0, 80,
+ MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
@@ -267,7 +286,8 @@ TEST(TransformationStoreTest, BasicTest) {
{
// Store to irrelevant variable from live block.
TransformationStore transformation(
- 16, 21, MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
+ 16, false, 0, 0, 21,
+ MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
@@ -279,7 +299,8 @@ TEST(TransformationStoreTest, BasicTest) {
{
// Store to non-irrelevant variable from dead block.
TransformationStore transformation(
- 53, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
+ 53, false, 0, 0, 21,
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
@@ -290,6 +311,7 @@ TEST(TransformationStoreTest, BasicTest) {
std::string after_transformation = R"(
OpCapability Shader
+ OpCapability VariablePointers
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %92 %52 %53
@@ -314,7 +336,6 @@ TEST(TransformationStoreTest, BasicTest) {
%34 = OpTypeBool
%35 = OpConstantFalse %34
%60 = OpConstantNull %50
- %61 = OpUndef %51
%52 = OpVariable %50 Private
%53 = OpVariable %51 Private
%80 = OpConstantComposite %8 %21 %24
@@ -414,14 +435,188 @@ TEST(TransformationStoreTest, DoNotAllowStoresToReadOnlyMemory) {
transformation_context.GetFactManager()->AddFactBlockIsDead(5);
ASSERT_FALSE(
- TransformationStore(15, 13, MakeInstructionDescriptor(27, SpvOpReturn, 0))
+ TransformationStore(15, false, 0, 0, 13,
+ MakeInstructionDescriptor(27, SpvOpReturn, 0))
.IsApplicable(context.get(), transformation_context));
ASSERT_FALSE(
- TransformationStore(19, 50, MakeInstructionDescriptor(27, SpvOpReturn, 0))
+ TransformationStore(19, false, 0, 0, 50,
+ MakeInstructionDescriptor(27, SpvOpReturn, 0))
.IsApplicable(context.get(), transformation_context));
ASSERT_FALSE(
- TransformationStore(27, 50, MakeInstructionDescriptor(27, SpvOpReturn, 0))
+ TransformationStore(27, false, 0, 0, 50,
+ MakeInstructionDescriptor(27, SpvOpReturn, 0))
+ .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationStoreTest, SupportAtomicStore) {
+ const std::string shader = R"(
+ OpCapability Shader
+ OpCapability Int8
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 8 1
+ %9 = OpTypeInt 32 0
+ %26 = OpTypeFloat 32
+ %8 = OpTypeStruct %6
+ %10 = OpTypePointer StorageBuffer %8
+ %11 = OpVariable %10 StorageBuffer
+ %19 = OpConstant %26 0
+ %18 = OpConstant %9 1
+ %12 = OpConstant %6 0
+ %13 = OpTypePointer StorageBuffer %6
+ %15 = OpConstant %6 4
+ %16 = OpConstant %6 7
+ %17 = OpConstant %7 4
+ %20 = OpConstant %9 64
+ %21 = OpConstant %6 15
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %14 = OpAccessChain %13 %11 %12
+ %24 = OpAccessChain %13 %11 %12
+ OpReturn
+ OpFunctionEnd
+ )";
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ spvtools::ValidatorOptions validator_options;
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+
+ transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+ 14);
+
+ // Bad: id 100 of memory scope instruction does not exist.
+ ASSERT_FALSE(
+ TransformationStore(14, true, 100, 20, 21,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
.IsApplicable(context.get(), transformation_context));
+ // Bad: id 100 of memory semantics instruction does not exist.
+ ASSERT_FALSE(
+ TransformationStore(14, true, 15, 100, 21,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+ // Bad: memory scope should be |OpConstant| opcode.
+ ASSERT_FALSE(
+ TransformationStore(14, true, 5, 20, 21,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+ // Bad: memory semantics should be |OpConstant| opcode.
+ ASSERT_FALSE(
+ TransformationStore(14, true, 15, 5, 21,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: The memory scope instruction must have an Integer operand.
+ ASSERT_FALSE(
+ TransformationStore(14, true, 15, 19, 21,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+ // Bad: The memory memory semantics instruction must have an Integer operand.
+ ASSERT_FALSE(
+ TransformationStore(14, true, 19, 20, 21,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: Integer size of the memory scope must be equal to 32 bits.
+ ASSERT_FALSE(
+ TransformationStore(14, true, 17, 20, 21,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: Integer size of memory semantics must be equal to 32 bits.
+ ASSERT_FALSE(
+ TransformationStore(14, true, 15, 17, 21,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: memory scope value must be 4 (SpvScopeInvocation).
+ ASSERT_FALSE(
+ TransformationStore(14, true, 16, 20, 21,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: memory semantics value must be either:
+ // 64 (SpvMemorySemanticsUniformMemoryMask)
+ // 256 (SpvMemorySemanticsWorkgroupMemoryMask)
+ ASSERT_FALSE(
+ TransformationStore(14, true, 15, 16, 21,
+ MakeInstructionDescriptor(24, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+ // Bad: The described instruction does not exist
+ ASSERT_FALSE(
+ TransformationStore(14, true, 15, 20, 21,
+ MakeInstructionDescriptor(150, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: Can't insert OpAccessChain before the id 15 of memory scope.
+ ASSERT_FALSE(
+ TransformationStore(14, true, 15, 20, 21,
+ MakeInstructionDescriptor(15, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: Can't insert OpAccessChain before the id 20 of memory semantics.
+ ASSERT_FALSE(
+ TransformationStore(14, true, 15, 20, 21,
+ MakeInstructionDescriptor(20, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Successful transformations.
+ {
+ TransformationStore transformation(
+ 14, true, 15, 20, 21, MakeInstructionDescriptor(24, SpvOpReturn, 0));
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(transformation, context.get(),
+ &transformation_context);
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
+ context.get(), validator_options, kConsoleMessageConsumer));
+ }
+
+ const std::string after_transformation = R"(
+ OpCapability Shader
+ OpCapability Int8
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 8 1
+ %9 = OpTypeInt 32 0
+ %26 = OpTypeFloat 32
+ %8 = OpTypeStruct %6
+ %10 = OpTypePointer StorageBuffer %8
+ %11 = OpVariable %10 StorageBuffer
+ %19 = OpConstant %26 0
+ %18 = OpConstant %9 1
+ %12 = OpConstant %6 0
+ %13 = OpTypePointer StorageBuffer %6
+ %15 = OpConstant %6 4
+ %16 = OpConstant %6 7
+ %17 = OpConstant %7 4
+ %20 = OpConstant %9 64
+ %21 = OpConstant %6 15
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %14 = OpAccessChain %13 %11 %12
+ %24 = OpAccessChain %13 %11 %12
+ OpAtomicStore %14 %15 %20 %21
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
diff --git a/test/fuzz/transformation_swap_two_functions_test.cpp b/test/fuzz/transformation_swap_two_functions_test.cpp
index 38d6a076..20342526 100644
--- a/test/fuzz/transformation_swap_two_functions_test.cpp
+++ b/test/fuzz/transformation_swap_two_functions_test.cpp
@@ -154,7 +154,20 @@ TEST(TransformationSwapTwoFunctionsTest, SimpleTest) {
// Function with result_id 10 and 13 should swap successfully.
auto swap_test5 = TransformationSwapTwoFunctions(10, 13);
ASSERT_TRUE(swap_test5.IsApplicable(context.get(), transformation_context));
+
+ // Get the definitions of functions 10 and 13, as recorded by the def-use
+ // manager.
+ auto def_use_manager = context->get_def_use_mgr();
+ auto function_10_inst = def_use_manager->GetDef(10);
+ auto function_13_inst = def_use_manager->GetDef(13);
+
ApplyAndCheckFreshIds(swap_test5, context.get(), &transformation_context);
+
+ // Check that def-use information for functions 10 and 13 has been preserved
+ // by the transformation.
+ ASSERT_EQ(function_10_inst, def_use_manager->GetDef(10));
+ ASSERT_EQ(function_13_inst, def_use_manager->GetDef(13));
+
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
diff --git a/test/fuzz/transformation_wrap_vector_synonym_test.cpp b/test/fuzz/transformation_wrap_vector_synonym_test.cpp
new file mode 100644
index 00000000..0d1009b3
--- /dev/null
+++ b/test/fuzz/transformation_wrap_vector_synonym_test.cpp
@@ -0,0 +1,1553 @@
+// Copyright (c) 2021 Shiyu Liu
+//
+// 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/fuzz/transformation_wrap_vector_synonym.h"
+
+#include "gtest/gtest.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationWrapVectorSynonym, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %97
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 10
+ %11 = OpConstant %6 -5
+ %12 = OpTypeVector %6 2
+ %13 = OpTypePointer Function %12
+ %18 = OpTypeInt 32 0
+ %19 = OpTypePointer Function %18
+ %21 = OpConstant %18 8
+ %23 = OpConstant %18 2
+ %24 = OpTypeVector %18 3
+ %25 = OpTypePointer Function %24
+ %31 = OpTypeFloat 32
+ %32 = OpTypePointer Function %31
+ %34 = OpConstant %31 3.29999995
+ %36 = OpConstant %31 1.10000002
+ %37 = OpTypeVector %31 4
+ %38 = OpTypePointer Function %37
+ %96 = OpTypePointer Input %31
+ %97 = OpVariable %96 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %14 = OpVariable %13 Function
+ %20 = OpVariable %19 Function
+ %22 = OpVariable %19 Function
+ %26 = OpVariable %25 Function
+ %33 = OpVariable %32 Function
+ %35 = OpVariable %32 Function
+ %39 = OpVariable %38 Function
+ %47 = OpVariable %7 Function
+ %51 = OpVariable %7 Function
+ %55 = OpVariable %7 Function
+ %59 = OpVariable %7 Function
+ %63 = OpVariable %19 Function
+ %67 = OpVariable %19 Function
+ %71 = OpVariable %19 Function
+ %75 = OpVariable %19 Function
+ %79 = OpVariable %32 Function
+ %83 = OpVariable %32 Function
+ %87 = OpVariable %32 Function
+ %91 = OpVariable %32 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %15 = OpLoad %6 %8
+ %16 = OpLoad %6 %10
+ %17 = OpCompositeConstruct %12 %15 %16
+ OpStore %14 %17
+ OpStore %20 %21
+ OpStore %22 %23
+ %27 = OpLoad %18 %20
+ %28 = OpLoad %18 %20
+ %29 = OpLoad %18 %22
+ %30 = OpCompositeConstruct %24 %27 %28 %29
+ OpStore %26 %30
+ OpStore %33 %34
+ OpStore %35 %36
+ %40 = OpLoad %31 %33
+ %41 = OpLoad %31 %33
+ %42 = OpLoad %31 %35
+ %43 = OpLoad %31 %35
+ %44 = OpCompositeConstruct %37 %40 %41 %42 %43
+ %45 = OpLoad %37 %39
+ %46 = OpVectorShuffle %37 %45 %44 5 6 7 4
+ OpStore %39 %46
+ %48 = OpLoad %6 %8
+ %49 = OpLoad %6 %10
+ %100 = OpCompositeConstruct %12 %48 %48
+ %101 = OpCompositeConstruct %12 %49 %49
+ %50 = OpIAdd %6 %48 %49
+ OpStore %47 %50
+ %52 = OpLoad %6 %8
+ %53 = OpLoad %6 %10
+ %54 = OpISub %6 %52 %53
+ OpStore %51 %54
+ %56 = OpLoad %6 %8
+ %57 = OpLoad %6 %10
+ %58 = OpIMul %6 %56 %57
+ OpStore %55 %58
+ %60 = OpLoad %6 %8
+ %61 = OpLoad %6 %10
+ %62 = OpSDiv %6 %60 %61
+ OpStore %59 %62
+ %64 = OpLoad %18 %20
+ %65 = OpLoad %18 %22
+ %66 = OpIAdd %18 %64 %65
+ OpStore %63 %66
+ %68 = OpLoad %18 %20
+ %69 = OpLoad %18 %22
+ %70 = OpISub %18 %68 %69
+ OpStore %67 %70
+ %72 = OpLoad %18 %20
+ %73 = OpLoad %18 %22
+ %74 = OpIMul %18 %72 %73
+ OpStore %71 %74
+ %76 = OpLoad %18 %20
+ %77 = OpLoad %18 %22
+ %78 = OpUDiv %18 %76 %77
+ OpStore %75 %78
+ %80 = OpLoad %31 %33
+ %81 = OpLoad %31 %35
+ %82 = OpFAdd %31 %80 %81
+ OpStore %79 %82
+ %84 = OpLoad %31 %33
+ %85 = OpLoad %31 %35
+ %86 = OpFSub %31 %84 %85
+ OpStore %83 %86
+ %88 = OpLoad %31 %33
+ %89 = OpLoad %31 %35
+ %90 = OpFMul %31 %88 %89
+ OpStore %87 %90
+ %92 = OpLoad %31 %33
+ %93 = OpLoad %31 %35
+ %94 = OpFDiv %31 %92 %93
+ OpStore %91 %94
+ OpReturn
+ OpFunctionEnd
+ )";
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ spvtools::ValidatorOptions validator_options;
+
+ // Check context validity.
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+
+ // Vec Type Id | Vector Type | Element Type id | Element Type |
+ // ------------+----------------+------------------+-----------------+
+ // 12 | vec2 | 6 | int32 |
+ // 24 | vec3 | 18 | uint32 |
+ // 37 | vec4 | 31 | float |
+
+ // Instruction Id | Opcode | Type Id | constant id 1 | constant id 2 |
+ // ---------------+---------+---------+---------------+---------------+
+ // 50 | OpIAdd | 6 | 48 | 49 |
+ // 54 | OpISub | 6 | 52 | 53 |
+ // 58 | OpIMul | 6 | 56 | 57 |
+ // 62 | OpSDiv | 6 | 60 | 61 |
+ // 66 | OpIAdd | 18 | 64 | 65 |
+ // 70 | OpISub | 18 | 68 | 69 |
+ // 74 | OpIMul | 18 | 72 | 73 |
+ // 78 | OpUDiv | 18 | 76 | 77 |
+ // 82 | OpFAdd | 31 | 80 | 81 |
+ // 86 | OpFSub | 31 | 84 | 85 |
+ // 90 | OpFMul | 31 | 88 | 89 |
+ // 94 | OpFDiv | 31 | 92 | 93 |
+
+ // Assert that the target scalar instruction result id is relevant.
+ ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(50));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(100, {1}), MakeDataDescriptor(48, {}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(101, {1}), MakeDataDescriptor(49, {}));
+
+ // The following are all invalid use.
+ {
+ // Bad: Instruction id does not exist.
+ TransformationWrapVectorSynonym wrap_add_int_bad1(103, 100, 101, 102, 1);
+ ASSERT_FALSE(
+ wrap_add_int_bad1.IsApplicable(context.get(), transformation_context));
+
+ // Bad: Instruction id given is not of a valid arithmetic operation typed
+ // instruction.
+ TransformationWrapVectorSynonym wrap_add_int_bad2(80, 100, 101, 102, 1);
+ ASSERT_FALSE(
+ wrap_add_int_bad1.IsApplicable(context.get(), transformation_context));
+
+ // Bad: the id for the first vector does not exist.
+ TransformationWrapVectorSynonym wrap_add_int_bad3(50, 105, 101, 102, 1);
+ ASSERT_FALSE(
+ wrap_add_int_bad3.IsApplicable(context.get(), transformation_context));
+
+ // Bad: the id for the second vector does not exist.
+ TransformationWrapVectorSynonym wrap_add_int_bad4(50, 100, 105, 102, 1);
+ ASSERT_FALSE(
+ wrap_add_int_bad4.IsApplicable(context.get(), transformation_context));
+
+ // Bad: vector id is not fresh.
+ TransformationWrapVectorSynonym wrap_add_int_bad6(50, 100, 101, 94, 1);
+ ASSERT_FALSE(
+ wrap_add_int_bad6.IsApplicable(context.get(), transformation_context));
+
+ // Bad: The position goes out of bound for the given vector type.
+ TransformationWrapVectorSynonym wrap_add_int_bad8(50, 100, 101, 102, 2);
+ ASSERT_FALSE(
+ wrap_add_int_bad8.IsApplicable(context.get(), transformation_context));
+
+ // Bad: The original instruction is not a valid scalar operation
+ // instruction.
+ TransformationWrapVectorSynonym wrap_add_int(27, 100, 101, 102, 1);
+ ASSERT_FALSE(
+ wrap_add_int.IsApplicable(context.get(), transformation_context));
+ }
+
+ // Good: The following transformation should be applicable.
+ TransformationWrapVectorSynonym wrap_add_int(50, 100, 101, 102, 1);
+ ASSERT_TRUE(wrap_add_int.IsApplicable(context.get(), transformation_context));
+ // Insert an arithmetic instruction of the same type to add two vectors.
+ ApplyAndCheckFreshIds(wrap_add_int, context.get(), &transformation_context);
+
+ // |instruction_id| and id at |scalar_position of the result vector should be
+ // synonyms.
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(102, {1}), MakeDataDescriptor(50, {})));
+
+ // After applying transformations, the instruction:
+ //
+ // %102 = OpIAdd %12 %100 %101
+ //
+ // should be added before:
+ //
+ // %50 = OpIAdd %6 %48 %49
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %97
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 10
+ %11 = OpConstant %6 -5
+ %12 = OpTypeVector %6 2
+ %13 = OpTypePointer Function %12
+ %18 = OpTypeInt 32 0
+ %19 = OpTypePointer Function %18
+ %21 = OpConstant %18 8
+ %23 = OpConstant %18 2
+ %24 = OpTypeVector %18 3
+ %25 = OpTypePointer Function %24
+ %31 = OpTypeFloat 32
+ %32 = OpTypePointer Function %31
+ %34 = OpConstant %31 3.29999995
+ %36 = OpConstant %31 1.10000002
+ %37 = OpTypeVector %31 4
+ %38 = OpTypePointer Function %37
+ %96 = OpTypePointer Input %31
+ %97 = OpVariable %96 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %14 = OpVariable %13 Function
+ %20 = OpVariable %19 Function
+ %22 = OpVariable %19 Function
+ %26 = OpVariable %25 Function
+ %33 = OpVariable %32 Function
+ %35 = OpVariable %32 Function
+ %39 = OpVariable %38 Function
+ %47 = OpVariable %7 Function
+ %51 = OpVariable %7 Function
+ %55 = OpVariable %7 Function
+ %59 = OpVariable %7 Function
+ %63 = OpVariable %19 Function
+ %67 = OpVariable %19 Function
+ %71 = OpVariable %19 Function
+ %75 = OpVariable %19 Function
+ %79 = OpVariable %32 Function
+ %83 = OpVariable %32 Function
+ %87 = OpVariable %32 Function
+ %91 = OpVariable %32 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %15 = OpLoad %6 %8
+ %16 = OpLoad %6 %10
+ %17 = OpCompositeConstruct %12 %15 %16
+ OpStore %14 %17
+ OpStore %20 %21
+ OpStore %22 %23
+ %27 = OpLoad %18 %20
+ %28 = OpLoad %18 %20
+ %29 = OpLoad %18 %22
+ %30 = OpCompositeConstruct %24 %27 %28 %29
+ OpStore %26 %30
+ OpStore %33 %34
+ OpStore %35 %36
+ %40 = OpLoad %31 %33
+ %41 = OpLoad %31 %33
+ %42 = OpLoad %31 %35
+ %43 = OpLoad %31 %35
+ %44 = OpCompositeConstruct %37 %40 %41 %42 %43
+ %45 = OpLoad %37 %39
+ %46 = OpVectorShuffle %37 %45 %44 5 6 7 4
+ OpStore %39 %46
+ %48 = OpLoad %6 %8
+ %49 = OpLoad %6 %10
+ %100 = OpCompositeConstruct %12 %48 %48
+ %101 = OpCompositeConstruct %12 %49 %49
+ %102 = OpIAdd %12 %100 %101
+ %50 = OpIAdd %6 %48 %49
+ OpStore %47 %50
+ %52 = OpLoad %6 %8
+ %53 = OpLoad %6 %10
+ %54 = OpISub %6 %52 %53
+ OpStore %51 %54
+ %56 = OpLoad %6 %8
+ %57 = OpLoad %6 %10
+ %58 = OpIMul %6 %56 %57
+ OpStore %55 %58
+ %60 = OpLoad %6 %8
+ %61 = OpLoad %6 %10
+ %62 = OpSDiv %6 %60 %61
+ OpStore %59 %62
+ %64 = OpLoad %18 %20
+ %65 = OpLoad %18 %22
+ %66 = OpIAdd %18 %64 %65
+ OpStore %63 %66
+ %68 = OpLoad %18 %20
+ %69 = OpLoad %18 %22
+ %70 = OpISub %18 %68 %69
+ OpStore %67 %70
+ %72 = OpLoad %18 %20
+ %73 = OpLoad %18 %22
+ %74 = OpIMul %18 %72 %73
+ OpStore %71 %74
+ %76 = OpLoad %18 %20
+ %77 = OpLoad %18 %22
+ %78 = OpUDiv %18 %76 %77
+ OpStore %75 %78
+ %80 = OpLoad %31 %33
+ %81 = OpLoad %31 %35
+ %82 = OpFAdd %31 %80 %81
+ OpStore %79 %82
+ %84 = OpLoad %31 %33
+ %85 = OpLoad %31 %35
+ %86 = OpFSub %31 %84 %85
+ OpStore %83 %86
+ %88 = OpLoad %31 %33
+ %89 = OpLoad %31 %35
+ %90 = OpFMul %31 %88 %89
+ OpStore %87 %90
+ %92 = OpLoad %31 %33
+ %93 = OpLoad %31 %35
+ %94 = OpFDiv %31 %92 %93
+ OpStore %91 %94
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationWrapVectorSynonym, OperationSupportTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %97
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 10
+ %11 = OpConstant %6 -5
+ %12 = OpTypeVector %6 2
+ %13 = OpTypePointer Function %12
+ %18 = OpTypeInt 32 0
+ %19 = OpTypePointer Function %18
+ %21 = OpConstant %18 8
+ %23 = OpConstant %18 2
+ %24 = OpTypeVector %18 3
+ %25 = OpTypePointer Function %24
+ %31 = OpTypeFloat 32
+ %32 = OpTypePointer Function %31
+ %34 = OpConstant %31 3.29999995
+ %36 = OpConstant %31 1.10000002
+ %37 = OpTypeVector %31 4
+ %38 = OpTypePointer Function %37
+ %96 = OpTypePointer Input %31
+ %97 = OpVariable %96 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %14 = OpVariable %13 Function
+ %20 = OpVariable %19 Function
+ %22 = OpVariable %19 Function
+ %26 = OpVariable %25 Function
+ %33 = OpVariable %32 Function
+ %35 = OpVariable %32 Function
+ %39 = OpVariable %38 Function
+ %47 = OpVariable %7 Function
+ %51 = OpVariable %7 Function
+ %55 = OpVariable %7 Function
+ %59 = OpVariable %7 Function
+ %63 = OpVariable %19 Function
+ %67 = OpVariable %19 Function
+ %71 = OpVariable %19 Function
+ %75 = OpVariable %19 Function
+ %79 = OpVariable %32 Function
+ %83 = OpVariable %32 Function
+ %87 = OpVariable %32 Function
+ %91 = OpVariable %32 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %15 = OpLoad %6 %8
+ %16 = OpLoad %6 %10
+ %17 = OpCompositeConstruct %12 %15 %16
+ OpStore %14 %17
+ OpStore %20 %21
+ OpStore %22 %23
+ %27 = OpLoad %18 %20
+ %28 = OpLoad %18 %20
+ %29 = OpLoad %18 %22
+ %30 = OpCompositeConstruct %24 %27 %28 %29
+ OpStore %26 %30
+ OpStore %33 %34
+ OpStore %35 %36
+ %40 = OpLoad %31 %33
+ %41 = OpLoad %31 %33
+ %42 = OpLoad %31 %35
+ %43 = OpLoad %31 %35
+ %44 = OpCompositeConstruct %37 %40 %41 %42 %43
+ %45 = OpLoad %37 %39
+ %46 = OpVectorShuffle %37 %45 %44 5 6 7 4
+ OpStore %39 %46
+ %48 = OpLoad %6 %8
+ %49 = OpLoad %6 %10
+ %50 = OpIAdd %6 %48 %49
+ OpStore %47 %50
+ %52 = OpLoad %6 %8
+ %53 = OpLoad %6 %10
+ %100 = OpCompositeConstruct %12 %52 %52
+ %101 = OpCompositeConstruct %12 %53 %53
+ %54 = OpISub %6 %52 %53
+ OpStore %51 %54
+ %56 = OpLoad %6 %8
+ %57 = OpLoad %6 %10
+ %103 = OpCompositeConstruct %12 %56 %56
+ %104 = OpCompositeConstruct %12 %57 %57
+ %58 = OpIMul %6 %56 %57
+ OpStore %55 %58
+ %60 = OpLoad %6 %8
+ %61 = OpLoad %6 %10
+ %62 = OpSDiv %6 %60 %61
+ OpStore %59 %62
+ %64 = OpLoad %18 %20
+ %65 = OpLoad %18 %22
+ %106 = OpCompositeConstruct %24 %64 %64 %64
+ %107 = OpCompositeConstruct %24 %65 %65 %65
+ %66 = OpIAdd %18 %64 %65
+ OpStore %63 %66
+ %68 = OpLoad %18 %20
+ %69 = OpLoad %18 %22
+ %109 = OpCompositeConstruct %24 %68 %68 %68
+ %110 = OpCompositeConstruct %24 %69 %69 %69
+ %70 = OpISub %18 %68 %69
+ OpStore %67 %70
+ %72 = OpLoad %18 %20
+ %73 = OpLoad %18 %22
+ %112 = OpCompositeConstruct %24 %72 %72 %72
+ %113 = OpCompositeConstruct %24 %73 %73 %73
+ %74 = OpIMul %18 %72 %73
+ OpStore %71 %74
+ %76 = OpLoad %18 %20
+ %77 = OpLoad %18 %22
+ %78 = OpUDiv %18 %76 %77
+ OpStore %75 %78
+ %80 = OpLoad %31 %33
+ %81 = OpLoad %31 %35
+ %115 = OpCompositeConstruct %37 %80 %80 %80 %80
+ %116 = OpCompositeConstruct %37 %81 %81 %81 %81
+ %82 = OpFAdd %31 %80 %81
+ OpStore %79 %82
+ %84 = OpLoad %31 %33
+ %85 = OpLoad %31 %35
+ %118 = OpCompositeConstruct %37 %84 %84 %84 %84
+ %119 = OpCompositeConstruct %37 %85 %85 %85 %85
+ %86 = OpFSub %31 %84 %85
+ OpStore %83 %86
+ %88 = OpLoad %31 %33
+ %89 = OpLoad %31 %35
+ %121 = OpCompositeConstruct %37 %88 %88 %88 %88
+ %122 = OpCompositeConstruct %37 %89 %89 %89 %89
+ %90 = OpFMul %31 %88 %89
+ OpStore %87 %90
+ %92 = OpLoad %31 %33
+ %93 = OpLoad %31 %35
+ %94 = OpFDiv %31 %92 %93
+ OpStore %91 %94
+ OpReturn
+ OpFunctionEnd
+ )";
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ spvtools::ValidatorOptions validator_options;
+
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+
+ {
+ // Add synonym facts between the vector operands at pos and the operands to
+ // the scalar instruction.
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(100, {1}), MakeDataDescriptor(52, {}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(101, {1}), MakeDataDescriptor(53, {}));
+
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(103, {0}), MakeDataDescriptor(56, {}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(104, {0}), MakeDataDescriptor(57, {}));
+
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(106, {2}), MakeDataDescriptor(64, {}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(107, {2}), MakeDataDescriptor(65, {}));
+
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(109, {2}), MakeDataDescriptor(68, {}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(110, {2}), MakeDataDescriptor(69, {}));
+
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(112, {1}), MakeDataDescriptor(72, {}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(113, {1}), MakeDataDescriptor(73, {}));
+
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(115, {2}), MakeDataDescriptor(80, {}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(116, {2}), MakeDataDescriptor(81, {}));
+
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(118, {3}), MakeDataDescriptor(84, {}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(119, {3}), MakeDataDescriptor(85, {}));
+
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(121, {1}), MakeDataDescriptor(88, {}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(122, {1}), MakeDataDescriptor(89, {}));
+ }
+
+ // Test OpISub for signed integer.
+ {
+ // Good: The following transformation should be applicable.
+ TransformationWrapVectorSynonym wrap_sub_int(54, 100, 101, 102, 1);
+ ASSERT_TRUE(
+ wrap_sub_int.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(wrap_sub_int, context.get(), &transformation_context);
+
+ // |instruction_id| and id at |scalar_position of the result vector should
+ // be synonyms.
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(102, {1}), MakeDataDescriptor(54, {})));
+ }
+
+ // Test OpIMul for signed integer.
+ {
+ // Good: The following transformation should be applicable.
+ TransformationWrapVectorSynonym wrap_mul_int(58, 103, 104, 105, 0);
+ ASSERT_TRUE(
+ wrap_mul_int.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(wrap_mul_int, context.get(), &transformation_context);
+
+ // |instruction_id| and id at |scalar_position of the result vector should
+ // be synonyms.
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(105, {0}), MakeDataDescriptor(58, {})));
+ }
+
+ // Test OpIAdd for unsigned integer.
+ {
+ // Good: The following transformation should be applicable.
+ TransformationWrapVectorSynonym wrap_add_uint(66, 106, 107, 108, 2);
+ ASSERT_TRUE(
+ wrap_add_uint.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(wrap_add_uint, context.get(),
+ &transformation_context);
+
+ // |instruction_id| and id at |scalar_position of the result vector should
+ // be synonyms.
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(108, {2}), MakeDataDescriptor(66, {})));
+ }
+
+ // Test OpISub for signed integer.
+ {
+ // Good: The following transformation should be applicable.
+ TransformationWrapVectorSynonym wrap_sub_uint(70, 109, 110, 111, 2);
+ ASSERT_TRUE(
+ wrap_sub_uint.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(wrap_sub_uint, context.get(),
+ &transformation_context);
+
+ // |instruction_id| and id at |scalar_position of the result vector should
+ // be synonyms.
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(111, {2}), MakeDataDescriptor(70, {})));
+ }
+
+ // Test OpIMul for signed integer.
+ {
+ // Good: The following transformation should be applicable.
+ TransformationWrapVectorSynonym wrap_mul_uint(74, 112, 113, 114, 1);
+ ASSERT_TRUE(
+ wrap_mul_uint.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(wrap_mul_uint, context.get(),
+ &transformation_context);
+
+ // |instruction_id| and id at |scalar_position of the result vector should
+ // be synonyms.
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(114, {1}), MakeDataDescriptor(74, {})));
+ }
+
+ // Test OpFAdd for float.
+ {
+ // Good: The following transformation should be applicable.
+ TransformationWrapVectorSynonym wrap_add_float(82, 115, 116, 117, 2);
+ ASSERT_TRUE(
+ wrap_add_float.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(wrap_add_float, context.get(),
+ &transformation_context);
+
+ // |instruction_id| and id at |scalar_position of the result vector should
+ // be synonyms.
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(117, {2}), MakeDataDescriptor(82, {})));
+ }
+
+ // Test OpFSub for float.
+ {
+ // Good: The following transformation should be applicable.
+ TransformationWrapVectorSynonym wrap_add_float(86, 118, 119, 120, 3);
+ ASSERT_TRUE(
+ wrap_add_float.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(wrap_add_float, context.get(),
+ &transformation_context);
+
+ // |instruction_id| and id at |scalar_position of the result vector should
+ // be synonyms.
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(120, {3}), MakeDataDescriptor(86, {})));
+ }
+
+ // Test OpFMul for float.
+ {
+ // Good: The following transformation should be applicable.
+ TransformationWrapVectorSynonym wrap_mul_float(90, 121, 122, 123, 1);
+ ASSERT_TRUE(
+ wrap_mul_float.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(wrap_mul_float, context.get(),
+ &transformation_context);
+
+ // |instruction_id| and id at |scalar_position of the result vector should
+ // be synonyms.
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(123, {1}), MakeDataDescriptor(90, {})));
+ }
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %97
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 10
+ %11 = OpConstant %6 -5
+ %12 = OpTypeVector %6 2
+ %13 = OpTypePointer Function %12
+ %18 = OpTypeInt 32 0
+ %19 = OpTypePointer Function %18
+ %21 = OpConstant %18 8
+ %23 = OpConstant %18 2
+ %24 = OpTypeVector %18 3
+ %25 = OpTypePointer Function %24
+ %31 = OpTypeFloat 32
+ %32 = OpTypePointer Function %31
+ %34 = OpConstant %31 3.29999995
+ %36 = OpConstant %31 1.10000002
+ %37 = OpTypeVector %31 4
+ %38 = OpTypePointer Function %37
+ %96 = OpTypePointer Input %31
+ %97 = OpVariable %96 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %14 = OpVariable %13 Function
+ %20 = OpVariable %19 Function
+ %22 = OpVariable %19 Function
+ %26 = OpVariable %25 Function
+ %33 = OpVariable %32 Function
+ %35 = OpVariable %32 Function
+ %39 = OpVariable %38 Function
+ %47 = OpVariable %7 Function
+ %51 = OpVariable %7 Function
+ %55 = OpVariable %7 Function
+ %59 = OpVariable %7 Function
+ %63 = OpVariable %19 Function
+ %67 = OpVariable %19 Function
+ %71 = OpVariable %19 Function
+ %75 = OpVariable %19 Function
+ %79 = OpVariable %32 Function
+ %83 = OpVariable %32 Function
+ %87 = OpVariable %32 Function
+ %91 = OpVariable %32 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %15 = OpLoad %6 %8
+ %16 = OpLoad %6 %10
+ %17 = OpCompositeConstruct %12 %15 %16
+ OpStore %14 %17
+ OpStore %20 %21
+ OpStore %22 %23
+ %27 = OpLoad %18 %20
+ %28 = OpLoad %18 %20
+ %29 = OpLoad %18 %22
+ %30 = OpCompositeConstruct %24 %27 %28 %29
+ OpStore %26 %30
+ OpStore %33 %34
+ OpStore %35 %36
+ %40 = OpLoad %31 %33
+ %41 = OpLoad %31 %33
+ %42 = OpLoad %31 %35
+ %43 = OpLoad %31 %35
+ %44 = OpCompositeConstruct %37 %40 %41 %42 %43
+ %45 = OpLoad %37 %39
+ %46 = OpVectorShuffle %37 %45 %44 5 6 7 4
+ OpStore %39 %46
+ %48 = OpLoad %6 %8
+ %49 = OpLoad %6 %10
+ %50 = OpIAdd %6 %48 %49
+ OpStore %47 %50
+ %52 = OpLoad %6 %8
+ %53 = OpLoad %6 %10
+ %100 = OpCompositeConstruct %12 %52 %52
+ %101 = OpCompositeConstruct %12 %53 %53
+ %102 = OpISub %12 %100 %101
+ %54 = OpISub %6 %52 %53
+ OpStore %51 %54
+ %56 = OpLoad %6 %8
+ %57 = OpLoad %6 %10
+ %103 = OpCompositeConstruct %12 %56 %56
+ %104 = OpCompositeConstruct %12 %57 %57
+ %105 = OpIMul %12 %103 %104
+ %58 = OpIMul %6 %56 %57
+ OpStore %55 %58
+ %60 = OpLoad %6 %8
+ %61 = OpLoad %6 %10
+ %62 = OpSDiv %6 %60 %61
+ OpStore %59 %62
+ %64 = OpLoad %18 %20
+ %65 = OpLoad %18 %22
+ %106 = OpCompositeConstruct %24 %64 %64 %64
+ %107 = OpCompositeConstruct %24 %65 %65 %65
+ %108 = OpIAdd %24 %106 %107
+ %66 = OpIAdd %18 %64 %65
+ OpStore %63 %66
+ %68 = OpLoad %18 %20
+ %69 = OpLoad %18 %22
+ %109 = OpCompositeConstruct %24 %68 %68 %68
+ %110 = OpCompositeConstruct %24 %69 %69 %69
+ %111 = OpISub %24 %109 %110
+ %70 = OpISub %18 %68 %69
+ OpStore %67 %70
+ %72 = OpLoad %18 %20
+ %73 = OpLoad %18 %22
+ %112 = OpCompositeConstruct %24 %72 %72 %72
+ %113 = OpCompositeConstruct %24 %73 %73 %73
+ %114 = OpIMul %24 %112 %113
+ %74 = OpIMul %18 %72 %73
+ OpStore %71 %74
+ %76 = OpLoad %18 %20
+ %77 = OpLoad %18 %22
+ %78 = OpUDiv %18 %76 %77
+ OpStore %75 %78
+ %80 = OpLoad %31 %33
+ %81 = OpLoad %31 %35
+ %115 = OpCompositeConstruct %37 %80 %80 %80 %80
+ %116 = OpCompositeConstruct %37 %81 %81 %81 %81
+ %117 = OpFAdd %37 %115 %116
+ %82 = OpFAdd %31 %80 %81
+ OpStore %79 %82
+ %84 = OpLoad %31 %33
+ %85 = OpLoad %31 %35
+ %118 = OpCompositeConstruct %37 %84 %84 %84 %84
+ %119 = OpCompositeConstruct %37 %85 %85 %85 %85
+ %120 = OpFSub %37 %118 %119
+ %86 = OpFSub %31 %84 %85
+ OpStore %83 %86
+ %88 = OpLoad %31 %33
+ %89 = OpLoad %31 %35
+ %121 = OpCompositeConstruct %37 %88 %88 %88 %88
+ %122 = OpCompositeConstruct %37 %89 %89 %89 %89
+ %123 = OpFMul %37 %121 %122
+ %90 = OpFMul %31 %88 %89
+ OpStore %87 %90
+ %92 = OpLoad %31 %33
+ %93 = OpLoad %31 %35
+ %94 = OpFDiv %31 %92 %93
+ OpStore %91 %94
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationWrapVectorSynonym, DivSupportTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %97
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 10
+ %11 = OpConstant %6 -5
+ %12 = OpTypeVector %6 2
+ %13 = OpTypePointer Function %12
+ %18 = OpTypeInt 32 0
+ %19 = OpTypePointer Function %18
+ %21 = OpConstant %18 8
+ %23 = OpConstant %18 2
+ %24 = OpTypeVector %18 3
+ %25 = OpTypePointer Function %24
+ %31 = OpTypeFloat 32
+ %32 = OpTypePointer Function %31
+ %34 = OpConstant %31 3.29999995
+ %36 = OpConstant %31 1.10000002
+ %37 = OpTypeVector %31 4
+ %38 = OpTypePointer Function %37
+ %96 = OpTypePointer Input %31
+ %97 = OpVariable %96 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %14 = OpVariable %13 Function
+ %20 = OpVariable %19 Function
+ %22 = OpVariable %19 Function
+ %26 = OpVariable %25 Function
+ %33 = OpVariable %32 Function
+ %35 = OpVariable %32 Function
+ %39 = OpVariable %38 Function
+ %47 = OpVariable %7 Function
+ %51 = OpVariable %7 Function
+ %55 = OpVariable %7 Function
+ %59 = OpVariable %7 Function
+ %63 = OpVariable %19 Function
+ %67 = OpVariable %19 Function
+ %71 = OpVariable %19 Function
+ %75 = OpVariable %19 Function
+ %79 = OpVariable %32 Function
+ %83 = OpVariable %32 Function
+ %87 = OpVariable %32 Function
+ %91 = OpVariable %32 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %15 = OpLoad %6 %8
+ %16 = OpLoad %6 %10
+ %17 = OpCompositeConstruct %12 %15 %16
+ OpStore %14 %17
+ OpStore %20 %21
+ OpStore %22 %23
+ %27 = OpLoad %18 %20
+ %28 = OpLoad %18 %20
+ %29 = OpLoad %18 %22
+ %30 = OpCompositeConstruct %24 %27 %28 %29
+ OpStore %26 %30
+ OpStore %33 %34
+ OpStore %35 %36
+ %40 = OpLoad %31 %33
+ %41 = OpLoad %31 %33
+ %42 = OpLoad %31 %35
+ %43 = OpLoad %31 %35
+ %44 = OpCompositeConstruct %37 %40 %41 %42 %43
+ %45 = OpLoad %37 %39
+ %46 = OpVectorShuffle %37 %45 %44 5 6 7 4
+ OpStore %39 %46
+ %48 = OpLoad %6 %8
+ %49 = OpLoad %6 %10
+ %50 = OpIAdd %6 %48 %49
+ OpStore %47 %50
+ %52 = OpLoad %6 %8
+ %53 = OpLoad %6 %10
+ %54 = OpISub %6 %52 %53
+ OpStore %51 %54
+ %56 = OpLoad %6 %8
+ %57 = OpLoad %6 %10
+ %58 = OpIMul %6 %56 %57
+ OpStore %55 %58
+ %60 = OpLoad %6 %8
+ %61 = OpLoad %6 %10
+ %100 = OpCompositeConstruct %12 %60 %60
+ %101 = OpCompositeConstruct %12 %61 %61
+ %62 = OpSDiv %6 %60 %61
+ OpStore %59 %62
+ %64 = OpLoad %18 %20
+ %65 = OpLoad %18 %22
+ %66 = OpIAdd %18 %64 %65
+ OpStore %63 %66
+ %68 = OpLoad %18 %20
+ %69 = OpLoad %18 %22
+ %70 = OpISub %18 %68 %69
+ OpStore %67 %70
+ %72 = OpLoad %18 %20
+ %73 = OpLoad %18 %22
+ %74 = OpIMul %18 %72 %73
+ OpStore %71 %74
+ %76 = OpLoad %18 %20
+ %77 = OpLoad %18 %22
+ %102 = OpCompositeConstruct %24 %76 %76 %76
+ %103 = OpCompositeConstruct %24 %77 %77 %77
+ %78 = OpUDiv %18 %76 %77
+ OpStore %75 %78
+ %80 = OpLoad %31 %33
+ %81 = OpLoad %31 %35
+ %82 = OpFAdd %31 %80 %81
+ OpStore %79 %82
+ %84 = OpLoad %31 %33
+ %85 = OpLoad %31 %35
+ %86 = OpFSub %31 %84 %85
+ OpStore %83 %86
+ %88 = OpLoad %31 %33
+ %89 = OpLoad %31 %35
+ %90 = OpFMul %31 %88 %89
+ OpStore %87 %90
+ %92 = OpLoad %31 %33
+ %93 = OpLoad %31 %35
+ %104 = OpCompositeConstruct %37 %92 %92 %92 %92
+ %105 = OpCompositeConstruct %37 %93 %93 %93 %93
+ %94 = OpFDiv %31 %92 %93
+ OpStore %91 %94
+ OpReturn
+ OpFunctionEnd
+ )";
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ spvtools::ValidatorOptions validator_options;
+
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(100, {1}), MakeDataDescriptor(60, {}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(101, {1}), MakeDataDescriptor(61, {}));
+
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(102, {1}), MakeDataDescriptor(76, {}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(103, {1}), MakeDataDescriptor(77, {}));
+
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(104, {1}), MakeDataDescriptor(92, {}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(105, {1}), MakeDataDescriptor(93, {}));
+
+ // Div operations are not currently supported.
+ {
+ TransformationWrapVectorSynonym wrap_div_bad1(62, 100, 101, 106, 1);
+ ASSERT_FALSE(
+ wrap_div_bad1.IsApplicable(context.get(), transformation_context));
+
+ TransformationWrapVectorSynonym wrap_div_bad2(78, 102, 103, 106, 1);
+ ASSERT_FALSE(
+ wrap_div_bad2.IsApplicable(context.get(), transformation_context));
+
+ TransformationWrapVectorSynonym wrap_div_bad3(94, 104, 105, 106, 1);
+ ASSERT_FALSE(
+ wrap_div_bad3.IsApplicable(context.get(), transformation_context));
+ }
+}
+
+TEST(TransformationWrapVectorSynonym, AdditionalWidthSupportTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ OpCapability Int64
+ OpCapability Float64
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %97
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 64 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 10
+ %11 = OpConstant %6 -5
+ %12 = OpTypeVector %6 2
+ %13 = OpTypePointer Function %12
+ %18 = OpTypeInt 64 0
+ %19 = OpTypePointer Function %18
+ %21 = OpConstant %18 8
+ %23 = OpConstant %18 2
+ %24 = OpTypeVector %18 3
+ %25 = OpTypePointer Function %24
+ %31 = OpTypeFloat 64
+ %32 = OpTypePointer Function %31
+ %34 = OpConstant %31 3.29999995
+ %36 = OpConstant %31 1.10000002
+ %37 = OpTypeVector %31 4
+ %38 = OpTypePointer Function %37
+ %96 = OpTypePointer Input %31
+ %97 = OpVariable %96 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %14 = OpVariable %13 Function
+ %20 = OpVariable %19 Function
+ %22 = OpVariable %19 Function
+ %26 = OpVariable %25 Function
+ %33 = OpVariable %32 Function
+ %35 = OpVariable %32 Function
+ %39 = OpVariable %38 Function
+ %47 = OpVariable %7 Function
+ %51 = OpVariable %7 Function
+ %55 = OpVariable %7 Function
+ %59 = OpVariable %7 Function
+ %63 = OpVariable %19 Function
+ %67 = OpVariable %19 Function
+ %71 = OpVariable %19 Function
+ %75 = OpVariable %19 Function
+ %79 = OpVariable %32 Function
+ %83 = OpVariable %32 Function
+ %87 = OpVariable %32 Function
+ %91 = OpVariable %32 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %15 = OpLoad %6 %8
+ %16 = OpLoad %6 %10
+ %17 = OpCompositeConstruct %12 %15 %16
+ OpStore %14 %17
+ OpStore %20 %21
+ OpStore %22 %23
+ %27 = OpLoad %18 %20
+ %28 = OpLoad %18 %20
+ %29 = OpLoad %18 %22
+ %30 = OpCompositeConstruct %24 %27 %28 %29
+ OpStore %26 %30
+ OpStore %33 %34
+ OpStore %35 %36
+ %40 = OpLoad %31 %33
+ %41 = OpLoad %31 %33
+ %42 = OpLoad %31 %35
+ %43 = OpLoad %31 %35
+ %44 = OpCompositeConstruct %37 %40 %41 %42 %43
+ %45 = OpLoad %37 %39
+ %46 = OpVectorShuffle %37 %45 %44 5 6 7 4
+ OpStore %39 %46
+ %48 = OpLoad %6 %8
+ %49 = OpLoad %6 %10
+ %100 = OpCompositeConstruct %12 %48 %48
+ %101 = OpCompositeConstruct %12 %49 %49
+ %50 = OpIAdd %6 %48 %49
+ OpStore %47 %50
+ %52 = OpLoad %6 %8
+ %53 = OpLoad %6 %10
+ %54 = OpISub %6 %52 %53
+ OpStore %51 %54
+ %56 = OpLoad %6 %8
+ %57 = OpLoad %6 %10
+ %58 = OpIMul %6 %56 %57
+ OpStore %55 %58
+ %60 = OpLoad %6 %8
+ %61 = OpLoad %6 %10
+ %62 = OpSDiv %6 %60 %61
+ OpStore %59 %62
+ %64 = OpLoad %18 %20
+ %65 = OpLoad %18 %22
+ %66 = OpIAdd %18 %64 %65
+ OpStore %63 %66
+ %68 = OpLoad %18 %20
+ %69 = OpLoad %18 %22
+ %103 = OpCompositeConstruct %24 %68 %68 %68
+ %104 = OpCompositeConstruct %24 %69 %69 %69
+ %70 = OpISub %18 %68 %69
+ OpStore %67 %70
+ %72 = OpLoad %18 %20
+ %73 = OpLoad %18 %22
+ %74 = OpIMul %18 %72 %73
+ OpStore %71 %74
+ %76 = OpLoad %18 %20
+ %77 = OpLoad %18 %22
+ %78 = OpUDiv %18 %76 %77
+ OpStore %75 %78
+ %80 = OpLoad %31 %33
+ %81 = OpLoad %31 %35
+ %82 = OpFAdd %31 %80 %81
+ OpStore %79 %82
+ %84 = OpLoad %31 %33
+ %85 = OpLoad %31 %35
+ %86 = OpFSub %31 %84 %85
+ OpStore %83 %86
+ %88 = OpLoad %31 %33
+ %89 = OpLoad %31 %35
+ %106 = OpCompositeConstruct %37 %88 %88 %88 %88
+ %107 = OpCompositeConstruct %37 %89 %89 %89 %89
+ %90 = OpFMul %31 %88 %89
+ OpStore %87 %90
+ %92 = OpLoad %31 %33
+ %93 = OpLoad %31 %35
+ %94 = OpFDiv %31 %92 %93
+ OpStore %91 %94
+ OpReturn
+ OpFunctionEnd
+ )";
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ spvtools::ValidatorOptions validator_options;
+
+ // Check context validity.
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+
+ // Vec Type Id | Vector Type | Element Type id | Element Type |
+ // ------------+----------------+------------------+-----------------+
+ // 12 | vec2 | 6 | int64 |
+ // 24 | vec3 | 18 | uint64 |
+ // 37 | vec4 | 31 | float64 |
+
+ // Test support for 64-bit signed int.
+ {
+ // Assert that the target scalar instruction result id is relevant.
+ ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(50));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(100, {1}), MakeDataDescriptor(48, {}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(101, {1}), MakeDataDescriptor(49, {}));
+
+ // Good: The following transformation should be applicable.
+ TransformationWrapVectorSynonym wrap_add_int64(50, 100, 101, 102, 1);
+ ASSERT_TRUE(
+ wrap_add_int64.IsApplicable(context.get(), transformation_context));
+ // Insert an arithmetic instruction of the same type to add two vectors.
+ ApplyAndCheckFreshIds(wrap_add_int64, context.get(),
+ &transformation_context);
+
+ // |instruction_id| and id at |scalar_position of the result vector should
+ // be synonyms.
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(102, {1}), MakeDataDescriptor(50, {})));
+ }
+
+ // Test support for 64-bit unsigned int.
+ {
+ ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(70));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(103, {2}), MakeDataDescriptor(68, {}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(104, {2}), MakeDataDescriptor(69, {}));
+
+ // Good: The following transformation should be applicable.
+ TransformationWrapVectorSynonym wrap_sub_uint64(70, 103, 104, 105, 2);
+ ASSERT_TRUE(
+ wrap_sub_uint64.IsApplicable(context.get(), transformation_context));
+
+ ApplyAndCheckFreshIds(wrap_sub_uint64, context.get(),
+ &transformation_context);
+
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(105, {2}), MakeDataDescriptor(70, {})));
+ }
+
+ // Test support for 64-bit float.
+ {
+ ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(90));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(106, {3}), MakeDataDescriptor(88, {}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(107, {3}), MakeDataDescriptor(89, {}));
+
+ // Good: The following transformation should be applicable.
+ TransformationWrapVectorSynonym wrap_mul_float64(90, 106, 107, 108, 3);
+ ASSERT_TRUE(
+ wrap_mul_float64.IsApplicable(context.get(), transformation_context));
+
+ ApplyAndCheckFreshIds(wrap_mul_float64, context.get(),
+ &transformation_context);
+
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(108, {3}), MakeDataDescriptor(90, {})));
+ }
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ OpCapability Int64
+ OpCapability Float64
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %97
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 64 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 10
+ %11 = OpConstant %6 -5
+ %12 = OpTypeVector %6 2
+ %13 = OpTypePointer Function %12
+ %18 = OpTypeInt 64 0
+ %19 = OpTypePointer Function %18
+ %21 = OpConstant %18 8
+ %23 = OpConstant %18 2
+ %24 = OpTypeVector %18 3
+ %25 = OpTypePointer Function %24
+ %31 = OpTypeFloat 64
+ %32 = OpTypePointer Function %31
+ %34 = OpConstant %31 3.29999995
+ %36 = OpConstant %31 1.10000002
+ %37 = OpTypeVector %31 4
+ %38 = OpTypePointer Function %37
+ %96 = OpTypePointer Input %31
+ %97 = OpVariable %96 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %14 = OpVariable %13 Function
+ %20 = OpVariable %19 Function
+ %22 = OpVariable %19 Function
+ %26 = OpVariable %25 Function
+ %33 = OpVariable %32 Function
+ %35 = OpVariable %32 Function
+ %39 = OpVariable %38 Function
+ %47 = OpVariable %7 Function
+ %51 = OpVariable %7 Function
+ %55 = OpVariable %7 Function
+ %59 = OpVariable %7 Function
+ %63 = OpVariable %19 Function
+ %67 = OpVariable %19 Function
+ %71 = OpVariable %19 Function
+ %75 = OpVariable %19 Function
+ %79 = OpVariable %32 Function
+ %83 = OpVariable %32 Function
+ %87 = OpVariable %32 Function
+ %91 = OpVariable %32 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %15 = OpLoad %6 %8
+ %16 = OpLoad %6 %10
+ %17 = OpCompositeConstruct %12 %15 %16
+ OpStore %14 %17
+ OpStore %20 %21
+ OpStore %22 %23
+ %27 = OpLoad %18 %20
+ %28 = OpLoad %18 %20
+ %29 = OpLoad %18 %22
+ %30 = OpCompositeConstruct %24 %27 %28 %29
+ OpStore %26 %30
+ OpStore %33 %34
+ OpStore %35 %36
+ %40 = OpLoad %31 %33
+ %41 = OpLoad %31 %33
+ %42 = OpLoad %31 %35
+ %43 = OpLoad %31 %35
+ %44 = OpCompositeConstruct %37 %40 %41 %42 %43
+ %45 = OpLoad %37 %39
+ %46 = OpVectorShuffle %37 %45 %44 5 6 7 4
+ OpStore %39 %46
+ %48 = OpLoad %6 %8
+ %49 = OpLoad %6 %10
+ %100 = OpCompositeConstruct %12 %48 %48
+ %101 = OpCompositeConstruct %12 %49 %49
+ %102 = OpIAdd %12 %100 %101
+ %50 = OpIAdd %6 %48 %49
+ OpStore %47 %50
+ %52 = OpLoad %6 %8
+ %53 = OpLoad %6 %10
+ %54 = OpISub %6 %52 %53
+ OpStore %51 %54
+ %56 = OpLoad %6 %8
+ %57 = OpLoad %6 %10
+ %58 = OpIMul %6 %56 %57
+ OpStore %55 %58
+ %60 = OpLoad %6 %8
+ %61 = OpLoad %6 %10
+ %62 = OpSDiv %6 %60 %61
+ OpStore %59 %62
+ %64 = OpLoad %18 %20
+ %65 = OpLoad %18 %22
+ %66 = OpIAdd %18 %64 %65
+ OpStore %63 %66
+ %68 = OpLoad %18 %20
+ %69 = OpLoad %18 %22
+ %103 = OpCompositeConstruct %24 %68 %68 %68
+ %104 = OpCompositeConstruct %24 %69 %69 %69
+ %105 = OpISub %24 %103 %104
+ %70 = OpISub %18 %68 %69
+ OpStore %67 %70
+ %72 = OpLoad %18 %20
+ %73 = OpLoad %18 %22
+ %74 = OpIMul %18 %72 %73
+ OpStore %71 %74
+ %76 = OpLoad %18 %20
+ %77 = OpLoad %18 %22
+ %78 = OpUDiv %18 %76 %77
+ OpStore %75 %78
+ %80 = OpLoad %31 %33
+ %81 = OpLoad %31 %35
+ %82 = OpFAdd %31 %80 %81
+ OpStore %79 %82
+ %84 = OpLoad %31 %33
+ %85 = OpLoad %31 %35
+ %86 = OpFSub %31 %84 %85
+ OpStore %83 %86
+ %88 = OpLoad %31 %33
+ %89 = OpLoad %31 %35
+ %106 = OpCompositeConstruct %37 %88 %88 %88 %88
+ %107 = OpCompositeConstruct %37 %89 %89 %89 %89
+ %108 = OpFMul %37 %106 %107
+ %90 = OpFMul %31 %88 %89
+ OpStore %87 %90
+ %92 = OpLoad %31 %33
+ %93 = OpLoad %31 %35
+ %94 = OpFDiv %31 %92 %93
+ OpStore %91 %94
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationWrapVectorSynonym, DifferentVectorSignedness) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeVector %6 2
+ %8 = OpTypePointer Function %7
+ %10 = OpConstant %6 1
+ %11 = OpConstant %6 0
+ %12 = OpConstantComposite %7 %10 %11
+ %14 = OpTypeInt 32 0
+ %15 = OpTypeVector %14 2
+ %18 = OpConstant %14 3
+ %19 = OpConstant %14 0
+ %20 = OpConstantComposite %15 %18 %19
+ %21 = OpConstantComposite %15 %19 %18
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %100 = OpIAdd %14 %10 %18
+ %101 = OpIAdd %6 %10 %18
+ %102 = OpIAdd %6 %18 %19
+ OpReturn
+ OpFunctionEnd
+ )";
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ spvtools::ValidatorOptions validator_options;
+
+ // Check context validity.
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(10, {}), MakeDataDescriptor(12, {0}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(18, {}), MakeDataDescriptor(20, {0}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(19, {}), MakeDataDescriptor(21, {0}));
+
+ {
+ TransformationWrapVectorSynonym transformation1(100, 12, 20, 200, 0);
+ ASSERT_TRUE(
+ transformation1.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(transformation1, context.get(),
+ &transformation_context);
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(200, {0}), MakeDataDescriptor(100, {})));
+ }
+
+ {
+ TransformationWrapVectorSynonym transformation2(101, 12, 20, 201, 0);
+ ASSERT_TRUE(
+ transformation2.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(transformation2, context.get(),
+ &transformation_context);
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(201, {0}), MakeDataDescriptor(101, {})));
+ }
+
+ {
+ TransformationWrapVectorSynonym transformation3(102, 20, 21, 202, 0);
+ ASSERT_TRUE(
+ transformation3.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(transformation3, context.get(),
+ &transformation_context);
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(202, {0}), MakeDataDescriptor(102, {})));
+ }
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeVector %6 2
+ %8 = OpTypePointer Function %7
+ %10 = OpConstant %6 1
+ %11 = OpConstant %6 0
+ %12 = OpConstantComposite %7 %10 %11
+ %14 = OpTypeInt 32 0
+ %15 = OpTypeVector %14 2
+ %18 = OpConstant %14 3
+ %19 = OpConstant %14 0
+ %20 = OpConstantComposite %15 %18 %19
+ %21 = OpConstantComposite %15 %19 %18
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %200 = OpIAdd %15 %12 %20
+ %100 = OpIAdd %14 %10 %18
+ %201 = OpIAdd %7 %12 %20
+ %101 = OpIAdd %6 %10 %18
+ %202 = OpIAdd %7 %20 %21
+ %102 = OpIAdd %6 %18 %19
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationWrapVectorSynonym, SignednessDoesNotMatchResultType) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeVector %6 2
+ %8 = OpTypePointer Function %7
+ %10 = OpConstant %6 1
+ %11 = OpConstant %6 0
+ %12 = OpConstantComposite %7 %10 %11
+ %13 = OpConstantComposite %7 %11 %10
+ %14 = OpTypeInt 32 0
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %100 = OpIAdd %14 %10 %11
+ OpReturn
+ OpFunctionEnd
+ )";
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ spvtools::ValidatorOptions validator_options;
+
+ // Check context validity.
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(10, {}), MakeDataDescriptor(12, {0}));
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(11, {}), MakeDataDescriptor(13, {0}));
+
+ ASSERT_FALSE(TransformationWrapVectorSynonym(100, 12, 13, 200, 0)
+ .IsApplicable(context.get(), transformation_context));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/test/fuzzers/BUILD.gn b/test/fuzzers/BUILD.gn
index ec09b2b0..fc458a4f 100644
--- a/test/fuzzers/BUILD.gn
+++ b/test/fuzzers/BUILD.gn
@@ -48,6 +48,7 @@ template("spvtools_fuzzer") {
source_set(target_name) {
testonly = true
sources = invoker.sources
+ sources += [ "random_generator.cpp" ]
deps = [
"../..:spvtools",
"../..:spvtools_opt",
@@ -86,18 +87,21 @@ spvtools_fuzzer("spvtools_dis_fuzzer_src") {
spvtools_fuzzer("spvtools_opt_performance_fuzzer_src") {
sources = [
"spvtools_opt_performance_fuzzer.cpp",
+ "spvtools_opt_fuzzer_common.cpp",
]
}
spvtools_fuzzer("spvtools_opt_legalization_fuzzer_src") {
sources = [
"spvtools_opt_legalization_fuzzer.cpp",
+ "spvtools_opt_fuzzer_common.cpp",
]
}
spvtools_fuzzer("spvtools_opt_size_fuzzer_src") {
sources = [
"spvtools_opt_size_fuzzer.cpp",
+ "spvtools_opt_fuzzer_common.cpp",
]
}
diff --git a/test/fuzzers/CMakeLists.txt b/test/fuzzers/CMakeLists.txt
new file mode 100644
index 00000000..e1fe516a
--- /dev/null
+++ b/test/fuzzers/CMakeLists.txt
@@ -0,0 +1,56 @@
+# Copyright (c) 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function(add_spvtools_libfuzzer_target)
+ set(one_value_args TARGET)
+ set(multi_value_args SRCS LIBS)
+ cmake_parse_arguments(
+ ARG "" "${one_value_args}" "${multi_value_args}" ${ARGN})
+
+ add_executable(${ARG_TARGET} ${ARG_SRCS})
+ spvtools_default_compile_options(${ARG_TARGET})
+ target_link_libraries(${ARG_TARGET} PRIVATE ${ARG_LIBS})
+ target_include_directories(${ARG_TARGET} PRIVATE
+ ${spirv-tools_SOURCE_DIR}
+ ${spirv-tools_BINARY_DIR}
+ )
+ set_property(TARGET ${ARG_TARGET} PROPERTY FOLDER "SPIRV-Tools libFuzzer targets")
+ if(NOT ${SPIRV_LIB_FUZZING_ENGINE_LINK_OPTIONS} STREQUAL "")
+ # This is set when the fuzzers are being built by OSS-Fuzz. In this case the
+ # variable provides the necessary linker flags, and OSS-Fuzz will take care
+ # of passing suitable compiler flags.
+ target_link_options(${ARG_TARGET} PRIVATE ${SPIRV_LIB_FUZZING_ENGINE_LINK_OPTIONS})
+ else()
+ # When the fuzzers are being built outside of OSS-Fuzz, standard libFuzzer
+ # arguments to enable fuzzing are used.
+ target_compile_options(${ARG_TARGET} PRIVATE "-fsanitize=fuzzer")
+ target_link_options(${ARG_TARGET} PRIVATE "-fsanitize=fuzzer")
+ endif()
+endfunction()
+
+if (${SPIRV_BUILD_LIBFUZZER_TARGETS})
+ if(NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
+ message(FATAL_ERROR "The libFuzzer targets are only supported with the Clang compiler. Compiler '${CMAKE_CXX_COMPILER_ID}' is not supported!")
+ endif()
+ add_spvtools_libfuzzer_target(TARGET spvtools_as_fuzzer SRCS spvtools_as_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
+ add_spvtools_libfuzzer_target(TARGET spvtools_binary_parser_fuzzer SRCS spvtools_binary_parser_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
+ add_spvtools_libfuzzer_target(TARGET spvtools_dis_fuzzer SRCS spvtools_dis_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
+ add_spvtools_libfuzzer_target(TARGET spvtools_opt_legalization_fuzzer SRCS spvtools_opt_legalization_fuzzer.cpp spvtools_opt_fuzzer_common.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
+ add_spvtools_libfuzzer_target(TARGET spvtools_opt_performance_fuzzer SRCS spvtools_opt_performance_fuzzer.cpp spvtools_opt_fuzzer_common.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
+ add_spvtools_libfuzzer_target(TARGET spvtools_opt_size_fuzzer SRCS spvtools_opt_size_fuzzer.cpp spvtools_opt_fuzzer_common.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
+ add_spvtools_libfuzzer_target(TARGET spvtools_val_fuzzer SRCS spvtools_val_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
+ if (${SPIRV_BUILD_FUZZER})
+ add_spvtools_libfuzzer_target(TARGET spvtools_fuzz_fuzzer SRCS spvtools_fuzz_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-fuzz ${SPIRV_TOOLS_FULL_VISIBILITY})
+ endif()
+endif()
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_000.spv b/test/fuzzers/corpora/spv/graphicsfuzz_000.spv
new file mode 100644
index 00000000..c203cec4
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_000.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_001.spv b/test/fuzzers/corpora/spv/graphicsfuzz_001.spv
new file mode 100644
index 00000000..c3dfbd9a
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_001.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_002.spv b/test/fuzzers/corpora/spv/graphicsfuzz_002.spv
new file mode 100644
index 00000000..5a3c145f
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_002.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_003.spv b/test/fuzzers/corpora/spv/graphicsfuzz_003.spv
new file mode 100644
index 00000000..c0e0a298
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_003.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_004.spv b/test/fuzzers/corpora/spv/graphicsfuzz_004.spv
new file mode 100644
index 00000000..f1afc725
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_004.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_005.spv b/test/fuzzers/corpora/spv/graphicsfuzz_005.spv
new file mode 100644
index 00000000..c6bf437d
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_005.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_006.spv b/test/fuzzers/corpora/spv/graphicsfuzz_006.spv
new file mode 100644
index 00000000..d0d4a700
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_006.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_007.spv b/test/fuzzers/corpora/spv/graphicsfuzz_007.spv
new file mode 100644
index 00000000..7f2f982d
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_007.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_008.spv b/test/fuzzers/corpora/spv/graphicsfuzz_008.spv
new file mode 100644
index 00000000..c67dc976
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_008.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_009.spv b/test/fuzzers/corpora/spv/graphicsfuzz_009.spv
new file mode 100644
index 00000000..68740b39
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_009.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_010.spv b/test/fuzzers/corpora/spv/graphicsfuzz_010.spv
new file mode 100644
index 00000000..fedc8b4c
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_010.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_011.spv b/test/fuzzers/corpora/spv/graphicsfuzz_011.spv
new file mode 100644
index 00000000..882bfe6b
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_011.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_012.spv b/test/fuzzers/corpora/spv/graphicsfuzz_012.spv
new file mode 100644
index 00000000..bd3c1144
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_012.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_013.spv b/test/fuzzers/corpora/spv/graphicsfuzz_013.spv
new file mode 100644
index 00000000..d7e35b4b
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_013.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_014.spv b/test/fuzzers/corpora/spv/graphicsfuzz_014.spv
new file mode 100644
index 00000000..abf54889
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_014.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_015.spv b/test/fuzzers/corpora/spv/graphicsfuzz_015.spv
new file mode 100644
index 00000000..868ab046
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_015.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_016.spv b/test/fuzzers/corpora/spv/graphicsfuzz_016.spv
new file mode 100644
index 00000000..a7cef28a
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_016.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_017.spv b/test/fuzzers/corpora/spv/graphicsfuzz_017.spv
new file mode 100644
index 00000000..6d338a91
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_017.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_018.spv b/test/fuzzers/corpora/spv/graphicsfuzz_018.spv
new file mode 100644
index 00000000..c7bf440c
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_018.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_019.spv b/test/fuzzers/corpora/spv/graphicsfuzz_019.spv
new file mode 100644
index 00000000..99d7e2df
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_019.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_020.spv b/test/fuzzers/corpora/spv/graphicsfuzz_020.spv
new file mode 100644
index 00000000..9e241246
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_020.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_021.spv b/test/fuzzers/corpora/spv/graphicsfuzz_021.spv
new file mode 100644
index 00000000..02734af1
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_021.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_022.spv b/test/fuzzers/corpora/spv/graphicsfuzz_022.spv
new file mode 100644
index 00000000..cd9ab76b
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_022.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_023.spv b/test/fuzzers/corpora/spv/graphicsfuzz_023.spv
new file mode 100644
index 00000000..d0ec5d31
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_023.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_024.spv b/test/fuzzers/corpora/spv/graphicsfuzz_024.spv
new file mode 100644
index 00000000..503627f6
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_024.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_025.spv b/test/fuzzers/corpora/spv/graphicsfuzz_025.spv
new file mode 100644
index 00000000..c4e0aa35
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_025.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_026.spv b/test/fuzzers/corpora/spv/graphicsfuzz_026.spv
new file mode 100644
index 00000000..c5b3cedd
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_026.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_027.spv b/test/fuzzers/corpora/spv/graphicsfuzz_027.spv
new file mode 100644
index 00000000..626e606f
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_027.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_028.spv b/test/fuzzers/corpora/spv/graphicsfuzz_028.spv
new file mode 100644
index 00000000..a9a43577
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_028.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_029.spv b/test/fuzzers/corpora/spv/graphicsfuzz_029.spv
new file mode 100644
index 00000000..18e49a5a
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_029.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_030.spv b/test/fuzzers/corpora/spv/graphicsfuzz_030.spv
new file mode 100644
index 00000000..ac4ea364
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_030.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_031.spv b/test/fuzzers/corpora/spv/graphicsfuzz_031.spv
new file mode 100644
index 00000000..2f8eff69
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_031.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_032.spv b/test/fuzzers/corpora/spv/graphicsfuzz_032.spv
new file mode 100644
index 00000000..a5808ecf
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_032.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_033.spv b/test/fuzzers/corpora/spv/graphicsfuzz_033.spv
new file mode 100644
index 00000000..98cf02e2
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_033.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_034.spv b/test/fuzzers/corpora/spv/graphicsfuzz_034.spv
new file mode 100644
index 00000000..306fd845
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_034.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_035.spv b/test/fuzzers/corpora/spv/graphicsfuzz_035.spv
new file mode 100644
index 00000000..7ed19e09
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_035.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_036.spv b/test/fuzzers/corpora/spv/graphicsfuzz_036.spv
new file mode 100644
index 00000000..22e67839
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_036.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_037.spv b/test/fuzzers/corpora/spv/graphicsfuzz_037.spv
new file mode 100644
index 00000000..19fbd20f
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_037.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_038.spv b/test/fuzzers/corpora/spv/graphicsfuzz_038.spv
new file mode 100644
index 00000000..7bc54890
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_038.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_039.spv b/test/fuzzers/corpora/spv/graphicsfuzz_039.spv
new file mode 100644
index 00000000..01b908de
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_039.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_040.spv b/test/fuzzers/corpora/spv/graphicsfuzz_040.spv
new file mode 100644
index 00000000..bd131571
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_040.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_041.spv b/test/fuzzers/corpora/spv/graphicsfuzz_041.spv
new file mode 100644
index 00000000..6e355c20
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_041.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_042.spv b/test/fuzzers/corpora/spv/graphicsfuzz_042.spv
new file mode 100644
index 00000000..d356cb19
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_042.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_043.spv b/test/fuzzers/corpora/spv/graphicsfuzz_043.spv
new file mode 100644
index 00000000..49c87ed4
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_043.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_044.spv b/test/fuzzers/corpora/spv/graphicsfuzz_044.spv
new file mode 100644
index 00000000..c7e36d5c
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_044.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_045.spv b/test/fuzzers/corpora/spv/graphicsfuzz_045.spv
new file mode 100644
index 00000000..ad197263
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_045.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_046.spv b/test/fuzzers/corpora/spv/graphicsfuzz_046.spv
new file mode 100644
index 00000000..3b3678ad
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_046.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_047.spv b/test/fuzzers/corpora/spv/graphicsfuzz_047.spv
new file mode 100644
index 00000000..7253247b
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_047.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_048.spv b/test/fuzzers/corpora/spv/graphicsfuzz_048.spv
new file mode 100644
index 00000000..5c05c1c0
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_048.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_049.spv b/test/fuzzers/corpora/spv/graphicsfuzz_049.spv
new file mode 100644
index 00000000..22bc9c21
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_049.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_050.spv b/test/fuzzers/corpora/spv/graphicsfuzz_050.spv
new file mode 100644
index 00000000..8f847880
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_050.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_051.spv b/test/fuzzers/corpora/spv/graphicsfuzz_051.spv
new file mode 100644
index 00000000..727c64d2
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_051.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_052.spv b/test/fuzzers/corpora/spv/graphicsfuzz_052.spv
new file mode 100644
index 00000000..d9ba7418
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_052.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_053.spv b/test/fuzzers/corpora/spv/graphicsfuzz_053.spv
new file mode 100644
index 00000000..d8270049
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_053.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_054.spv b/test/fuzzers/corpora/spv/graphicsfuzz_054.spv
new file mode 100644
index 00000000..a3aec7b6
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_054.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_055.spv b/test/fuzzers/corpora/spv/graphicsfuzz_055.spv
new file mode 100644
index 00000000..2da1375f
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_055.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_056.spv b/test/fuzzers/corpora/spv/graphicsfuzz_056.spv
new file mode 100644
index 00000000..515d4f1c
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_056.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_057.spv b/test/fuzzers/corpora/spv/graphicsfuzz_057.spv
new file mode 100644
index 00000000..2a0f4897
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_057.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_058.spv b/test/fuzzers/corpora/spv/graphicsfuzz_058.spv
new file mode 100644
index 00000000..3211ec20
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_058.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_059.spv b/test/fuzzers/corpora/spv/graphicsfuzz_059.spv
new file mode 100644
index 00000000..89e34ecf
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_059.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_060.spv b/test/fuzzers/corpora/spv/graphicsfuzz_060.spv
new file mode 100644
index 00000000..d1fdaecc
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_060.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_061.spv b/test/fuzzers/corpora/spv/graphicsfuzz_061.spv
new file mode 100644
index 00000000..de10f68b
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_061.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_062.spv b/test/fuzzers/corpora/spv/graphicsfuzz_062.spv
new file mode 100644
index 00000000..503304c3
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_062.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_063.spv b/test/fuzzers/corpora/spv/graphicsfuzz_063.spv
new file mode 100644
index 00000000..75d9b4c4
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_063.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_064.spv b/test/fuzzers/corpora/spv/graphicsfuzz_064.spv
new file mode 100644
index 00000000..0b902cb2
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_064.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_065.spv b/test/fuzzers/corpora/spv/graphicsfuzz_065.spv
new file mode 100644
index 00000000..437e45b0
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_065.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_066.spv b/test/fuzzers/corpora/spv/graphicsfuzz_066.spv
new file mode 100644
index 00000000..5585a6f8
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_066.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_067.spv b/test/fuzzers/corpora/spv/graphicsfuzz_067.spv
new file mode 100644
index 00000000..763fd34f
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_067.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_068.spv b/test/fuzzers/corpora/spv/graphicsfuzz_068.spv
new file mode 100644
index 00000000..1ad0bd50
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_068.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_069.spv b/test/fuzzers/corpora/spv/graphicsfuzz_069.spv
new file mode 100644
index 00000000..f1e7950b
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_069.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_070.spv b/test/fuzzers/corpora/spv/graphicsfuzz_070.spv
new file mode 100644
index 00000000..47f4c829
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_070.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_071.spv b/test/fuzzers/corpora/spv/graphicsfuzz_071.spv
new file mode 100644
index 00000000..3eac74aa
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_071.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_072.spv b/test/fuzzers/corpora/spv/graphicsfuzz_072.spv
new file mode 100644
index 00000000..e2461157
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_072.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_073.spv b/test/fuzzers/corpora/spv/graphicsfuzz_073.spv
new file mode 100644
index 00000000..b7e915be
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_073.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_074.spv b/test/fuzzers/corpora/spv/graphicsfuzz_074.spv
new file mode 100644
index 00000000..35887578
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_074.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_075.spv b/test/fuzzers/corpora/spv/graphicsfuzz_075.spv
new file mode 100644
index 00000000..d3dc46dc
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_075.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_076.spv b/test/fuzzers/corpora/spv/graphicsfuzz_076.spv
new file mode 100644
index 00000000..11bee684
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_076.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_077.spv b/test/fuzzers/corpora/spv/graphicsfuzz_077.spv
new file mode 100644
index 00000000..d1b8c76a
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_077.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_078.spv b/test/fuzzers/corpora/spv/graphicsfuzz_078.spv
new file mode 100644
index 00000000..4739a9ac
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_078.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_079.spv b/test/fuzzers/corpora/spv/graphicsfuzz_079.spv
new file mode 100644
index 00000000..6553ecf4
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_079.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_080.spv b/test/fuzzers/corpora/spv/graphicsfuzz_080.spv
new file mode 100644
index 00000000..802375b4
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_080.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_081.spv b/test/fuzzers/corpora/spv/graphicsfuzz_081.spv
new file mode 100644
index 00000000..72d1027f
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_081.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_082.spv b/test/fuzzers/corpora/spv/graphicsfuzz_082.spv
new file mode 100644
index 00000000..619c742c
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_082.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_083.spv b/test/fuzzers/corpora/spv/graphicsfuzz_083.spv
new file mode 100644
index 00000000..fa27c33a
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_083.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_084.spv b/test/fuzzers/corpora/spv/graphicsfuzz_084.spv
new file mode 100644
index 00000000..14f7bfdd
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_084.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_085.spv b/test/fuzzers/corpora/spv/graphicsfuzz_085.spv
new file mode 100644
index 00000000..5f5fc03d
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_085.spv
Binary files differ
diff --git a/test/fuzzers/corpora/spv/graphicsfuzz_086.spv b/test/fuzzers/corpora/spv/graphicsfuzz_086.spv
new file mode 100644
index 00000000..a3119927
--- /dev/null
+++ b/test/fuzzers/corpora/spv/graphicsfuzz_086.spv
Binary files differ
diff --git a/test/fuzzers/random_generator.cpp b/test/fuzzers/random_generator.cpp
new file mode 100644
index 00000000..801a9ffe
--- /dev/null
+++ b/test/fuzzers/random_generator.cpp
@@ -0,0 +1,135 @@
+// Copyright (c) 2021 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 "test/fuzzers/random_generator.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+
+namespace spvtools {
+namespace fuzzers {
+
+namespace {
+/// Generate integer from uniform distribution
+/// @tparam I - integer type
+/// @param engine - random number engine to use
+/// @param lower - Lower bound of integer generated
+/// @param upper - Upper bound of integer generated
+/// @returns i, where lower <= i < upper
+template <typename I>
+I RandomUInt(std::mt19937_64* engine, I lower, I upper) {
+ assert(lower < upper && "|lower| must be stictly less than |upper|");
+ return std::uniform_int_distribution<I>(lower, upper - 1)(*engine);
+}
+
+/// Helper for obtaining a seed bias value for HashCombine with a bit-width
+/// dependent on the size of size_t.
+template <int SIZE_OF_SIZE_T>
+struct HashCombineOffset {};
+/// Specialization of HashCombineOffset for size_t == 4.
+template <>
+struct HashCombineOffset<4> {
+ /// @returns the seed bias value for HashCombine()
+ static constexpr inline uint32_t value() {
+ return 0x9e3779b9; // Fractional portion of Golden Ratio, suggested by
+ // Linux Kernel and Knuth's Art of Computer Programming
+ }
+};
+/// Specialization of HashCombineOffset for size_t == 8.
+template <>
+struct HashCombineOffset<8> {
+ /// @returns the seed bias value for HashCombine()
+ static constexpr inline uint64_t value() {
+ return 0x9e3779b97f4a7c16; // Fractional portion of Golden Ratio, suggested
+ // by Linux Kernel and Knuth's Art of Computer
+ // Programming
+ }
+};
+
+/// HashCombine "hashes" together an existing hash and hashable values.
+template <typename T>
+void HashCombine(size_t* hash, const T& value) {
+ constexpr size_t offset = HashCombineOffset<sizeof(size_t)>::value();
+ *hash ^= std::hash<T>()(value) + offset + (*hash << 6) + (*hash >> 2);
+}
+
+/// Calculate the hash for the contents of a C-style data buffer
+/// @param data - pointer to buffer to be hashed
+/// @param size - number of elements in buffer
+/// @returns hash of the data in the buffer
+size_t HashBuffer(const uint8_t* data, const size_t size) {
+ size_t hash =
+ static_cast<size_t>(0xCA8945571519E991); // seed with an arbitrary prime
+ HashCombine(&hash, size);
+ for (size_t i = 0; i < size; i++) {
+ HashCombine(&hash, data[i]);
+ }
+ return hash;
+}
+
+} // namespace
+
+RandomGenerator::RandomGenerator(uint64_t seed) : engine_(seed) {}
+
+RandomGenerator::RandomGenerator(const uint8_t* data, size_t size) {
+ RandomGenerator(RandomGenerator::CalculateSeed(data, size));
+}
+
+spv_target_env RandomGenerator::GetTargetEnv() {
+ spv_target_env result;
+
+ // Need to check that the generated value isn't for a deprecated target env.
+ do {
+ result = static_cast<spv_target_env>(
+ RandomUInt(&engine_, 0u, static_cast<unsigned int>(SPV_ENV_MAX)));
+ } while (!spvIsValidEnv(result));
+
+ return result;
+}
+
+uint32_t RandomGenerator::GetUInt32(uint32_t lower, uint32_t upper) {
+ return RandomUInt(&engine_, lower, upper);
+}
+
+uint32_t RandomGenerator::GetUInt32(uint32_t bound) {
+ assert(bound > 0 && "|bound| must be greater than 0");
+ return RandomUInt(&engine_, 0u, bound);
+}
+
+uint64_t RandomGenerator::CalculateSeed(const uint8_t* data, size_t size) {
+ assert(data != nullptr && "|data| must be !nullptr");
+
+ // Number of bytes we want to skip at the start of data for the hash.
+ // Fewer bytes may be skipped when `size` is small.
+ // Has lower precedence than kHashDesiredMinBytes.
+ static const int64_t kHashDesiredLeadingSkipBytes = 5;
+ // Minimum number of bytes we want to use in the hash.
+ // Used for short buffers.
+ static const int64_t kHashDesiredMinBytes = 4;
+ // Maximum number of bytes we want to use in the hash.
+ static const int64_t kHashDesiredMaxBytes = 32;
+ int64_t size_i64 = static_cast<int64_t>(size);
+ int64_t hash_begin_i64 =
+ std::min(kHashDesiredLeadingSkipBytes,
+ std::max<int64_t>(size_i64 - kHashDesiredMinBytes, 0));
+ int64_t hash_end_i64 =
+ std::min(hash_begin_i64 + kHashDesiredMaxBytes, size_i64);
+ size_t hash_begin = static_cast<size_t>(hash_begin_i64);
+ size_t hash_size = static_cast<size_t>(hash_end_i64) - hash_begin;
+ return HashBuffer(data + hash_begin, hash_size);
+}
+
+} // namespace fuzzers
+} // namespace spvtools
diff --git a/test/fuzzers/random_generator.h b/test/fuzzers/random_generator.h
new file mode 100644
index 00000000..b121fe8c
--- /dev/null
+++ b/test/fuzzers/random_generator.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2021 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.
+
+#ifndef TEST_FUZZERS_RANDOM_GENERATOR_H_
+#define TEST_FUZZERS_RANDOM_GENERATOR_H_
+
+#include <cstdint>
+#include <random>
+
+#include "source/spirv_target_env.h"
+
+namespace spvtools {
+namespace fuzzers {
+
+/// Pseudo random generator utility class for fuzzing
+class RandomGenerator {
+ public:
+ /// @brief Initializes the internal engine
+ /// @param seed - seed value passed to engine
+ explicit RandomGenerator(uint64_t seed);
+
+ /// @brief Initializes the internal engine
+ /// @param data - data to calculate the seed from
+ /// @param size - size of the data
+ explicit RandomGenerator(const uint8_t* data, size_t size);
+
+ ~RandomGenerator() {}
+
+ /// Calculate a seed value based on a blob of data.
+ /// Currently hashes bytes near the front of the buffer, after skipping N
+ /// bytes.
+ /// @param data - pointer to data to base calculation off of, must be !nullptr
+ /// @param size - number of elements in |data|, must be > 0
+ static uint64_t CalculateSeed(const uint8_t* data, size_t size);
+
+ /// Get random valid target env.
+ spv_target_env GetTargetEnv();
+
+ /// Get uint32_t value from uniform distribution.
+ /// @param lower - lower bound of integer generated
+ /// @param upper - upper bound of integer generated
+ /// @returns i, where lower <= i < upper
+ uint32_t GetUInt32(uint32_t lower, uint32_t upper);
+
+ /// Get uint32_t value from uniform distribution.
+ /// @param bound - Upper bound of integer generated
+ /// @returns i, where 0 <= i < bound
+ uint32_t GetUInt32(uint32_t bound);
+
+ private:
+ std::mt19937_64 engine_;
+}; // class RandomGenerator
+
+} // namespace fuzzers
+} // namespace spvtools
+
+#endif // TEST_FUZZERS_RANDOM_GENERATOR_UTILS_H_
diff --git a/test/fuzzers/spvtools_as_fuzzer.cpp b/test/fuzzers/spvtools_as_fuzzer.cpp
index 8cecb05f..8ead1cff 100644
--- a/test/fuzzers/spvtools_as_fuzzer.cpp
+++ b/test/fuzzers/spvtools_as_fuzzer.cpp
@@ -18,27 +18,27 @@
#include "source/spirv_target_env.h"
#include "spirv-tools/libspirv.hpp"
+#include "test/fuzzers/random_generator.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- if (size < sizeof(spv_target_env) + 1) return 0;
-
- const spv_context context =
- spvContextCreate(*reinterpret_cast<const spv_target_env*>(data));
- if (context == nullptr) return 0;
-
- data += sizeof(spv_target_env);
- size -= sizeof(spv_target_env);
+ spv_target_env target_env = SPV_ENV_UNIVERSAL_1_0;
+ if (size > 0) {
+ spvtools::fuzzers::RandomGenerator random_gen(data, size);
+ target_env = random_gen.GetTargetEnv();
+ }
- std::vector<uint32_t> input;
+ const spv_context context = spvContextCreate(target_env);
+ if (context == nullptr) {
+ return 0;
+ }
- std::vector<char> input_str;
- size_t char_count = input.size() * sizeof(uint32_t) / sizeof(char);
- input_str.resize(char_count);
- memcpy(input_str.data(), input.data(), input.size() * sizeof(uint32_t));
+ std::vector<char> contents;
+ contents.resize(size);
+ memcpy(contents.data(), data, size);
spv_binary binary = nullptr;
spv_diagnostic diagnostic = nullptr;
- spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(),
+ spvTextToBinaryWithOptions(context, contents.data(), contents.size(),
SPV_TEXT_TO_BINARY_OPTION_NONE, &binary,
&diagnostic);
if (diagnostic) {
@@ -52,7 +52,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
binary = nullptr;
}
- spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(),
+ spvTextToBinaryWithOptions(context, contents.data(), contents.size(),
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS,
&binary, &diagnostic);
if (diagnostic) {
diff --git a/test/fuzzers/spvtools_binary_parser_fuzzer.cpp b/test/fuzzers/spvtools_binary_parser_fuzzer.cpp
index 76ba4d9e..3a97db41 100644
--- a/test/fuzzers/spvtools_binary_parser_fuzzer.cpp
+++ b/test/fuzzers/spvtools_binary_parser_fuzzer.cpp
@@ -16,16 +16,18 @@
#include <vector>
#include "spirv-tools/libspirv.hpp"
+#include "test/fuzzers/random_generator.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- if (size < sizeof(spv_target_env) + 1) return 0;
-
- const spv_context context =
- spvContextCreate(*reinterpret_cast<const spv_target_env*>(data));
- if (context == nullptr) return 0;
+ if (size < 1) {
+ return 0;
+ }
- data += sizeof(spv_target_env);
- size -= sizeof(spv_target_env);
+ spvtools::fuzzers::RandomGenerator random_gen(data, size);
+ const spv_context context = spvContextCreate(random_gen.GetTargetEnv());
+ if (context == nullptr) {
+ return 0;
+ }
std::vector<uint32_t> input;
input.resize(size >> 2);
diff --git a/test/fuzzers/spvtools_dis_fuzzer.cpp b/test/fuzzers/spvtools_dis_fuzzer.cpp
index ca9a52d8..0cb0eff5 100644
--- a/test/fuzzers/spvtools_dis_fuzzer.cpp
+++ b/test/fuzzers/spvtools_dis_fuzzer.cpp
@@ -18,16 +18,20 @@
#include "source/spirv_target_env.h"
#include "spirv-tools/libspirv.hpp"
+#include "test/fuzzers/random_generator.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- if (size < sizeof(spv_target_env) + 1) return 0;
-
- const spv_context context =
- spvContextCreate(*reinterpret_cast<const spv_target_env*>(data));
- if (context == nullptr) return 0;
+ if (size < 4) {
+ // There are not enough bytes to constitute a binary that can be
+ // disassembled.
+ return 0;
+ }
- data += sizeof(spv_target_env);
- size -= sizeof(spv_target_env);
+ spvtools::fuzzers::RandomGenerator random_gen(data, size);
+ const spv_context context = spvContextCreate(random_gen.GetTargetEnv());
+ if (context == nullptr) {
+ return 0;
+ }
std::vector<uint32_t> input;
input.resize(size >> 2);
diff --git a/test/fuzzers/spvtools_fuzz_fuzzer.cpp b/test/fuzzers/spvtools_fuzz_fuzzer.cpp
new file mode 100644
index 00000000..d43920cf
--- /dev/null
+++ b/test/fuzzers/spvtools_fuzz_fuzzer.cpp
@@ -0,0 +1,80 @@
+// Copyright (c) 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+#include <vector>
+
+#include "source/fuzz/fuzzer.h"
+#include "source/fuzz/pseudo_random_generator.h"
+#include "spirv-tools/libspirv.hpp"
+#include "test/fuzzers/random_generator.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size == 0 || (size % sizeof(uint32_t)) != 0) {
+ // An empty binary, or a binary whose size is not a multiple of word-size,
+ // cannot be valid, so can be rejected immediately.
+ return 0;
+ }
+
+ std::vector<uint32_t> initial_binary(size / sizeof(uint32_t));
+ memcpy(initial_binary.data(), data, size);
+
+ spvtools::ValidatorOptions validator_options;
+
+ spvtools::MessageConsumer message_consumer =
+ [](spv_message_level_t, const char*, const spv_position_t&, const char*) {
+ };
+
+ spvtools::fuzzers::RandomGenerator random_gen(data, size);
+ auto target_env = random_gen.GetTargetEnv();
+ std::unique_ptr<spvtools::opt::IRContext> ir_context;
+ if (!spvtools::fuzz::fuzzerutil::BuildIRContext(
+ target_env, message_consumer, initial_binary, validator_options,
+ &ir_context)) {
+ // The input is invalid - give up.
+ return 0;
+ }
+
+ std::vector<spvtools::fuzz::fuzzerutil::ModuleSupplier> donor_suppliers = {
+ [&initial_binary, message_consumer, target_env,
+ &validator_options]() -> std::unique_ptr<spvtools::opt::IRContext> {
+ std::unique_ptr<spvtools::opt::IRContext> result;
+ if (!spvtools::fuzz::fuzzerutil::BuildIRContext(
+ target_env, message_consumer, initial_binary, validator_options,
+ &result)) {
+ // The input was successfully parsed and validated first time around,
+ // so something is wrong if it is now invalid.
+ abort();
+ }
+ return result;
+ }};
+
+ uint32_t seed = random_gen.GetUInt32(std::numeric_limits<uint32_t>::max());
+ auto fuzzer_context = spvtools::MakeUnique<spvtools::fuzz::FuzzerContext>(
+ spvtools::MakeUnique<spvtools::fuzz::PseudoRandomGenerator>(seed),
+ spvtools::fuzz::FuzzerContext::GetMinFreshId(ir_context.get()), false);
+
+ auto transformation_context =
+ spvtools::MakeUnique<spvtools::fuzz::TransformationContext>(
+ spvtools::MakeUnique<spvtools::fuzz::FactManager>(ir_context.get()),
+ validator_options);
+
+ spvtools::fuzz::Fuzzer fuzzer(
+ std::move(ir_context), std::move(transformation_context),
+ std::move(fuzzer_context), message_consumer, donor_suppliers, false,
+ spvtools::fuzz::RepeatedPassStrategy::kLoopedWithRecommendations, true,
+ validator_options);
+ fuzzer.Run(0);
+ return 0;
+}
diff --git a/test/fuzzers/spvtools_opt_fuzzer_common.cpp b/test/fuzzers/spvtools_opt_fuzzer_common.cpp
new file mode 100644
index 00000000..49785090
--- /dev/null
+++ b/test/fuzzers/spvtools_opt_fuzzer_common.cpp
@@ -0,0 +1,84 @@
+// Copyright (c) 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "test/fuzzers/spvtools_opt_fuzzer_common.h"
+
+#include "source/opt/build_module.h"
+#include "test/fuzzers/random_generator.h"
+
+namespace spvtools {
+namespace fuzzers {
+
+int OptFuzzerTestOneInput(
+ const uint8_t* data, size_t size,
+ const std::function<void(spvtools::Optimizer&)>& register_passes) {
+ if (size < 1) {
+ return 0;
+ }
+
+ spvtools::fuzzers::RandomGenerator random_gen(data, size);
+ auto target_env = random_gen.GetTargetEnv();
+ spvtools::Optimizer optimizer(target_env);
+ optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
+ const spv_position_t&, const char*) {});
+
+ std::vector<uint32_t> input;
+ input.resize(size >> 2);
+
+ size_t count = 0;
+ for (size_t i = 0; (i + 3) < size; i += 4) {
+ input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
+ (data[i + 3]) << 24;
+ }
+
+ // The largest possible id bound is used when running the optimizer, to avoid
+ // the problem of id overflows.
+ const size_t kFinalIdLimit = UINT32_MAX;
+
+ // The input is scanned to check that it does not already use an id too close
+ // to this limit. This still gives the optimizer a large set of ids to
+ // consume. It is thus very unlikely that id overflow will occur during
+ // fuzzing. If it does, then the initial id limit should be decreased.
+ const size_t kInitialIdLimit = kFinalIdLimit - 1000000U;
+
+ // Build the module and scan it to check that all used ids are below the
+ // initial limit.
+ auto ir_context =
+ spvtools::BuildModule(target_env, nullptr, input.data(), input.size());
+ if (ir_context == nullptr) {
+ // It was not possible to build a valid module; that's OK - skip this input.
+ return 0;
+ }
+ if (ir_context->module()->id_bound() >= kInitialIdLimit) {
+ // The input already has a very large id bound. The input is thus abandoned,
+ // to avoid the possibility of ending up hitting the id bound limit.
+ return 0;
+ }
+
+ // Set the optimizer and its validator up with the largest possible id bound
+ // limit.
+ spvtools::ValidatorOptions validator_options;
+ spvtools::OptimizerOptions optimizer_options;
+ optimizer_options.set_max_id_bound(kFinalIdLimit);
+ validator_options.SetUniversalLimit(spv_validator_limit_max_id_bound,
+ kFinalIdLimit);
+ optimizer_options.set_validator_options(validator_options);
+ register_passes(optimizer);
+ optimizer.Run(input.data(), input.size(), &input, optimizer_options);
+
+ return 0;
+}
+
+} // namespace fuzzers
+} // namespace spvtools
diff --git a/test/fuzzers/spvtools_opt_fuzzer_common.h b/test/fuzzers/spvtools_opt_fuzzer_common.h
new file mode 100644
index 00000000..b8d4281c
--- /dev/null
+++ b/test/fuzzers/spvtools_opt_fuzzer_common.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef TEST_FUZZERS_SPVTOOLS_OPT_FUZZER_COMMON_H_
+#define TEST_FUZZERS_SPVTOOLS_OPT_FUZZER_COMMON_H_
+
+#include <cinttypes>
+#include <cstddef>
+#include <functional>
+
+#include "spirv-tools/optimizer.hpp"
+
+namespace spvtools {
+namespace fuzzers {
+
+// Helper function capturing the common logic for the various optimizer fuzzers.
+int OptFuzzerTestOneInput(
+ const uint8_t* data, size_t size,
+ const std::function<void(spvtools::Optimizer&)>& register_passes);
+
+} // namespace fuzzers
+} // namespace spvtools
+
+#endif // TEST_FUZZERS_SPVTOOLS_OPT_FUZZER_COMMON_H_
diff --git a/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp b/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp
index b45a98c3..fac4d23c 100644
--- a/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp
+++ b/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp
@@ -12,27 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <cstdint>
-#include <vector>
+#include <cinttypes>
+#include <cstddef>
+#include <functional>
#include "spirv-tools/optimizer.hpp"
+#include "test/fuzzers/spvtools_opt_fuzzer_common.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3);
- optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
- const spv_position_t&, const char*) {});
-
- std::vector<uint32_t> input;
- input.resize(size >> 2);
-
- size_t count = 0;
- for (size_t i = 0; (i + 3) < size; i += 4) {
- input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
- (data[i + 3]) << 24;
- }
-
- optimizer.RegisterLegalizationPasses();
- optimizer.Run(input.data(), input.size(), &input);
-
- return 0;
+ return spvtools::fuzzers::OptFuzzerTestOneInput(
+ data, size, [](spvtools::Optimizer& optimizer) -> void {
+ optimizer.RegisterLegalizationPasses();
+ });
}
diff --git a/test/fuzzers/spvtools_opt_performance_fuzzer.cpp b/test/fuzzers/spvtools_opt_performance_fuzzer.cpp
index 6c3bd6ab..e6038b90 100644
--- a/test/fuzzers/spvtools_opt_performance_fuzzer.cpp
+++ b/test/fuzzers/spvtools_opt_performance_fuzzer.cpp
@@ -12,27 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <cstdint>
-#include <vector>
+#include <cinttypes>
+#include <cstddef>
+#include <functional>
#include "spirv-tools/optimizer.hpp"
+#include "test/fuzzers/spvtools_opt_fuzzer_common.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3);
- optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
- const spv_position_t&, const char*) {});
-
- std::vector<uint32_t> input;
- input.resize(size >> 2);
-
- size_t count = 0;
- for (size_t i = 0; (i + 3) < size; i += 4) {
- input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
- (data[i + 3]) << 24;
- }
-
- optimizer.RegisterPerformancePasses();
- optimizer.Run(input.data(), input.size(), &input);
-
- return 0;
+ return spvtools::fuzzers::OptFuzzerTestOneInput(
+ data, size, [](spvtools::Optimizer& optimizer) -> void {
+ optimizer.RegisterPerformancePasses();
+ });
}
diff --git a/test/fuzzers/spvtools_opt_size_fuzzer.cpp b/test/fuzzers/spvtools_opt_size_fuzzer.cpp
index 68c79747..65492b1a 100644
--- a/test/fuzzers/spvtools_opt_size_fuzzer.cpp
+++ b/test/fuzzers/spvtools_opt_size_fuzzer.cpp
@@ -12,27 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <cstdint>
-#include <vector>
+#include <cinttypes>
+#include <cstddef>
+#include <functional>
#include "spirv-tools/optimizer.hpp"
+#include "test/fuzzers/spvtools_opt_fuzzer_common.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3);
- optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
- const spv_position_t&, const char*) {});
-
- std::vector<uint32_t> input;
- input.resize(size >> 2);
-
- size_t count = 0;
- for (size_t i = 0; (i + 3) < size; i += 4) {
- input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
- (data[i + 3]) << 24;
- }
-
- optimizer.RegisterSizePasses();
- optimizer.Run(input.data(), input.size(), &input);
-
- return 0;
+ return spvtools::fuzzers::OptFuzzerTestOneInput(
+ data, size, [](spvtools::Optimizer& optimizer) -> void {
+ optimizer.RegisterSizePasses();
+ });
}
diff --git a/test/fuzzers/spvtools_val_fuzzer.cpp b/test/fuzzers/spvtools_val_fuzzer.cpp
index 5dc4303b..fd6396cd 100644
--- a/test/fuzzers/spvtools_val_fuzzer.cpp
+++ b/test/fuzzers/spvtools_val_fuzzer.cpp
@@ -16,9 +16,15 @@
#include <vector>
#include "spirv-tools/libspirv.hpp"
+#include "test/fuzzers/random_generator.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_3);
+ if (size < 1) {
+ return 0;
+ }
+
+ spvtools::fuzzers::RandomGenerator random_gen(data, size);
+ spvtools::SpirvTools tools(random_gen.GetTargetEnv());
tools.SetMessageConsumer([](spv_message_level_t, const char*,
const spv_position_t&, const char*) {});
diff --git a/test/hex_float_test.cpp b/test/hex_float_test.cpp
index c422f756..7edfd43d 100644
--- a/test/hex_float_test.cpp
+++ b/test/hex_float_test.cpp
@@ -1325,6 +1325,76 @@ TEST(FloatProxy, Lowest) {
Eq(std::numeric_limits<double>::lowest()));
}
+template <typename T>
+struct StreamParseCase {
+ StreamParseCase(const std::string& lit, bool succ, const std::string& suffix,
+ T value)
+ : literal(lit),
+ expect_success(succ),
+ expected_suffix(suffix),
+ expected_value(HexFloat<FloatProxy<T>>(value)) {}
+
+ std::string literal;
+ bool expect_success;
+ std::string expected_suffix;
+ HexFloat<FloatProxy<T>> expected_value;
+};
+
+template <typename T>
+std::ostream& operator<<(std::ostream& os, const StreamParseCase<T>& fspc) {
+ os << "StreamParseCase(" << fspc.literal
+ << ", expect_success:" << int(fspc.expect_success) << ","
+ << fspc.expected_suffix << "," << fspc.expected_value << ")";
+ return os;
+}
+
+using FloatStreamParseTest = ::testing::TestWithParam<StreamParseCase<float>>;
+
+TEST_P(FloatStreamParseTest, Samples) {
+ std::stringstream input(GetParam().literal);
+ HexFloat<FloatProxy<float>> parsed_value(0.0f);
+ // Hex floats must be read with the stream input operator.
+ input >> parsed_value;
+ if (GetParam().expect_success) {
+ EXPECT_FALSE(input.fail());
+ std::string suffix;
+ input >> suffix;
+ // EXPECT_EQ(suffix, GetParam().expected_suffix);
+ EXPECT_EQ(parsed_value.value().getAsFloat(),
+ GetParam().expected_value.value().getAsFloat());
+ } else {
+ EXPECT_TRUE(input.fail());
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ HexFloatExponentMissingDigits, FloatStreamParseTest,
+ ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
+ {"0x1.0p1", true, "", 2.0f},
+ {"0x1.0p1a", true, "a", 2.0f},
+ {"-0x1.0p1f", true, "f", -2.0f},
+ {"0x1.0p", false, "", 0.0f},
+ {"0x1.0pa", false, "", 0.0f},
+ {"0x1.0p!", false, "", 0.0f},
+ {"0x1.0p+", false, "", 0.0f},
+ {"0x1.0p+a", false, "", 0.0f},
+ {"0x1.0p+!", false, "", 0.0f},
+ {"0x1.0p-", false, "", 0.0f},
+ {"0x1.0p-a", false, "", 0.0f},
+ {"0x1.0p-!", false, "", 0.0f},
+ {"0x1.0p++", false, "", 0.0f},
+ {"0x1.0p+-", false, "", 0.0f},
+ {"0x1.0p-+", false, "", 0.0f},
+ {"0x1.0p--", false, "", 0.0f}}));
+
+INSTANTIATE_TEST_SUITE_P(
+ HexFloatExponentTrailingSign, FloatStreamParseTest,
+ ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
+ // Don't consume a sign after the binary exponent digits.
+ {"0x1.0p1", true, "", 2.0f},
+ {"0x1.0p1+", true, "+", 2.0f},
+ {"0x1.0p1-", true, "-", 2.0f}}));
+
// TODO(awoloszyn): Add fp16 tests and HexFloatTraits.
} // namespace
} // namespace utils
diff --git a/test/link/binary_version_test.cpp b/test/link/binary_version_test.cpp
index 80aab0fd..a56030f4 100644
--- a/test/link/binary_version_test.cpp
+++ b/test/link/binary_version_test.cpp
@@ -20,40 +20,57 @@
namespace spvtools {
namespace {
+using ::testing::HasSubstr;
using BinaryVersion = spvtest::LinkerTest;
-TEST_F(BinaryVersion, LinkerChoosesMaxSpirvVersion) {
+spvtest::Binary CreateBinary(uint32_t version) {
+ return {
+ // clang-format off
+ // Header
+ SpvMagicNumber,
+ version,
+ SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0),
+ 1u, // NOTE: Bound
+ 0u, // NOTE: Schema; reserved
+
+ // OpCapability Shader
+ SpvOpCapability | 2u << SpvWordCountShift,
+ SpvCapabilityShader,
+
+ // OpMemoryModel Logical Simple
+ SpvOpMemoryModel | 3u << SpvWordCountShift,
+ SpvAddressingModelLogical,
+ SpvMemoryModelSimple
+ // clang-format on
+ };
+}
+
+TEST_F(BinaryVersion, Match) {
// clang-format off
spvtest::Binaries binaries = {
- {
- SpvMagicNumber,
- 0x00010300u,
- SPV_GENERATOR_CODEPLAY,
- 1u, // NOTE: Bound
- 0u // NOTE: Schema; reserved
- },
- {
- SpvMagicNumber,
- 0x00010500u,
- SPV_GENERATOR_CODEPLAY,
- 1u, // NOTE: Bound
- 0u // NOTE: Schema; reserved
- },
- {
- SpvMagicNumber,
- 0x00010100u,
- SPV_GENERATOR_CODEPLAY,
- 1u, // NOTE: Bound
- 0u // NOTE: Schema; reserved
- }
+ CreateBinary(SPV_SPIRV_VERSION_WORD(1, 3)),
+ CreateBinary(SPV_SPIRV_VERSION_WORD(1, 3)),
};
// clang-format on
spvtest::Binary linked_binary;
-
- ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary));
+ ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage();
EXPECT_THAT(GetErrorMessage(), std::string());
+ EXPECT_EQ(SPV_SPIRV_VERSION_WORD(1, 3), linked_binary[1]);
+}
- EXPECT_EQ(0x00010500u, linked_binary[1]);
+TEST_F(BinaryVersion, Mismatch) {
+ // clang-format off
+ spvtest::Binaries binaries = {
+ CreateBinary(SPV_SPIRV_VERSION_WORD(1, 3)),
+ CreateBinary(SPV_SPIRV_VERSION_WORD(1, 5)),
+ };
+ // clang-format on
+ spvtest::Binary linked_binary;
+ ASSERT_EQ(SPV_ERROR_INTERNAL, Link(binaries, &linked_binary))
+ << GetErrorMessage();
+ EXPECT_THAT(GetErrorMessage(),
+ HasSubstr("Conflicting SPIR-V versions: 1.3 (input modules 1 "
+ "through 1) vs 1.5 (input module 2)."));
}
} // namespace
diff --git a/test/link/entry_points_test.cpp b/test/link/entry_points_test.cpp
index bac8e02e..df7ea20c 100644
--- a/test/link/entry_points_test.cpp
+++ b/test/link/entry_points_test.cpp
@@ -26,6 +26,8 @@ class EntryPoints : public spvtest::LinkerTest {};
TEST_F(EntryPoints, SameModelDifferentName) {
const std::string body1 = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %3 "foo"
%1 = OpTypeVoid
%2 = OpTypeFunction %1
@@ -33,6 +35,8 @@ OpEntryPoint GLCompute %3 "foo"
OpFunctionEnd
)";
const std::string body2 = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %3 "bar"
%1 = OpTypeVoid
%2 = OpTypeFunction %1
@@ -41,12 +45,15 @@ OpFunctionEnd
)";
spvtest::Binary linked_binary;
- EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary));
+ ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
+ << GetErrorMessage();
EXPECT_THAT(GetErrorMessage(), std::string());
}
TEST_F(EntryPoints, DifferentModelSameName) {
const std::string body1 = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %3 "foo"
%1 = OpTypeVoid
%2 = OpTypeFunction %1
@@ -54,6 +61,8 @@ OpEntryPoint GLCompute %3 "foo"
OpFunctionEnd
)";
const std::string body2 = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %3 "foo"
%1 = OpTypeVoid
%2 = OpTypeFunction %1
@@ -62,12 +71,15 @@ OpFunctionEnd
)";
spvtest::Binary linked_binary;
- EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary));
+ ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
+ << GetErrorMessage();
EXPECT_THAT(GetErrorMessage(), std::string());
}
TEST_F(EntryPoints, SameModelAndName) {
const std::string body1 = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %3 "foo"
%1 = OpTypeVoid
%2 = OpTypeFunction %1
@@ -75,6 +87,8 @@ OpEntryPoint GLCompute %3 "foo"
OpFunctionEnd
)";
const std::string body2 = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %3 "foo"
%1 = OpTypeVoid
%2 = OpTypeFunction %1
diff --git a/test/link/global_values_amount_test.cpp b/test/link/global_values_amount_test.cpp
index 2c4ee1f0..3158b7ec 100644
--- a/test/link/global_values_amount_test.cpp
+++ b/test/link/global_values_amount_test.cpp
@@ -22,85 +22,56 @@ namespace {
using ::testing::HasSubstr;
+const uint32_t binary_count = 2u;
+
class EntryPointsAmountTest : public spvtest::LinkerTest {
public:
- EntryPointsAmountTest() { binaries.reserve(0xFFFF); }
+ EntryPointsAmountTest() { binaries.reserve(binary_count + 1u); }
void SetUp() override {
- binaries.push_back({SpvMagicNumber,
- SpvVersion,
- SPV_GENERATOR_CODEPLAY,
- 10u, // NOTE: Bound
- 0u, // NOTE: Schema; reserved
-
- 3u << SpvWordCountShift | SpvOpTypeFloat,
- 1u, // NOTE: Result ID
- 32u, // NOTE: Width
-
- 4u << SpvWordCountShift | SpvOpTypePointer,
- 2u, // NOTE: Result ID
- SpvStorageClassInput,
- 1u, // NOTE: Type ID
-
- 2u << SpvWordCountShift | SpvOpTypeVoid,
- 3u, // NOTE: Result ID
-
- 3u << SpvWordCountShift | SpvOpTypeFunction,
- 4u, // NOTE: Result ID
- 3u, // NOTE: Return type
-
- 5u << SpvWordCountShift | SpvOpFunction,
- 3u, // NOTE: Result type
- 5u, // NOTE: Result ID
- SpvFunctionControlMaskNone,
- 4u, // NOTE: Function type
-
- 2u << SpvWordCountShift | SpvOpLabel,
- 6u, // NOTE: Result ID
-
- 4u << SpvWordCountShift | SpvOpVariable,
- 2u, // NOTE: Type ID
- 7u, // NOTE: Result ID
- SpvStorageClassFunction,
-
- 4u << SpvWordCountShift | SpvOpVariable,
- 2u, // NOTE: Type ID
- 8u, // NOTE: Result ID
- SpvStorageClassFunction,
-
- 4u << SpvWordCountShift | SpvOpVariable,
- 2u, // NOTE: Type ID
- 9u, // NOTE: Result ID
- SpvStorageClassFunction,
-
- 1u << SpvWordCountShift | SpvOpReturn,
-
- 1u << SpvWordCountShift | SpvOpFunctionEnd});
- for (size_t i = 0u; i < 2u; ++i) {
- spvtest::Binary binary = {
- SpvMagicNumber,
- SpvVersion,
- SPV_GENERATOR_CODEPLAY,
- 103u, // NOTE: Bound
- 0u, // NOTE: Schema; reserved
-
- 3u << SpvWordCountShift | SpvOpTypeFloat,
- 1u, // NOTE: Result ID
- 32u, // NOTE: Width
-
- 4u << SpvWordCountShift | SpvOpTypePointer,
- 2u, // NOTE: Result ID
- SpvStorageClassInput,
- 1u // NOTE: Type ID
- };
-
- for (uint32_t j = 0u; j < 0xFFFFu / 2u; ++j) {
- binary.push_back(4u << SpvWordCountShift | SpvOpVariable);
- binary.push_back(2u); // NOTE: Type ID
- binary.push_back(j + 3u); // NOTE: Result ID
- binary.push_back(SpvStorageClassInput);
- }
- binaries.push_back(binary);
+ const uint32_t global_variable_count_per_binary =
+ (SPV_LIMIT_GLOBAL_VARIABLES_MAX - 1u) / binary_count;
+
+ spvtest::Binary common_binary = {
+ // clang-format off
+ SpvMagicNumber,
+ SpvVersion,
+ SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0),
+ 3u + global_variable_count_per_binary, // NOTE: Bound
+ 0u, // NOTE: Schema; reserved
+
+ SpvOpCapability | 2u << SpvWordCountShift,
+ SpvCapabilityShader,
+
+ SpvOpMemoryModel | 3u << SpvWordCountShift,
+ SpvAddressingModelLogical,
+ SpvMemoryModelSimple,
+
+ SpvOpTypeFloat | 3u << SpvWordCountShift,
+ 1u, // NOTE: Result ID
+ 32u, // NOTE: Width
+
+ SpvOpTypePointer | 4u << SpvWordCountShift,
+ 2u, // NOTE: Result ID
+ SpvStorageClassInput,
+ 1u // NOTE: Type ID
+ // clang-format on
+ };
+
+ binaries.push_back({});
+ spvtest::Binary& binary = binaries.back();
+ binary.reserve(common_binary.size() + global_variable_count_per_binary * 4);
+ binary.insert(binary.end(), common_binary.cbegin(), common_binary.cend());
+
+ for (uint32_t i = 0u; i < global_variable_count_per_binary; ++i) {
+ binary.push_back(SpvOpVariable | 4u << SpvWordCountShift);
+ binary.push_back(2u); // NOTE: Type ID
+ binary.push_back(3u + i); // NOTE: Result ID
+ binary.push_back(SpvStorageClassInput);
+ }
+
+ for (uint32_t i = 0u; i < binary_count - 1u; ++i) {
+ binaries.push_back(binaries.back());
}
}
void TearDown() override { binaries.clear(); }
@@ -111,42 +82,53 @@ class EntryPointsAmountTest : public spvtest::LinkerTest {
TEST_F(EntryPointsAmountTest, UnderLimit) {
spvtest::Binary linked_binary;
- EXPECT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary));
+ ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage();
EXPECT_THAT(GetErrorMessage(), std::string());
}
TEST_F(EntryPointsAmountTest, OverLimit) {
- binaries.push_back({SpvMagicNumber,
- SpvVersion,
- SPV_GENERATOR_CODEPLAY,
- 5u, // NOTE: Bound
- 0u, // NOTE: Schema; reserved
-
- 3u << SpvWordCountShift | SpvOpTypeFloat,
- 1u, // NOTE: Result ID
- 32u, // NOTE: Width
-
- 4u << SpvWordCountShift | SpvOpTypePointer,
- 2u, // NOTE: Result ID
- SpvStorageClassInput,
- 1u, // NOTE: Type ID
-
- 4u << SpvWordCountShift | SpvOpVariable,
- 2u, // NOTE: Type ID
- 3u, // NOTE: Result ID
- SpvStorageClassInput,
-
- 4u << SpvWordCountShift | SpvOpVariable,
- 2u, // NOTE: Type ID
- 4u, // NOTE: Result ID
- SpvStorageClassInput});
+ binaries.push_back({
+ // clang-format off
+ SpvMagicNumber,
+ SpvVersion,
+ SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0),
+ 5u, // NOTE: Bound
+ 0u, // NOTE: Schema; reserved
+
+ SpvOpCapability | 2u << SpvWordCountShift,
+ SpvCapabilityShader,
+
+ SpvOpMemoryModel | 3u << SpvWordCountShift,
+ SpvAddressingModelLogical,
+ SpvMemoryModelSimple,
+
+ SpvOpTypeFloat | 3u << SpvWordCountShift,
+ 1u, // NOTE: Result ID
+ 32u, // NOTE: Width
+
+ SpvOpTypePointer | 4u << SpvWordCountShift,
+ 2u, // NOTE: Result ID
+ SpvStorageClassInput,
+ 1u, // NOTE: Type ID
+
+ SpvOpVariable | 4u << SpvWordCountShift,
+ 2u, // NOTE: Type ID
+ 3u, // NOTE: Result ID
+ SpvStorageClassInput,
+
+ SpvOpVariable | 4u << SpvWordCountShift,
+ 2u, // NOTE: Type ID
+ 4u, // NOTE: Result ID
+ SpvStorageClassInput
+ // clang-format on
+ });
spvtest::Binary linked_binary;
-
- EXPECT_EQ(SPV_ERROR_INTERNAL, Link(binaries, &linked_binary));
- EXPECT_THAT(GetErrorMessage(),
- HasSubstr("The limit of global values, 65535, was exceeded; "
- "65536 global values were found."));
+ ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage();
+ EXPECT_THAT(
+ GetErrorMessage(),
+ HasSubstr("The minimum limit of global values, 65535, was exceeded; "
+ "65536 global values were found."));
}
} // namespace
diff --git a/test/link/ids_limit_test.cpp b/test/link/ids_limit_test.cpp
index 6d7815a2..846fbef8 100644
--- a/test/link/ids_limit_test.cpp
+++ b/test/link/ids_limit_test.cpp
@@ -21,51 +21,114 @@ namespace spvtools {
namespace {
using ::testing::HasSubstr;
-using IdsLimit = spvtest::LinkerTest;
-
-TEST_F(IdsLimit, UnderLimit) {
- spvtest::Binaries binaries = {
- {
- SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY,
- 0x2FFFFFu, // NOTE: Bound
- 0u, // NOTE: Schema; reserved
- },
- {
- SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY,
- 0x100000u, // NOTE: Bound
- 0u, // NOTE: Schema; reserved
- }};
- spvtest::Binary linked_binary;
- ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary));
+class IdsLimit : public spvtest::LinkerTest {
+ public:
+ IdsLimit() { binaries.reserve(2u); }
+
+ void SetUp() override {
+ const uint32_t id_bound = SPV_LIMIT_RESULT_ID_BOUND - 1u;
+ const uint32_t constant_count =
+ id_bound -
+ 2u; // One ID is used for TypeBool, and (constant_count + 1) < id_bound
+
+ // This is needed, as otherwise the ID bound will get reset to 1 while
+ // running the RemoveDuplicates pass.
+ spvtest::Binary common_binary = {
+ // clang-format off
+ SpvMagicNumber,
+ SpvVersion,
+ SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0),
+ id_bound, // NOTE: Bound
+ 0u, // NOTE: Schema; reserved
+
+ SpvOpCapability | 2u << SpvWordCountShift,
+ SpvCapabilityShader,
+
+ SpvOpMemoryModel | 3u << SpvWordCountShift,
+ SpvAddressingModelLogical,
+ SpvMemoryModelSimple,
+
+ SpvOpTypeBool | 2u << SpvWordCountShift,
+ 1u // NOTE: Result ID
+ // clang-format on
+ };
+
+ binaries.push_back({});
+ spvtest::Binary& binary = binaries.back();
+ binary.reserve(common_binary.size() + constant_count * 3u);
+ binary.insert(binary.end(), common_binary.cbegin(), common_binary.cend());
+
+ for (uint32_t i = 0u; i < constant_count; ++i) {
+ binary.push_back(SpvOpConstantTrue | 3u << SpvWordCountShift);
+ binary.push_back(1u); // NOTE: Type ID
+ binary.push_back(2u + i); // NOTE: Result ID
+ }
+ }
+ void TearDown() override { binaries.clear(); }
+
+ spvtest::Binaries binaries;
+};
+
+spvtest::Binary CreateBinary(uint32_t id_bound) {
+ return {
+ // clang-format off
+ // Header
+ SpvMagicNumber,
+ SpvVersion,
+ SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0),
+ id_bound, // NOTE: Bound
+ 0u, // NOTE: Schema; reserved
+
+ // OpCapability Shader
+ SpvOpCapability | 2u << SpvWordCountShift,
+ SpvCapabilityShader,
+
+ // OpMemoryModel Logical Simple
+ SpvOpMemoryModel | 3u << SpvWordCountShift,
+ SpvAddressingModelLogical,
+ SpvMemoryModelSimple
+ // clang-format on
+ };
+}
+
+TEST_F(IdsLimit, DISABLED_UnderLimit) {
+ spvtest::Binary linked_binary;
+ ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage();
EXPECT_THAT(GetErrorMessage(), std::string());
- EXPECT_EQ(0x3FFFFEu, linked_binary[3]);
+ EXPECT_EQ(0x3FFFFFu, linked_binary[3]);
+}
+
+TEST_F(IdsLimit, DISABLED_OverLimit) {
+ spvtest::Binary& binary = binaries.back();
+
+ const uint32_t id_bound = binary[3];
+ binary[3] = id_bound + 1u;
+
+ binary.push_back(SpvOpConstantFalse | 3u << SpvWordCountShift);
+ binary.push_back(1u); // NOTE: Type ID
+ binary.push_back(id_bound); // NOTE: Result ID
+
+ spvtest::Binary linked_binary;
+ ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage();
+ EXPECT_THAT(
+ GetErrorMessage(),
+ HasSubstr("The minimum limit of IDs, 4194303, was exceeded: 4194304 is "
+ "the current ID bound."));
+ EXPECT_EQ(0x400000u, linked_binary[3]);
}
-TEST_F(IdsLimit, OverLimit) {
- spvtest::Binaries binaries = {
- {
- SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY,
- 0x2FFFFFu, // NOTE: Bound
- 0u, // NOTE: Schema; reserved
- },
- {
- SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY,
- 0x100000u, // NOTE: Bound
- 0u, // NOTE: Schema; reserved
- },
- {
- SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY,
- 3u, // NOTE: Bound
- 0u, // NOTE: Schema; reserved
- }};
+TEST_F(IdsLimit, DISABLED_Overflow) {
+ spvtest::Binaries binaries = {CreateBinary(0xFFFFFFFFu),
+ CreateBinary(0x00000002u)};
spvtest::Binary linked_binary;
- EXPECT_EQ(SPV_ERROR_INVALID_ID, Link(binaries, &linked_binary));
- EXPECT_THAT(GetErrorMessage(),
- HasSubstr("The limit of IDs, 4194303, was exceeded: 4194304 is "
- "the current ID bound."));
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, Link(binaries, &linked_binary));
+ EXPECT_THAT(
+ GetErrorMessage(),
+ HasSubstr("Too many IDs (4294967296): combining all modules would "
+ "overflow the 32-bit word of the SPIR-V header."));
}
} // namespace
diff --git a/test/link/matching_imports_to_exports_test.cpp b/test/link/matching_imports_to_exports_test.cpp
index e76c69fb..6b02fc46 100644
--- a/test/link/matching_imports_to_exports_test.cpp
+++ b/test/link/matching_imports_to_exports_test.cpp
@@ -26,6 +26,9 @@ using MatchingImportsToExports = spvtest::LinkerTest;
TEST_F(MatchingImportsToExports, Default) {
const std::string body1 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Import
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
@@ -33,6 +36,9 @@ OpDecorate %1 LinkageAttributes "foo" Import
)";
const std::string body2 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%3 = OpConstant %2 42
@@ -40,11 +46,14 @@ OpDecorate %1 LinkageAttributes "foo" Export
)";
spvtest::Binary linked_binary;
- EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
+ ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
<< GetErrorMessage();
const std::string expected_res =
- R"(OpModuleProcessed "Linked by SPIR-V Tools Linker"
+ R"(OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
+OpModuleProcessed "Linked by SPIR-V Tools Linker"
%1 = OpTypeFloat 32
%2 = OpVariable %1 Input
%3 = OpConstant %1 42
@@ -52,7 +61,7 @@ OpDecorate %1 LinkageAttributes "foo" Export
)";
std::string res_body;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
- EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
+ ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
<< GetErrorMessage();
EXPECT_EQ(expected_res, res_body);
}
@@ -60,23 +69,29 @@ OpDecorate %1 LinkageAttributes "foo" Export
TEST_F(MatchingImportsToExports, NotALibraryExtraExports) {
const std::string body = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
)";
spvtest::Binary linked_binary;
- EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary))
+ ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary))
<< GetErrorMessage();
const std::string expected_res =
- R"(OpModuleProcessed "Linked by SPIR-V Tools Linker"
+ R"(OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
+OpModuleProcessed "Linked by SPIR-V Tools Linker"
%1 = OpTypeFloat 32
%2 = OpVariable %1 Uniform
)";
std::string res_body;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
- EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
+ ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
<< GetErrorMessage();
EXPECT_EQ(expected_res, res_body);
}
@@ -84,6 +99,9 @@ OpDecorate %1 LinkageAttributes "foo" Export
TEST_F(MatchingImportsToExports, LibraryExtraExports) {
const std::string body = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
@@ -92,10 +110,13 @@ OpDecorate %1 LinkageAttributes "foo" Export
spvtest::Binary linked_binary;
LinkerOptions options;
options.SetCreateLibrary(true);
- EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary, options))
+ ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary, options))
<< GetErrorMessage();
const std::string expected_res = R"(OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpModuleProcessed "Linked by SPIR-V Tools Linker"
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
@@ -103,7 +124,7 @@ OpDecorate %1 LinkageAttributes "foo" Export
)";
std::string res_body;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
- EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
+ ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
<< GetErrorMessage();
EXPECT_EQ(expected_res, res_body);
}
@@ -111,11 +132,18 @@ OpDecorate %1 LinkageAttributes "foo" Export
TEST_F(MatchingImportsToExports, UnresolvedImports) {
const std::string body1 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Import
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
)";
- const std::string body2 = R"()";
+ const std::string body2 = R"(
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
+)";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
@@ -127,6 +155,9 @@ OpDecorate %1 LinkageAttributes "foo" Import
TEST_F(MatchingImportsToExports, TypeMismatch) {
const std::string body1 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Import
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
@@ -134,6 +165,9 @@ OpDecorate %1 LinkageAttributes "foo" Import
)";
const std::string body2 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeInt 32 0
%3 = OpConstant %2 42
@@ -153,6 +187,9 @@ OpDecorate %1 LinkageAttributes "foo" Export
TEST_F(MatchingImportsToExports, MultipleDefinitions) {
const std::string body1 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Import
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
@@ -160,6 +197,9 @@ OpDecorate %1 LinkageAttributes "foo" Import
)";
const std::string body2 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%3 = OpConstant %2 42
@@ -167,6 +207,9 @@ OpDecorate %1 LinkageAttributes "foo" Export
)";
const std::string body3 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%3 = OpConstant %2 -1
@@ -185,6 +228,9 @@ OpDecorate %1 LinkageAttributes "foo" Export
TEST_F(MatchingImportsToExports, SameNameDifferentTypes) {
const std::string body1 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Import
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
@@ -192,6 +238,9 @@ OpDecorate %1 LinkageAttributes "foo" Import
)";
const std::string body2 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeInt 32 0
%3 = OpConstant %2 42
@@ -199,6 +248,9 @@ OpDecorate %1 LinkageAttributes "foo" Export
)";
const std::string body3 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%3 = OpConstant %2 12
@@ -217,6 +269,9 @@ OpDecorate %1 LinkageAttributes "foo" Export
TEST_F(MatchingImportsToExports, DecorationMismatch) {
const std::string body1 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Import
OpDecorate %2 Constant
%2 = OpTypeFloat 32
@@ -225,6 +280,9 @@ OpDecorate %2 Constant
)";
const std::string body2 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%3 = OpConstant %2 42
@@ -244,8 +302,10 @@ OpDecorate %1 LinkageAttributes "foo" Export
TEST_F(MatchingImportsToExports,
FuncParamAttrDifferButStillMatchExportToImport) {
const std::string body1 = R"(
-OpCapability Kernel
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Import
OpDecorate %2 FuncParamAttr Zext
%3 = OpTypeVoid
@@ -256,8 +316,10 @@ OpDecorate %2 FuncParamAttr Zext
OpFunctionEnd
)";
const std::string body2 = R"(
-OpCapability Kernel
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Export
OpDecorate %2 FuncParamAttr Sext
%3 = OpTypeVoid
@@ -271,10 +333,12 @@ OpFunctionEnd
)";
spvtest::Binary linked_binary;
- EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
+ ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
<< GetErrorMessage();
- const std::string expected_res = R"(OpCapability Kernel
+ const std::string expected_res = R"(OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpModuleProcessed "Linked by SPIR-V Tools Linker"
OpDecorate %1 FuncParamAttr Sext
%2 = OpTypeVoid
@@ -288,7 +352,7 @@ OpFunctionEnd
)";
std::string res_body;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
- EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
+ ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
<< GetErrorMessage();
EXPECT_EQ(expected_res, res_body);
}
@@ -296,6 +360,9 @@ OpFunctionEnd
TEST_F(MatchingImportsToExports, FunctionCtrl) {
const std::string body1 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Import
%2 = OpTypeVoid
%3 = OpTypeFunction %2
@@ -306,6 +373,9 @@ OpFunctionEnd
)";
const std::string body2 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeVoid
%3 = OpTypeFunction %2
@@ -316,11 +386,14 @@ OpFunctionEnd
)";
spvtest::Binary linked_binary;
- EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
+ ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
<< GetErrorMessage();
const std::string expected_res =
- R"(OpModuleProcessed "Linked by SPIR-V Tools Linker"
+ R"(OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
+OpModuleProcessed "Linked by SPIR-V Tools Linker"
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpTypeFloat 32
@@ -332,15 +405,17 @@ OpFunctionEnd
)";
std::string res_body;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
- EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
+ ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
<< GetErrorMessage();
EXPECT_EQ(expected_res, res_body);
}
TEST_F(MatchingImportsToExports, UseExportedFuncParamAttr) {
const std::string body1 = R"(
-OpCapability Kernel
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Import
OpDecorate %2 FuncParamAttr Zext
%2 = OpDecorationGroup
@@ -356,8 +431,10 @@ OpFunctionEnd
OpFunctionEnd
)";
const std::string body2 = R"(
-OpCapability Kernel
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Export
OpDecorate %2 FuncParamAttr Sext
%3 = OpTypeVoid
@@ -371,10 +448,12 @@ OpFunctionEnd
)";
spvtest::Binary linked_binary;
- EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
+ ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
<< GetErrorMessage();
- const std::string expected_res = R"(OpCapability Kernel
+ const std::string expected_res = R"(OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpModuleProcessed "Linked by SPIR-V Tools Linker"
OpDecorate %1 FuncParamAttr Zext
%1 = OpDecorationGroup
@@ -394,15 +473,17 @@ OpFunctionEnd
)";
std::string res_body;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
- EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
+ ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
<< GetErrorMessage();
EXPECT_EQ(expected_res, res_body);
}
TEST_F(MatchingImportsToExports, NamesAndDecorations) {
const std::string body1 = R"(
-OpCapability Kernel
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpName %1 "foo"
OpName %3 "param"
OpDecorate %1 LinkageAttributes "foo" Import
@@ -422,8 +503,10 @@ OpFunctionEnd
OpFunctionEnd
)";
const std::string body2 = R"(
-OpCapability Kernel
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpName %1 "foo"
OpName %2 "param"
OpDecorate %1 LinkageAttributes "foo" Export
@@ -440,10 +523,12 @@ OpFunctionEnd
)";
spvtest::Binary linked_binary;
- EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
+ ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
<< GetErrorMessage();
- const std::string expected_res = R"(OpCapability Kernel
+ const std::string expected_res = R"(OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpName %1 "foo"
OpName %2 "param"
OpModuleProcessed "Linked by SPIR-V Tools Linker"
@@ -467,7 +552,7 @@ OpFunctionEnd
)";
std::string res_body;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
- EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
+ ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
<< GetErrorMessage();
EXPECT_EQ(expected_res, res_body);
}
diff --git a/test/link/memory_model_test.cpp b/test/link/memory_model_test.cpp
index 2add5046..280a776a 100644
--- a/test/link/memory_model_test.cpp
+++ b/test/link/memory_model_test.cpp
@@ -50,9 +50,9 @@ OpMemoryModel Physical32 Simple
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_ERROR_INTERNAL,
AssembleAndLink({body1, body2}, &linked_binary));
- EXPECT_THAT(
- GetErrorMessage(),
- HasSubstr("Conflicting addressing models: Logical vs Physical32."));
+ EXPECT_THAT(GetErrorMessage(),
+ HasSubstr("Conflicting addressing models: Logical (input modules "
+ "1 through 1) vs Physical32 (input module 2)."));
}
TEST_F(MemoryModel, MemoryMismatch) {
@@ -67,7 +67,38 @@ OpMemoryModel Logical GLSL450
EXPECT_EQ(SPV_ERROR_INTERNAL,
AssembleAndLink({body1, body2}, &linked_binary));
EXPECT_THAT(GetErrorMessage(),
- HasSubstr("Conflicting memory models: Simple vs GLSL450."));
+ HasSubstr("Conflicting memory models: Simple (input modules 1 "
+ "through 1) vs GLSL450 (input module 2)."));
+}
+
+TEST_F(MemoryModel, FirstLackMemoryModel) {
+ const std::string body1 = R"(
+)";
+ const std::string body2 = R"(
+OpMemoryModel Logical GLSL450
+)";
+
+ spvtest::Binary linked_binary;
+ EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
+ AssembleAndLink({body1, body2}, &linked_binary));
+ EXPECT_THAT(
+ GetErrorMessage(),
+ HasSubstr("Input module 1 is lacking an OpMemoryModel instruction."));
+}
+
+TEST_F(MemoryModel, SecondLackMemoryModel) {
+ const std::string body1 = R"(
+OpMemoryModel Logical GLSL450
+)";
+ const std::string body2 = R"(
+)";
+
+ spvtest::Binary linked_binary;
+ EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
+ AssembleAndLink({body1, body2}, &linked_binary));
+ EXPECT_THAT(
+ GetErrorMessage(),
+ HasSubstr("Input module 2 is lacking an OpMemoryModel instruction."));
}
} // namespace
diff --git a/test/link/partial_linkage_test.cpp b/test/link/partial_linkage_test.cpp
index c43b06e5..bf4b508b 100644
--- a/test/link/partial_linkage_test.cpp
+++ b/test/link/partial_linkage_test.cpp
@@ -26,6 +26,9 @@ using PartialLinkage = spvtest::LinkerTest;
TEST_F(PartialLinkage, Allowed) {
const std::string body1 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Import
OpDecorate %2 LinkageAttributes "bar" Import
%3 = OpTypeFloat 32
@@ -34,6 +37,9 @@ OpDecorate %2 LinkageAttributes "bar" Import
)";
const std::string body2 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "bar" Export
%2 = OpTypeFloat 32
%3 = OpConstant %2 3.1415
@@ -44,9 +50,13 @@ OpDecorate %1 LinkageAttributes "bar" Export
LinkerOptions linker_options;
linker_options.SetAllowPartialLinkage(true);
ASSERT_EQ(SPV_SUCCESS,
- AssembleAndLink({body1, body2}, &linked_binary, linker_options));
+ AssembleAndLink({body1, body2}, &linked_binary, linker_options))
+ << GetErrorMessage();
const std::string expected_res = R"(OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpModuleProcessed "Linked by SPIR-V Tools Linker"
OpDecorate %1 LinkageAttributes "foo" Import
%2 = OpTypeFloat 32
@@ -64,6 +74,9 @@ OpDecorate %1 LinkageAttributes "foo" Import
TEST_F(PartialLinkage, Disallowed) {
const std::string body1 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "foo" Import
OpDecorate %2 LinkageAttributes "bar" Import
%3 = OpTypeFloat 32
@@ -72,6 +85,9 @@ OpDecorate %2 LinkageAttributes "bar" Import
)";
const std::string body2 = R"(
OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpMemoryModel Physical64 OpenCL
OpDecorate %1 LinkageAttributes "bar" Export
%2 = OpTypeFloat 32
%3 = OpConstant %2 3.1415
diff --git a/test/link/type_match_test.cpp b/test/link/type_match_test.cpp
index dae70c16..efc5cf7e 100644
--- a/test/link/type_match_test.cpp
+++ b/test/link/type_match_test.cpp
@@ -66,6 +66,7 @@ using TypeMatch = spvtest::LinkerTest;
"OpCapability Kernel\n" \
"OpCapability Shader\n" \
"OpCapability Addresses\n" \
+ "OpMemoryModel Physical64 OpenCL\n" \
"OpDecorate %var LinkageAttributes \"foo\" " \
"{Import,Export}\n" \
"; CHECK: [[baseint:%\\w+]] = OpTypeInt 32 1\n" \
diff --git a/test/link/unique_ids_test.cpp b/test/link/unique_ids_test.cpp
index 55c70ea6..16a9b52a 100644
--- a/test/link/unique_ids_test.cpp
+++ b/test/link/unique_ids_test.cpp
@@ -135,7 +135,8 @@ TEST_F(UniqueIds, UniquelyMerged) {
LinkerOptions options;
options.SetVerifyIds(true);
spv_result_t res = AssembleAndLink(bodies, &linked_binary, options);
- EXPECT_EQ(SPV_SUCCESS, res);
+ ASSERT_EQ(SPV_SUCCESS, res) << GetErrorMessage();
+ EXPECT_THAT(GetErrorMessage(), std::string());
}
} // namespace
diff --git a/test/lint/CMakeLists.txt b/test/lint/CMakeLists.txt
new file mode 100644
index 00000000..09bc6d39
--- /dev/null
+++ b/test/lint/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2021 Google LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_spvtools_unittest(TARGET lint
+ SRCS divergence_analysis_test.cpp
+ LIBS SPIRV-Tools-lint SPIRV-Tools-opt
+)
diff --git a/test/lint/divergence_analysis_test.cpp b/test/lint/divergence_analysis_test.cpp
new file mode 100644
index 00000000..36cd32dd
--- /dev/null
+++ b/test/lint/divergence_analysis_test.cpp
@@ -0,0 +1,700 @@
+// Copyright (c) 2021 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/lint/divergence_analysis.h"
+
+#include <string>
+
+#include "gtest/gtest.h"
+#include "source/opt/build_module.h"
+#include "source/opt/ir_context.h"
+#include "source/opt/module.h"
+#include "spirv-tools/libspirv.h"
+
+namespace spvtools {
+namespace lint {
+namespace {
+
+void CLIMessageConsumer(spv_message_level_t level, const char*,
+ const spv_position_t& position, const char* message) {
+ switch (level) {
+ case SPV_MSG_FATAL:
+ case SPV_MSG_INTERNAL_ERROR:
+ case SPV_MSG_ERROR:
+ std::cerr << "error: line " << position.index << ": " << message
+ << std::endl;
+ break;
+ case SPV_MSG_WARNING:
+ std::cout << "warning: line " << position.index << ": " << message
+ << std::endl;
+ break;
+ case SPV_MSG_INFO:
+ std::cout << "info: line " << position.index << ": " << message
+ << std::endl;
+ break;
+ default:
+ break;
+ }
+}
+
+class DivergenceTest : public ::testing::Test {
+ protected:
+ std::unique_ptr<opt::IRContext> context_;
+ std::unique_ptr<DivergenceAnalysis> divergence_;
+
+ void Build(std::string text, uint32_t function_id = 1) {
+ context_ = BuildModule(SPV_ENV_UNIVERSAL_1_0, CLIMessageConsumer, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ASSERT_NE(nullptr, context_.get());
+ opt::Module* module = context_->module();
+ ASSERT_NE(nullptr, module);
+ // First function should have the given ID.
+ ASSERT_NE(module->begin(), module->end());
+ opt::Function* function = &*module->begin();
+ ASSERT_EQ(function->result_id(), function_id);
+ divergence_.reset(new DivergenceAnalysis(*context_));
+ divergence_->Run(function);
+ }
+};
+
+// Makes assertions a bit shorter.
+using Level = DivergenceAnalysis::DivergenceLevel;
+
+namespace {
+std::string Preamble() {
+ return R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main" %x %y
+ OpExecutionMode %1 OriginLowerLeft
+ OpDecorate %y Flat
+ %void = OpTypeVoid
+ %void_f = OpTypeFunction %void
+ %bool = OpTypeBool
+ %float = OpTypeFloat 32
+ %false = OpConstantFalse %bool
+ %true = OpConstantTrue %bool
+ %zero = OpConstant %float 0
+ %one = OpConstant %float 1
+ %x_t = OpTypePointer Input %float
+ %x = OpVariable %x_t Input
+ %y = OpVariable %x_t Input
+ %1 = OpFunction %void None %void_f
+ )";
+}
+} // namespace
+
+TEST_F(DivergenceTest, SimpleTest) {
+ // pseudocode:
+ // %10:
+ // %11 = load x
+ // if (%12 = (%11 < 0)) {
+ // %13:
+ // // do nothing
+ // }
+ // %14:
+ // return
+ ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
+ %10 = OpLabel
+ %11 = OpLoad %float %x
+ %12 = OpFOrdLessThan %bool %11 %zero
+ OpSelectionMerge %14 None
+ OpBranchConditional %12 %13 %14
+ %13 = OpLabel
+ OpBranch %14
+ %14 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )"));
+ // Control flow divergence.
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13));
+ EXPECT_EQ(12, divergence_->GetDivergenceSource(13));
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14));
+ // Value divergence.
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11));
+ EXPECT_EQ(0, divergence_->GetDivergenceSource(11));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12));
+ EXPECT_EQ(11, divergence_->GetDivergenceSource(12));
+}
+
+TEST_F(DivergenceTest, FlowTypesTest) {
+ // pseudocode:
+ // %10:
+ // %11 = load x
+ // %12 = x < 0 // data -> data
+ // if (%12) {
+ // %13: // data -> control
+ // if (true) {
+ // %14: // control -> control
+ // }
+ // %15:
+ // %16 = 1
+ // } else {
+ // %17:
+ // %18 = 2
+ // }
+ // %19:
+ // %19 = phi(%16 from %15, %18 from %17) // control -> data
+ // return
+ ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
+ %10 = OpLabel
+ %11 = OpLoad %float %x
+ %12 = OpFOrdLessThan %bool %11 %zero
+ OpSelectionMerge %19 None
+ OpBranchConditional %12 %13 %17
+ %13 = OpLabel
+ OpSelectionMerge %15 None
+ OpBranchConditional %true %14 %15
+ %14 = OpLabel
+ OpBranch %15
+ %15 = OpLabel
+ %16 = OpFAdd %float %zero %zero
+ OpBranch %19
+ %17 = OpLabel
+ %18 = OpFAdd %float %zero %one
+ OpBranch %19
+ %19 = OpLabel
+ %20 = OpPhi %float %16 %15 %18 %17
+ OpReturn
+ OpFunctionEnd
+ )"));
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
+
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11));
+ EXPECT_EQ(0, divergence_->GetDivergenceSource(11));
+
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12));
+ EXPECT_EQ(11, divergence_->GetDivergenceSource(12));
+
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13));
+ EXPECT_EQ(12, divergence_->GetDivergenceSource(13));
+ EXPECT_EQ(10, divergence_->GetDivergenceDependenceSource(13));
+
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14));
+ EXPECT_EQ(13, divergence_->GetDivergenceSource(14));
+
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15));
+ EXPECT_EQ(12, divergence_->GetDivergenceSource(15));
+ EXPECT_EQ(10, divergence_->GetDivergenceDependenceSource(15));
+
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(16));
+
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(17));
+ EXPECT_EQ(12, divergence_->GetDivergenceSource(17));
+ EXPECT_EQ(10, divergence_->GetDivergenceDependenceSource(17));
+
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(18));
+
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(19));
+
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(20));
+ EXPECT_TRUE(divergence_->GetDivergenceSource(20) == 15 ||
+ divergence_->GetDivergenceDependenceSource(20) == 17)
+ << "Got: " << divergence_->GetDivergenceDependenceSource(20);
+}
+
+TEST_F(DivergenceTest, ExitDependenceTest) {
+ // pseudocode:
+ // %10:
+ // %11 = load x
+ // %12 = %11 < 0
+ // %13:
+ // do {
+ // %14:
+ // if (%12) {
+ // %15:
+ // continue;
+ // }
+ // %16:
+ // %17:
+ // continue;
+ // } %18: while(false);
+ // %19:
+ // return
+ ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
+ %10 = OpLabel
+ %11 = OpLoad %float %x
+ %12 = OpFOrdLessThan %bool %11 %zero ; data -> data
+ OpBranch %13
+ %13 = OpLabel
+ OpLoopMerge %19 %18 None
+ OpBranch %14
+ %14 = OpLabel
+ OpSelectionMerge %16 None
+ OpBranchConditional %12 %15 %16
+ %15 = OpLabel
+ OpBranch %18 ; continue
+ %16 = OpLabel
+ OpBranch %17
+ %17 = OpLabel
+ OpBranch %18 ; continue
+ %18 = OpLabel
+ OpBranchConditional %false %13 %19
+ %19 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )"));
+
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
+
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11));
+ EXPECT_EQ(0, divergence_->GetDivergenceSource(11));
+
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12));
+ EXPECT_EQ(11, divergence_->GetDivergenceSource(12));
+
+ // Since both branches continue, there's no divergent control dependence
+ // to 13.
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(13));
+
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14));
+
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15));
+ EXPECT_EQ(12, divergence_->GetDivergenceSource(15));
+ EXPECT_EQ(14, divergence_->GetDivergenceDependenceSource(15));
+
+ // These two blocks are outside the if but are still control dependent.
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16));
+ EXPECT_EQ(12, divergence_->GetDivergenceSource(16));
+ EXPECT_EQ(14, divergence_->GetDivergenceDependenceSource(16));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(17));
+ EXPECT_EQ(12, divergence_->GetDivergenceSource(17));
+ EXPECT_EQ(14, divergence_->GetDivergenceDependenceSource(17));
+
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(18));
+
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(19));
+}
+
+TEST_F(DivergenceTest, ReconvergencePromotionTest) {
+ // pseudocode:
+ // %10:
+ // %11 = load y
+ // %12 = %11 < 0
+ // if (%12) {
+ // %13:
+ // %14:
+ // %15:
+ // if (true) {
+ // %16:
+ // }
+ // // Reconvergence *not* guaranteed as
+ // // control is not uniform on the IG level
+ // // at %15.
+ // %17:
+ // %18:
+ // %19:
+ // %20 = load x
+ // }
+ // %21:
+ // %22 = phi(%11, %20)
+ // return
+ ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
+ %10 = OpLabel
+ %11 = OpLoad %float %y
+ %12 = OpFOrdLessThan %bool %11 %zero
+ OpSelectionMerge %21 None
+ OpBranchConditional %12 %13 %21
+ %13 = OpLabel
+ OpBranch %14
+ %14 = OpLabel
+ OpBranch %15
+ %15 = OpLabel
+ OpSelectionMerge %17 None
+ OpBranchConditional %true %16 %17
+ %16 = OpLabel
+ OpBranch %17
+ %17 = OpLabel
+ OpBranch %18
+ %18 = OpLabel
+ OpBranch %19
+ %19 = OpLabel
+ %20 = OpLoad %float %y
+ OpBranch %21
+ %21 = OpLabel
+ %22 = OpPhi %float %11 %10 %20 %19
+ OpReturn
+ OpFunctionEnd
+ )"));
+ ASSERT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
+ ASSERT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(21));
+
+ ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(11));
+ ASSERT_EQ(0, divergence_->GetDivergenceSource(11));
+ ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(12));
+ ASSERT_EQ(11, divergence_->GetDivergenceSource(12));
+ ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(13));
+ ASSERT_EQ(12, divergence_->GetDivergenceSource(13));
+ ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(13));
+ ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(14));
+ ASSERT_EQ(12, divergence_->GetDivergenceSource(14));
+ ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(14));
+ ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(15));
+ ASSERT_EQ(12, divergence_->GetDivergenceSource(15));
+ ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(15));
+ ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(16));
+ ASSERT_EQ(15, divergence_->GetDivergenceSource(16));
+
+ ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(17));
+ ASSERT_EQ(12, divergence_->GetDivergenceSource(17));
+ ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(18));
+ ASSERT_EQ(12, divergence_->GetDivergenceSource(18));
+ ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(19));
+ ASSERT_EQ(12, divergence_->GetDivergenceSource(19));
+
+ ASSERT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(20));
+ ASSERT_EQ(0, divergence_->GetDivergenceSource(20));
+ ASSERT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(22));
+ ASSERT_EQ(19, divergence_->GetDivergenceSource(22));
+ ASSERT_EQ(10, divergence_->GetDivergenceDependenceSource(15));
+}
+
+TEST_F(DivergenceTest, FunctionCallTest) {
+ // pseudocode:
+ // %2() {
+ // %20:
+ // %21 = load x
+ // %22 = %21 < 0
+ // if (%22) {
+ // %23:
+ // return
+ // }
+ // %24:
+ // return
+ // }
+ //
+ // main() {
+ // %10:
+ // %11 = %2();
+ // // Reconvergence *not* guaranteed.
+ // %12:
+ // return
+ // }
+ ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
+ %10 = OpLabel
+ %11 = OpFunctionCall %void %2
+ OpBranch %12
+ %12 = OpLabel
+ OpReturn
+ OpFunctionEnd
+
+ %2 = OpFunction %void None %void_f
+ %20 = OpLabel
+ %21 = OpLoad %float %x
+ %22 = OpFOrdLessThan %bool %21 %zero
+ OpSelectionMerge %24 None
+ OpBranchConditional %22 %23 %24
+ %23 = OpLabel
+ OpReturn
+ %24 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )"));
+
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
+ // Conservatively assume function return value is uniform.
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(11));
+ // TODO(dongja): blocks reachable from diverging function calls should be
+ // divergent.
+ // EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12));
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(12)); // Wrong!
+}
+
+TEST_F(DivergenceTest, LateMergeTest) {
+ // pseudocode:
+ // %10:
+ // %11 = load y
+ // %12 = %11 < 0
+ // [merge: %15]
+ // if (%12) {
+ // %13:
+ // }
+ // %14: // Reconvergence hasn't happened by here.
+ // %15:
+ // return
+ ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
+ %10 = OpLabel
+ %11 = OpLoad %float %x
+ %12 = OpFOrdLessThan %bool %11 %zero
+ OpSelectionMerge %15 None
+ OpBranchConditional %12 %13 %14
+ %13 = OpLabel
+ OpBranch %14
+ %14 = OpLabel
+ OpBranch %15
+ %15 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )"));
+
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13));
+ // TODO(dongja):
+ // EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14));
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14)); // Wrong!
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(15));
+}
+
+// The following series of tests makes sure that we find the least fixpoint.
+TEST_F(DivergenceTest, UniformFixpointTest) {
+ // pseudocode:
+ // %10:
+ // %20 = load x
+ // %21 = load y
+ // do {
+ // %11:
+ // %12:
+ // %13 = phi(%zero from %11, %14 from %16)
+ // %14 = %13 + 1
+ // %15 = %13 < 1
+ // } %16: while (%15)
+ // %17:
+ ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
+ %10 = OpLabel
+ %20 = OpLoad %float %x
+ %21 = OpLoad %float %y
+ OpBranch %11
+ %11 = OpLabel
+ %13 = OpPhi %float %zero %10 %14 %16
+ OpLoopMerge %17 %16 None
+ OpBranch %12
+ %12 = OpLabel
+ %14 = OpFAdd %float %13 %one
+ %15 = OpFOrdLessThan %bool %13 %one
+ OpBranch %16
+ %16 = OpLabel
+ OpBranchConditional %15 %11 %17
+ %17 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )"));
+
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(11));
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(12));
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(13));
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(14));
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(15));
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(16));
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17));
+}
+
+TEST_F(DivergenceTest, PartiallyUniformFixpointTest) {
+ // pseudocode:
+ // %10:
+ // %20 = load x
+ // %21 = load y
+ // do {
+ // %11:
+ // %12:
+ // %13 = phi(%zero from %11, %14 from %16)
+ // %14 = %13 + 1
+ // %15 = %13 < %21
+ // } %16: while (%15)
+ // %17:
+ ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
+ %10 = OpLabel
+ %20 = OpLoad %float %x
+ %21 = OpLoad %float %y
+ OpBranch %11
+ %11 = OpLabel
+ %13 = OpPhi %float %zero %10 %14 %16
+ OpLoopMerge %17 %16 None
+ OpBranch %12
+ %12 = OpLabel
+ %14 = OpFAdd %float %13 %one
+ %15 = OpFOrdLessThan %bool %13 %21
+ OpBranch %16
+ %16 = OpLabel
+ OpBranchConditional %15 %11 %17
+ %17 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )"));
+
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
+ EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(11));
+ EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(12));
+ EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(13));
+ EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(14));
+ EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(15));
+ EXPECT_EQ(Level::kPartiallyUniform, divergence_->GetDivergenceLevel(16));
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17));
+}
+
+TEST_F(DivergenceTest, DivergentFixpointTest) {
+ // pseudocode:
+ // %10:
+ // %20 = load x
+ // %21 = load y
+ // do {
+ // %11:
+ // %12:
+ // %13 = phi(%zero from %11, %14 from %16)
+ // %14 = %13 + 1
+ // %15 = %13 < %20
+ // } %16: while (%15)
+ // %17:
+ ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
+ %10 = OpLabel
+ %20 = OpLoad %float %x
+ %21 = OpLoad %float %y
+ OpBranch %11
+ %11 = OpLabel
+ %13 = OpPhi %float %zero %10 %14 %16
+ OpLoopMerge %17 %16 None
+ OpBranch %12
+ %12 = OpLabel
+ %14 = OpFAdd %float %13 %one
+ %15 = OpFOrdLessThan %bool %13 %20
+ OpBranch %16
+ %16 = OpLabel
+ OpBranchConditional %15 %11 %17
+ %17 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )"));
+
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16));
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17));
+}
+
+TEST_F(DivergenceTest, DivergentOverridesPartiallyUniformTest) {
+ // pseudocode:
+ // %10:
+ // %20 = load x
+ // %21 = load y
+ // %11:
+ // do {
+ // %12:
+ // %13 = phi(%21 from %11, %14 from %16)
+ // %14 = %13 + 1
+ // %15 = %13 < %20
+ // } %16: while (%15)
+ // %17:
+ ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
+ %10 = OpLabel
+ %20 = OpLoad %float %x
+ %21 = OpLoad %float %y
+ OpBranch %11
+ %11 = OpLabel
+ %13 = OpPhi %float %zero %10 %14 %16
+ OpLoopMerge %17 %16 None
+ OpBranch %12
+ %12 = OpLabel
+ %14 = OpFAdd %float %13 %one
+ %15 = OpFOrdLessThan %bool %13 %20
+ OpBranch %16
+ %16 = OpLabel
+ OpBranchConditional %15 %11 %17
+ %17 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )"));
+
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16));
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17));
+}
+
+TEST_F(DivergenceTest, NestedFixpointTest) {
+ // pseudocode:
+ // %10:
+ // %20 = load x
+ // %21 = load y
+ // do {
+ // %22:
+ // %23:
+ // %24 = phi(%zero from %22, %25 from %26)
+ // %11:
+ // do {
+ // %12:
+ // %13 = phi(%zero from %11, %14 from %16)
+ // %14 = %13 + 1
+ // %15 = %13 < %24
+ // } %16: while (%15)
+ // %17:
+ // %25 = load x
+ // } %26: while (false)
+ // %27:
+ // return
+ ASSERT_NO_FATAL_FAILURE(Build(Preamble() + R"(
+ %10 = OpLabel
+ %20 = OpLoad %float %x
+ %21 = OpLoad %float %y
+ OpBranch %22
+ %22 = OpLabel
+ %24 = OpPhi %float %zero %10 %25 %26
+ OpLoopMerge %27 %26 None
+ OpBranch %23
+ %23 = OpLabel
+ OpBranch %11
+ %11 = OpLabel
+ %13 = OpPhi %float %zero %23 %14 %16
+ OpLoopMerge %17 %16 None
+ OpBranch %12
+ %12 = OpLabel
+ %14 = OpFAdd %float %13 %one
+ %15 = OpFOrdLessThan %bool %13 %24
+ OpBranch %16
+ %16 = OpLabel
+ OpBranchConditional %15 %11 %17
+ %17 = OpLabel
+ %25 = OpLoad %float %x
+ OpBranch %26
+ %26 = OpLabel
+ OpBranchConditional %false %22 %27
+ %27 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )"));
+ // This test makes sure that divergent values flowing upward can influence the
+ // fixpoint of a loop.
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(10));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(11));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(12));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(13));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(14));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(15));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(16));
+ // Control of the outer loop is still uniform.
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(17));
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(22));
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(23));
+ // Seed divergent values.
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(24));
+ EXPECT_EQ(Level::kDivergent, divergence_->GetDivergenceLevel(25));
+ // Outer loop control.
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(26));
+ // Merged.
+ EXPECT_EQ(Level::kUniform, divergence_->GetDivergenceLevel(27));
+}
+
+} // namespace
+} // namespace lint
+} // namespace spvtools
diff --git a/test/operand_capabilities_test.cpp b/test/operand_capabilities_test.cpp
index 6f83dfeb..bc0ee055 100644
--- a/test/operand_capabilities_test.cpp
+++ b/test/operand_capabilities_test.cpp
@@ -368,19 +368,6 @@ INSTANTIATE_TEST_SUITE_P(
// clang-format on
})));
-// See SPIR-V Section 3.15 FP Fast Math Mode
-INSTANTIATE_TEST_SUITE_P(
- FPFastMathMode, EnumCapabilityTest,
- Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
- ValuesIn(std::vector<EnumCapabilityCase>{
- CASE0(FP_FAST_MATH_MODE, FPFastMathModeMaskNone),
- CASE1(FP_FAST_MATH_MODE, FPFastMathModeNotNaNMask, Kernel),
- CASE1(FP_FAST_MATH_MODE, FPFastMathModeNotInfMask, Kernel),
- CASE1(FP_FAST_MATH_MODE, FPFastMathModeNSZMask, Kernel),
- CASE1(FP_FAST_MATH_MODE, FPFastMathModeAllowRecipMask, Kernel),
- CASE1(FP_FAST_MATH_MODE, FPFastMathModeFastMask, Kernel),
- })));
-
// See SPIR-V Section 3.17 Linkage Type
INSTANTIATE_TEST_SUITE_P(
LinkageType, EnumCapabilityTest,
diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt
index f65d2ff3..759d4237 100644
--- a/test/opt/CMakeLists.txt
+++ b/test/opt/CMakeLists.txt
@@ -28,8 +28,11 @@ add_spvtools_unittest(TARGET opt
compact_ids_test.cpp
constants_test.cpp
constant_manager_test.cpp
+ control_dependence.cpp
convert_relaxed_to_half_test.cpp
+ convert_to_sampled_image_test.cpp
copy_prop_array_test.cpp
+ dataflow.cpp
dead_branch_elim_test.cpp
dead_insert_elim_test.cpp
dead_variable_elim_test.cpp
@@ -79,16 +82,19 @@ add_spvtools_unittest(TARGET opt
propagator_test.cpp
reduce_load_size_test.cpp
redundancy_elimination_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
replace_invalid_opc_test.cpp
scalar_analysis.cpp
scalar_replacement_test.cpp
set_spec_const_default_value_test.cpp
simplification_test.cpp
+ spread_volatile_semantics_test.cpp
strength_reduction_test.cpp
strip_debug_info_test.cpp
- strip_reflect_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 5b4291da..25f85416 100644
--- a/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -38,55 +38,45 @@ TEST_F(AggressiveDCETest, EliminateExtendedInst) {
// vec4 dv = sqrt(Dead);
// gl_FragColor = v;
// }
-
- const std::string predefs1 =
- R"(OpCapability Shader
+ const std::string spirv = R"(
+OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
+; CHECK: OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
-)";
-
- const std::string names_before =
- R"(OpName %main "main"
+OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
+; CHECK-NOT: OpName %dv "dv"
OpName %dv "dv"
+; CHECK-NOT: OpName %Dead "Dead"
OpName %Dead "Dead"
OpName %gl_FragColor "gl_FragColor"
-)";
-
- const std::string names_after =
- R"(OpName %main "main"
-OpName %v "v"
-OpName %BaseColor "BaseColor"
-OpName %Dead "Dead"
-OpName %gl_FragColor "gl_FragColor"
-)";
-
- const std::string predefs2 =
- R"(%void = OpTypeVoid
+%void = OpTypeVoid
%9 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
+; CHECK-NOT: %Dead = OpVariable
%Dead = OpVariable %_ptr_Input_v4float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %9
+%main = OpFunction %void None %9
%15 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
+; CHECK-NOT: %dv = OpVariable
%dv = OpVariable %_ptr_Function_v4float Function
%16 = OpLoad %v4float %BaseColor
OpStore %v %16
+; CHECK-NOT: OpLoad %v4float %Dead
%17 = OpLoad %v4float %Dead
+; CHECK-NOT: OpExtInst %v4float %1 Sqrt
%18 = OpExtInst %v4float %1 Sqrt %17
+; CHECK-NOT: OpStore %dv
OpStore %dv %18
%19 = OpLoad %v4float %v
OpStore %gl_FragColor %19
@@ -94,21 +84,7 @@ OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %9
-%15 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%16 = OpLoad %v4float %BaseColor
-OpStore %v %16
-%19 = OpLoad %v4float %v
-OpStore %gl_FragColor %19
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndCheck<AggressiveDCEPass>(
- predefs1 + names_before + predefs2 + func_before,
- predefs1 + names_after + predefs2 + func_after, true, true);
+ SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
}
TEST_F(AggressiveDCETest, NoEliminateFrexp) {
@@ -242,35 +218,23 @@ TEST_F(AggressiveDCETest, EliminateDecorate) {
// gl_FragColor = v;
// }
- const std::string predefs1 =
- R"(OpCapability Shader
+ const std::string spirv =
+ R"(
+OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
-)";
-
- const std::string names_before =
- R"(OpName %main "main"
+OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
OpName %dv "dv"
OpName %Dead "Dead"
OpName %gl_FragColor "gl_FragColor"
+; CHECK-NOT: OpDecorate
OpDecorate %8 RelaxedPrecision
-)";
-
- const std::string names_after =
- R"(OpName %main "main"
-OpName %v "v"
-OpName %BaseColor "BaseColor"
-OpName %Dead "Dead"
-OpName %gl_FragColor "gl_FragColor"
-)";
-
- const std::string predefs2_before =
- R"(%void = OpTypeVoid
+%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
@@ -281,29 +245,14 @@ OpName %gl_FragColor "gl_FragColor"
%float_0_5 = OpConstant %float 0.5
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
-)";
-
- const std::string predefs2_after =
- R"(%void = OpTypeVoid
-%10 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%Dead = OpVariable %_ptr_Input_v4float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%gl_FragColor = OpVariable %_ptr_Output_v4float Output
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %10
+%main = OpFunction %void None %10
%17 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%dv = OpVariable %_ptr_Function_v4float Function
%18 = OpLoad %v4float %BaseColor
OpStore %v %18
%19 = OpLoad %v4float %Dead
+; CHECK-NOT: OpVectorTimesScalar
%8 = OpVectorTimesScalar %v4float %19 %float_0_5
OpStore %dv %8
%20 = OpLoad %v4float %v
@@ -312,21 +261,7 @@ OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %10
-%17 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%18 = OpLoad %v4float %BaseColor
-OpStore %v %18
-%20 = OpLoad %v4float %v
-OpStore %gl_FragColor %20
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndCheck<AggressiveDCEPass>(
- predefs1 + names_before + predefs2_before + func_before,
- predefs1 + names_after + predefs2_after + func_after, true, true);
+ SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
}
TEST_F(AggressiveDCETest, Simple) {
@@ -342,53 +277,44 @@ TEST_F(AggressiveDCETest, Simple) {
// gl_FragColor = v;
// }
- const std::string predefs1 =
- R"(OpCapability Shader
+ const std::string spirv =
+ R"(
+OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
+; CHECK: OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
-)";
-
- const std::string names_before =
- R"(OpName %main "main"
+OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
+; CHECK-NOT: OpName %dv "dv"
OpName %dv "dv"
+; CHECK-NOT: OpName %Dead "Dead"
OpName %Dead "Dead"
OpName %gl_FragColor "gl_FragColor"
-)";
-
- const std::string names_after =
- R"(OpName %main "main"
-OpName %v "v"
-OpName %BaseColor "BaseColor"
-OpName %Dead "Dead"
-OpName %gl_FragColor "gl_FragColor"
-)";
-
- const std::string predefs2 =
- R"(%void = OpTypeVoid
+%void = OpTypeVoid
%9 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
+; CHECK-NOT: %Dead = OpVariable
%Dead = OpVariable %_ptr_Input_v4float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %9
+%main = OpFunction %void None %9
%15 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
+; CHECK-NOT: %dv = OpVariable
%dv = OpVariable %_ptr_Function_v4float Function
%16 = OpLoad %v4float %BaseColor
OpStore %v %16
+; CHECK-NOT: OpLoad %v4float %Dead
%17 = OpLoad %v4float %Dead
+; CHECK-NOT: OpStore %dv
OpStore %dv %17
%18 = OpLoad %v4float %v
OpStore %gl_FragColor %18
@@ -396,21 +322,7 @@ OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %9
-%15 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%16 = OpLoad %v4float %BaseColor
-OpStore %v %16
-%18 = OpLoad %v4float %v
-OpStore %gl_FragColor %18
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndCheck<AggressiveDCEPass>(
- predefs1 + names_before + predefs2 + func_before,
- predefs1 + names_after + predefs2 + func_after, true, true);
+ SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
}
TEST_F(AggressiveDCETest, OptAllowListExtension) {
@@ -426,35 +338,22 @@ TEST_F(AggressiveDCETest, OptAllowListExtension) {
// gl_FragColor = v;
// }
- const std::string predefs1 =
+ const std::string spirv =
R"(OpCapability Shader
OpExtension "SPV_AMD_gpu_shader_int16"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
+; CHECK: OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
-)";
-
- const std::string names_before =
- R"(OpName %main "main"
+OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
OpName %dv "dv"
OpName %Dead "Dead"
OpName %gl_FragColor "gl_FragColor"
-)";
-
- const std::string names_after =
- R"(OpName %main "main"
-OpName %v "v"
-OpName %BaseColor "BaseColor"
-OpName %Dead "Dead"
-OpName %gl_FragColor "gl_FragColor"
-)";
-
- const std::string predefs2 =
- R"(%void = OpTypeVoid
+%void = OpTypeVoid
%9 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
@@ -464,10 +363,7 @@ OpName %gl_FragColor "gl_FragColor"
%Dead = OpVariable %_ptr_Input_v4float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %9
+%main = OpFunction %void None %9
%15 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%dv = OpVariable %_ptr_Function_v4float Function
@@ -481,21 +377,7 @@ OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %9
-%15 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%16 = OpLoad %v4float %BaseColor
-OpStore %v %16
-%18 = OpLoad %v4float %v
-OpStore %gl_FragColor %18
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndCheck<AggressiveDCEPass>(
- predefs1 + names_before + predefs2 + func_before,
- predefs1 + names_after + predefs2 + func_after, true, true);
+ SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
}
TEST_F(AggressiveDCETest, NoOptDenyListExtension) {
@@ -571,7 +453,7 @@ TEST_F(AggressiveDCETest, ElimWithCall) {
// gl_FragColor = vec4(0.0);
// }
- const std::string defs_before =
+ const std::string text =
R"( OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
@@ -600,54 +482,25 @@ OpName %gl_FragColor "gl_FragColor"
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
%float_0 = OpConstant %float 0
%20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
-)";
-
- const std::string defs_after =
- R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %i1 %i2 %gl_FragColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 140
-OpName %main "main"
-OpName %nothing_vf4_ "nothing(vf4;"
-OpName %v "v"
-OpName %v1 "v1"
-OpName %i1 "i1"
-OpName %i2 "i2"
-OpName %param "param"
-OpName %gl_FragColor "gl_FragColor"
-%void = OpTypeVoid
-%12 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%16 = OpTypeFunction %void %_ptr_Function_v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%i1 = OpVariable %_ptr_Input_v4float Input
-%i2 = OpVariable %_ptr_Input_v4float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%gl_FragColor = OpVariable %_ptr_Output_v4float Output
-%float_0 = OpConstant %float 0
-%20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %12
+%main = OpFunction %void None %12
%21 = OpLabel
%v1 = OpVariable %_ptr_Function_v4float Function
%v2 = OpVariable %_ptr_Function_v4float Function
%param = OpVariable %_ptr_Function_v4float Function
%22 = OpLoad %v4float %i1
OpStore %v1 %22
+; CHECK-NOT: OpLoad %v4float %i2
%23 = OpLoad %v4float %i2
+; CHECK-NOT: OpStore %v2
OpStore %v2 %23
%24 = OpLoad %v4float %v1
OpStore %param %24
+; CHECK: OpFunctionCall %void %nothing_vf4_
%25 = OpFunctionCall %void %nothing_vf4_ %param
OpStore %gl_FragColor %20
OpReturn
OpFunctionEnd
+; CHECK: %nothing_vf4_ = OpFunction
%nothing_vf4_ = OpFunction %void None %16
%v = OpFunctionParameter %_ptr_Function_v4float
%26 = OpLabel
@@ -655,28 +508,7 @@ OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %12
-%21 = OpLabel
-%v1 = OpVariable %_ptr_Function_v4float Function
-%param = OpVariable %_ptr_Function_v4float Function
-%22 = OpLoad %v4float %i1
-OpStore %v1 %22
-%24 = OpLoad %v4float %v1
-OpStore %param %24
-%25 = OpFunctionCall %void %nothing_vf4_ %param
-OpStore %gl_FragColor %20
-OpReturn
-OpFunctionEnd
-%nothing_vf4_ = OpFunction %void None %16
-%v = OpFunctionParameter %_ptr_Function_v4float
-%26 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndCheck<AggressiveDCEPass>(defs_before + func_before,
- defs_after + func_after, true, true);
+ SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
}
TEST_F(AggressiveDCETest, NoParamElim) {
@@ -998,7 +830,7 @@ TEST_F(AggressiveDCETest, PrivateStoreElimInEntryNoCalls) {
// OutColor = v;
// }
- const std::string predefs_before =
+ const std::string spirv =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
@@ -1008,6 +840,7 @@ OpSource GLSL 450
OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
+; CHECK-NOT: OpName %dv "dv"
OpName %dv "dv"
OpName %Dead "Dead"
OpName %OutColor "OutColor"
@@ -1019,49 +852,22 @@ OpDecorate %OutColor Location 0
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
+; CHECK-NOT: OpTypePointer Private
%_ptr_Private_v4float = OpTypePointer Private %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%Dead = OpVariable %_ptr_Input_v4float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
+; CHECK-NOT: %dv = OpVariable
%dv = OpVariable %_ptr_Private_v4float Private
%OutColor = OpVariable %_ptr_Output_v4float Output
-)";
-
- const std::string predefs_after =
- R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BaseColor %Dead %OutColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpName %main "main"
-OpName %v "v"
-OpName %BaseColor "BaseColor"
-OpName %Dead "Dead"
-OpName %OutColor "OutColor"
-OpDecorate %BaseColor Location 0
-OpDecorate %Dead Location 1
-OpDecorate %OutColor Location 0
-%void = OpTypeVoid
-%9 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%Dead = OpVariable %_ptr_Input_v4float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%OutColor = OpVariable %_ptr_Output_v4float Output
-)";
-
- const std::string main_before =
- R"(%main = OpFunction %void None %9
+%main = OpFunction %void None %9
%16 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%17 = OpLoad %v4float %BaseColor
OpStore %v %17
%18 = OpLoad %v4float %Dead
+; CHECK-NOT: OpStore %dv
OpStore %dv %18
%19 = OpLoad %v4float %v
%20 = OpFNegate %v4float %19
@@ -1070,21 +876,7 @@ OpReturn
OpFunctionEnd
)";
- const std::string main_after =
- R"(%main = OpFunction %void None %9
-%16 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%17 = OpLoad %v4float %BaseColor
-OpStore %v %17
-%19 = OpLoad %v4float %v
-%20 = OpFNegate %v4float %19
-OpStore %OutColor %20
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndCheck<AggressiveDCEPass>(
- predefs_before + main_before, predefs_after + main_after, true, true);
+ SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
}
TEST_F(AggressiveDCETest, NoPrivateStoreElimIfLoad) {
@@ -1288,7 +1080,7 @@ TEST_F(AggressiveDCETest, WorkgroupStoreElimInEntryNoCalls) {
// OutColor = v;
// }
- const std::string predefs_before =
+ const std::string spirv =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
@@ -1298,6 +1090,7 @@ OpSource GLSL 450
OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
+; CHECK-NOT: OpName %dv "dv"
OpName %dv "dv"
OpName %Dead "Dead"
OpName %OutColor "OutColor"
@@ -1309,49 +1102,22 @@ OpDecorate %OutColor Location 0
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
+; CHECK-NOT: OpTypePointer Workgroup
%_ptr_Workgroup_v4float = OpTypePointer Workgroup %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%Dead = OpVariable %_ptr_Input_v4float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
+; CHECK-NOT: %dv = OpVariable
%dv = OpVariable %_ptr_Workgroup_v4float Workgroup
%OutColor = OpVariable %_ptr_Output_v4float Output
-)";
-
- const std::string predefs_after =
- R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BaseColor %Dead %OutColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpName %main "main"
-OpName %v "v"
-OpName %BaseColor "BaseColor"
-OpName %Dead "Dead"
-OpName %OutColor "OutColor"
-OpDecorate %BaseColor Location 0
-OpDecorate %Dead Location 1
-OpDecorate %OutColor Location 0
-%void = OpTypeVoid
-%9 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Function_v4float = OpTypePointer Function %v4float
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%Dead = OpVariable %_ptr_Input_v4float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%OutColor = OpVariable %_ptr_Output_v4float Output
-)";
-
- const std::string main_before =
- R"(%main = OpFunction %void None %9
+%main = OpFunction %void None %9
%16 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%17 = OpLoad %v4float %BaseColor
OpStore %v %17
%18 = OpLoad %v4float %Dead
+; CHECK-NOT: OpStore %dv
OpStore %dv %18
%19 = OpLoad %v4float %v
%20 = OpFNegate %v4float %19
@@ -1360,21 +1126,7 @@ OpReturn
OpFunctionEnd
)";
- const std::string main_after =
- R"(%main = OpFunction %void None %9
-%16 = OpLabel
-%v = OpVariable %_ptr_Function_v4float Function
-%17 = OpLoad %v4float %BaseColor
-OpStore %v %17
-%19 = OpLoad %v4float %v
-%20 = OpFNegate %v4float %19
-OpStore %OutColor %20
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndCheck<AggressiveDCEPass>(
- predefs_before + main_before, predefs_after + main_after, true, true);
+ SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
}
TEST_F(AggressiveDCETest, EliminateDeadIfThenElse) {
@@ -1393,7 +1145,7 @@ TEST_F(AggressiveDCETest, EliminateDeadIfThenElse) {
// OutColor = vec4(1.0,1.0,1.0,1.0);
// }
- const std::string predefs_before =
+ const std::string spirv =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
@@ -1424,34 +1176,11 @@ OpDecorate %OutColor Location 0
%OutColor = OpVariable %_ptr_Output_v4float Output
%float_1 = OpConstant %float 1
%21 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
-)";
-
- const std::string predefs_after =
- R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BaseColor %OutColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpName %main "main"
-OpName %BaseColor "BaseColor"
-OpName %OutColor "OutColor"
-OpDecorate %BaseColor Location 0
-OpDecorate %OutColor Location 0
-%void = OpTypeVoid
-%7 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%OutColor = OpVariable %_ptr_Output_v4float Output
-%float_1 = OpConstant %float 1
-%21 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %7
+; CHECK: = OpFunction %void
+; CHECK-NEXT: %22 = OpLabel
+; CHECK-NEXT: OpBranch %26
+; CHECK-NEXT: %26 = OpLabel
+%main = OpFunction %void None %7
%22 = OpLabel
%d = OpVariable %_ptr_Function_float Function
%23 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0
@@ -1475,18 +1204,7 @@ OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %7
-%22 = OpLabel
-OpBranch %26
-%26 = OpLabel
-OpStore %OutColor %21
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndCheck<AggressiveDCEPass>(
- predefs_before + func_before, predefs_after + func_after, true, true);
+ SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
}
TEST_F(AggressiveDCETest, EliminateDeadIfThen) {
@@ -1503,7 +1221,7 @@ TEST_F(AggressiveDCETest, EliminateDeadIfThen) {
// OutColor = vec4(1.0,1.0,1.0,1.0);
// }
- const std::string predefs_before =
+ const std::string spirv =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
@@ -1533,34 +1251,11 @@ OpDecorate %OutColor Location 0
%OutColor = OpVariable %_ptr_Output_v4float Output
%float_1 = OpConstant %float 1
%20 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
-)";
-
- const std::string predefs_after =
- R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BaseColor %OutColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpName %main "main"
-OpName %BaseColor "BaseColor"
-OpName %OutColor "OutColor"
-OpDecorate %BaseColor Location 0
-OpDecorate %OutColor Location 0
-%void = OpTypeVoid
-%7 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%OutColor = OpVariable %_ptr_Output_v4float Output
-%float_1 = OpConstant %float 1
-%20 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
-)";
-
- const std::string func_before =
- R"(%main = OpFunction %void None %7
+; CHECK: = OpFunction
+; CHECK-NEXT: %21 = OpLabel
+; CHECK-NEXT: OpBranch [[target:%\w+]]
+; CHECK-NEXT: [[target]] = OpLabel
+%main = OpFunction %void None %7
%21 = OpLabel
%d = OpVariable %_ptr_Function_float Function
%22 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0
@@ -1579,18 +1274,7 @@ OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %7
-%21 = OpLabel
-OpBranch %25
-%25 = OpLabel
-OpStore %OutColor %20
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndCheck<AggressiveDCEPass>(
- predefs_before + func_before, predefs_after + func_after, true, true);
+ SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
}
TEST_F(AggressiveDCETest, EliminateDeadSwitch) {
@@ -1609,7 +1293,7 @@ TEST_F(AggressiveDCETest, EliminateDeadSwitch) {
// }
// OutColor = vec4(1.0,1.0,1.0,1.0);
// }
- const std::string before =
+ const std::string spirv =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
@@ -1642,6 +1326,10 @@ TEST_F(AggressiveDCETest, EliminateDeadSwitch) {
%OutColor = OpVariable %_ptr_Output_v4float Output
%float_1 = OpConstant %float 1
%27 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+; CHECK: = OpFunction
+; CHECK-NEXT: = OpLabel
+; CHECK-NEXT: OpBranch [[target:%\w+]]
+; CHECK-NEXT: [[target]] = OpLabel
%main = OpFunction %void None %3
%5 = OpLabel
%d = OpVariable %_ptr_Function_float Function
@@ -1658,45 +1346,7 @@ TEST_F(AggressiveDCETest, EliminateDeadSwitch) {
OpReturn
OpFunctionEnd)";
- const std::string after =
- R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %x %BaseColor %OutColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpName %main "main"
-OpName %x "x"
-OpName %BaseColor "BaseColor"
-OpName %OutColor "OutColor"
-OpDecorate %x Flat
-OpDecorate %x Location 1
-OpDecorate %BaseColor Location 0
-OpDecorate %OutColor Location 0
-%void = OpTypeVoid
-%3 = OpTypeFunction %void
-%int = OpTypeInt 32 1
-%_ptr_Input_int = OpTypePointer Input %int
-%x = OpVariable %_ptr_Input_int Input
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%OutColor = OpVariable %_ptr_Output_v4float Output
-%float_1 = OpConstant %float 1
-%27 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
-%main = OpFunction %void None %3
-%5 = OpLabel
-OpBranch %11
-%11 = OpLabel
-OpStore %OutColor %27
-OpReturn
-OpFunctionEnd
-)";
-
- SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- SinglePassRunAndCheck<AggressiveDCEPass>(before, after, true, true);
+ SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
}
TEST_F(AggressiveDCETest, EliminateDeadIfThenElseNested) {
@@ -1721,7 +1371,7 @@ TEST_F(AggressiveDCETest, EliminateDeadIfThenElseNested) {
// OutColor = vec4(1.0,1.0,1.0,1.0);
// }
- const std::string predefs_before =
+ const std::string spirv =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
@@ -1754,34 +1404,14 @@ OpDecorate %OutColor Location 0
%OutColor = OpVariable %_ptr_Output_v4float Output
%float_1 = OpConstant %float 1
%23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
-)";
- const std::string predefs_after =
- R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BaseColor %OutColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpName %main "main"
-OpName %BaseColor "BaseColor"
-OpName %OutColor "OutColor"
-OpDecorate %BaseColor Location 0
-OpDecorate %OutColor Location 0
-%void = OpTypeVoid
-%7 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%OutColor = OpVariable %_ptr_Output_v4float Output
-%float_1 = OpConstant %float 1
-%23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
-)";
+; CHECK: = OpFunction
+; CHECK-NEXT: = OpLabel
+; CHECK-NEXT: OpBranch [[target:%\w+]]
+; CHECK-NEXT: [[target]] = OpLabel
+; CHECK-NOT: OpLabel
- const std::string func_before =
- R"(%main = OpFunction %void None %7
+%main = OpFunction %void None %7
%24 = OpLabel
%d = OpVariable %_ptr_Function_float Function
%25 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0
@@ -1823,18 +1453,7 @@ OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %7
-%24 = OpLabel
-OpBranch %28
-%28 = OpLabel
-OpStore %OutColor %23
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndCheck<AggressiveDCEPass>(
- predefs_before + func_before, predefs_after + func_after, true, true);
+ SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
}
TEST_F(AggressiveDCETest, NoEliminateLiveIfThenElse) {
@@ -2578,7 +2197,7 @@ TEST_F(AggressiveDCETest, EliminateEntireFunctionBody) {
// d = BaseColor.z;
// }
- const std::string predefs_before =
+ const std::string spirv =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
@@ -2607,32 +2226,15 @@ OpDecorate %OutColor Location 0
%uint_2 = OpConstant %uint 2
%_ptr_Output_v4float = OpTypePointer Output %v4float
%OutColor = OpVariable %_ptr_Output_v4float Output
-)";
- const std::string predefs_after =
- R"(OpCapability Shader
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main" %BaseColor %OutColor
-OpExecutionMode %main OriginUpperLeft
-OpSource GLSL 450
-OpName %main "main"
-OpName %BaseColor "BaseColor"
-OpName %OutColor "OutColor"
-OpDecorate %BaseColor Location 0
-OpDecorate %OutColor Location 0
-%void = OpTypeVoid
-%7 = OpTypeFunction %void
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%_ptr_Input_v4float = OpTypePointer Input %v4float
-%BaseColor = OpVariable %_ptr_Input_v4float Input
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%OutColor = OpVariable %_ptr_Output_v4float Output
-)";
+; CHECK: = OpFunction
+; CHECK-NEXT: = OpLabel
+; CHECK-NEXT: OpBranch [[target:%\w+]]
+; CHECK-NEXT: [[target]] = OpLabel
+; CHECK-NEXT: OpReturn
+; CHECK-NEXT: OpFunctionEnd
- const std::string func_before =
- R"(%main = OpFunction %void None %7
+%main = OpFunction %void None %7
%20 = OpLabel
%d = OpVariable %_ptr_Function_float Function
%21 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0
@@ -2655,17 +2257,7 @@ OpReturn
OpFunctionEnd
)";
- const std::string func_after =
- R"(%main = OpFunction %void None %7
-%20 = OpLabel
-OpBranch %24
-%24 = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
- SinglePassRunAndCheck<AggressiveDCEPass>(
- predefs_before + func_before, predefs_after + func_after, true, true);
+ SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
}
TEST_F(AggressiveDCETest, EliminateUselessInnerLoop) {
@@ -4731,7 +4323,7 @@ INSTANTIATE_TEST_SUITE_P(
},
// Uint vector type spec constants. One vector has all component dead,
- // another vector has one dead unsigend integer and one used unsigned
+ // another vector has one dead unsigned integer and one used unsigned
// integer.
{
/* .used_consts = */
@@ -5521,10 +5113,9 @@ OpCapability ImageBuffer
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %2 "min" %gl_GlobalInvocationID
+OpEntryPoint GLCompute %2 "min"
OpExecutionMode %2 LocalSize 64 1 1
OpSource HLSL 600
-OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
OpDecorate %4 DescriptorSet 4
OpDecorate %4 Binding 70
%uint = OpTypeInt 32 0
@@ -5535,12 +5126,9 @@ OpDecorate %4 Binding 70
%10 = OpTypeFunction %void
%uint_0 = OpConstant %uint 0
%uint_1 = OpConstant %uint 1
-%v3uint = OpTypeVector %uint 3
-%_ptr_Input_v3uint = OpTypePointer Input %v3uint
%_ptr_Image_uint = OpTypePointer Image %uint
%4 = OpVariable %_ptr_UniformConstant_6 UniformConstant
%16 = OpVariable %_ptr_Private_6 Private
-%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
%2 = OpFunction %void None %10
%17 = OpLabel
%18 = OpLoad %6 %4
@@ -6393,8 +5981,8 @@ OpFunctionEnd
TEST_F(AggressiveDCETest, DeadInputInterfaceV13) {
const std::string spirv = R"(
-; CHECK: OpEntryPoint GLCompute %main "main" [[var:%\w+]]
-; CHECK: [[var]] = OpVariable
+; CHECK: OpEntryPoint GLCompute %main "main"
+; CHECK-NOT: OpVariable
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %dead
@@ -6417,8 +6005,8 @@ OpFunctionEnd
TEST_F(AggressiveDCETest, DeadInputInterfaceV14) {
const std::string spirv = R"(
-; CHECK: OpEntryPoint GLCompute %main "main" [[var:%\w+]]
-; CHECK: [[var]] = OpVariable
+; CHECK: OpEntryPoint GLCompute %main "main"
+; CHECK-NOT: OpVariable
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %dead
@@ -7063,6 +6651,287 @@ TEST_F(AggressiveDCETest, DebugInfoKeepInFunctionElimStoreVar) {
SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
}
+TEST_F(AggressiveDCETest, ShaderDebugInfoKeepInFunctionElimStoreVar) {
+ // Verify that dead local variable tc and store eliminated but all
+ // in-function NonSemantic Shader debuginfo kept.
+
+ 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" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0
+ OpExecutionMode %MainPs OriginUpperLeft
+ %7 = OpString "foo.frag"
+ %8 = OpString "PS_OUTPUT"
+ %9 = OpString "float"
+ %10 = OpString "vColor"
+ %11 = OpString "PS_INPUT"
+ %12 = OpString "vTextureCoords"
+ %13 = OpString "@type.2d.image"
+ %14 = OpString "type.2d.image"
+ %15 = OpString "Texture2D.TemplateParam"
+ %16 = OpString "src.MainPs"
+ %17 = OpString "tc"
+ %18 = OpString "ps_output"
+ %19 = OpString "i"
+ %20 = OpString "@type.sampler"
+ %21 = OpString "type.sampler"
+ %22 = OpString "g_sAniso"
+ %23 = OpString "g_tColor"
+ 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 %param_var_i "param.var.i"
+ 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_128 = OpConstant %uint 128
+ %uint_0 = OpConstant %uint 0
+ %uint_1 = OpConstant %uint 1
+ %uint_2 = OpConstant %uint 2
+ %uint_3 = OpConstant %uint 3
+ %uint_4 = OpConstant %uint 4
+ %uint_5 = OpConstant %uint 5
+ %uint_7 = OpConstant %uint 7
+ %uint_8 = OpConstant %uint 8
+ %uint_10 = OpConstant %uint 10
+ %uint_11 = OpConstant %uint 11
+ %uint_12 = OpConstant %uint 12
+ %uint_14 = OpConstant %uint 14
+ %uint_15 = OpConstant %uint 15
+ %uint_16 = OpConstant %uint 16
+ %uint_17 = OpConstant %uint 17
+ %uint_19 = OpConstant %uint 19
+ %uint_20 = OpConstant %uint 20
+ %uint_21 = OpConstant %uint 21
+ %uint_25 = OpConstant %uint 25
+ %uint_29 = OpConstant %uint 29
+ %uint_30 = OpConstant %uint 30
+ %uint_35 = OpConstant %uint 35
+ %uint_41 = OpConstant %uint 41
+ %uint_48 = OpConstant %uint 48
+ %uint_53 = OpConstant %uint 53
+ %uint_64 = OpConstant %uint 64
+ %45 = OpTypeFunction %void
+ %PS_INPUT = OpTypeStruct %v2float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+ %PS_OUTPUT = OpTypeStruct %v4float
+ %47 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+ %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
+ %51 = OpExtInst %void %1 DebugInfoNone
+ %52 = OpExtInst %void %1 DebugExpression
+ %53 = OpExtInst %void %1 DebugOperation %uint_0
+ %54 = OpExtInst %void %1 DebugExpression %53
+ %55 = OpExtInst %void %1 DebugSource %7
+ %56 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %55 %uint_5
+ %59 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 %uint_3 %uint_0
+ %60 = OpExtInst %void %1 DebugTypeVector %59 %uint_4
+ %58 = OpExtInst %void %1 DebugTypeMember %10 %60 %55 %uint_12 %uint_5 %uint_0 %uint_128 %uint_3
+ %57 = OpExtInst %void %1 DebugTypeComposite %8 %uint_1 %55 %uint_10 %uint_1 %56 %8 %uint_128 %uint_3 %58
+ %63 = OpExtInst %void %1 DebugTypeVector %59 %uint_2
+ %62 = OpExtInst %void %1 DebugTypeMember %12 %63 %55 %uint_7 %uint_5 %uint_0 %uint_64 %uint_3
+ %61 = OpExtInst %void %1 DebugTypeComposite %11 %uint_1 %55 %uint_5 %uint_1 %56 %11 %uint_64 %uint_3 %62
+ %64 = OpExtInst %void %1 DebugTypeComposite %13 %uint_0 %55 %uint_0 %uint_0 %56 %14 %51 %uint_3
+ %67 = OpExtInst %void %1 DebugTypeFunction %uint_3 %57 %61
+ %68 = OpExtInst %void %1 DebugFunction %16 %67 %55 %uint_15 %uint_1 %56 %16 %uint_3 %uint_16
+ %69 = OpExtInst %void %1 DebugLexicalBlock %55 %uint_16 %uint_1 %68
+ %70 = OpExtInst %void %1 DebugLocalVariable %17 %63 %55 %uint_19 %uint_12 %69 %uint_4
+ %71 = OpExtInst %void %1 DebugLocalVariable %18 %57 %55 %uint_17 %uint_15 %69 %uint_4
+ %72 = OpExtInst %void %1 DebugLocalVariable %19 %61 %55 %uint_15 %uint_29 %68 %uint_4 %uint_1
+ %73 = OpExtInst %void %1 DebugTypeComposite %20 %uint_1 %55 %uint_0 %uint_0 %56 %21 %51 %uint_3
+ %74 = OpExtInst %void %1 DebugGlobalVariable %22 %73 %55 %uint_3 %uint_14 %56 %22 %g_sAniso %uint_8
+ %75 = OpExtInst %void %1 DebugGlobalVariable %23 %64 %55 %uint_1 %uint_11 %56 %23 %g_tColor %uint_8
+ %MainPs = OpFunction %void None %45
+ %76 = OpLabel
+ %78 = OpVariable %_ptr_Function_PS_OUTPUT Function
+ %79 = OpVariable %_ptr_Function_v2float Function
+ %81 = OpVariable %_ptr_Function_PS_OUTPUT Function
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+ %82 = OpLoad %v2float %in_var_TEXCOORD2
+ %83 = OpCompositeConstruct %PS_INPUT %82
+ OpStore %param_var_i %83
+ %112 = OpExtInst %void %1 DebugFunctionDefinition %68 %MainPs
+ %109 = OpExtInst %void %1 DebugScope %68
+ %85 = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52
+ %110 = OpExtInst %void %1 DebugScope %69
+ %87 = OpExtInst %void %1 DebugDeclare %71 %78 %52
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugFunctionDefinition %68 %MainPs
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %68
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %69
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %71 %78 %52
+ %300 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_17 %uint_30
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_17 %uint_30
+ %88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
+ %89 = OpLoad %v2float %88
+ %301 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35
+ OpStore %79 %89
+;CHECK-NOT: OpStore %79 %89
+ %302 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35
+ %106 = OpExtInst %void %1 DebugValue %70 %89 %52
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugValue %70 %89 %52
+ %303 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_25 %uint_32
+ %91 = OpLoad %type_2d_image %g_tColor
+ %304 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_41 %uint_48
+ %92 = OpLoad %type_sampler %g_sAniso
+ %305 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_25 %uint_53
+ %94 = OpSampledImage %type_sampled_image %91 %92
+ %95 = OpImageSampleImplicitLod %v4float %94 %89 None
+ %306 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_5 %uint_53
+ %96 = OpAccessChain %_ptr_Function_v4float %78 %int_0
+ OpStore %96 %95
+ %307 = OpExtInst %void %1 DebugLine %55 %uint_21 %uint_21 %uint_12 %uint_20
+ %97 = OpLoad %PS_OUTPUT %78
+ %308 = OpExtInst %void %1 DebugLine %55 %uint_21 %uint_21 %uint_5 %uint_20
+ OpStore %81 %97
+ %309 = OpExtInst %void %1 DebugNoLine
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoLine
+ %111 = OpExtInst %void %1 DebugNoScope
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoScope
+ %100 = OpCompositeExtract %v4float %97 0
+ OpStore %out_var_SV_Target0 %100
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_2);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
+}
+
+TEST_F(AggressiveDCETest, ShaderDebugInfoGlobalDCE) {
+ // Verify that DebugGlobalVariable for eliminated private variable has
+ // variable operand replaced with DebugInfoNone.
+
+ 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 %a
+OpExecutionMode %MainPs OriginUpperLeft
+%5 = OpString "source2.hlsl"
+%24 = OpString "float"
+%29 = OpString "vColor"
+%33 = OpString "PS_OUTPUT"
+%37 = OpString "MainPs"
+%38 = OpString ""
+%42 = OpString "ps_output"
+%46 = OpString "a"
+OpName %a "a"
+OpName %out_var_SV_Target0 "out.var.SV_Target0"
+OpName %MainPs "MainPs"
+OpName %PS_OUTPUT "PS_OUTPUT"
+OpMemberName %PS_OUTPUT 0 "vColor"
+OpDecorate %out_var_SV_Target0 Location 0
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%8 = OpConstantNull %v4float
+%float_0 = OpConstant %float 0
+%10 = OpConstantComposite %v4float %float_0 %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_Private_v4float = OpTypePointer Private %v4float
+%_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_8 = OpConstant %uint 8
+%uint_9 = OpConstant %uint 9
+%uint_10 = OpConstant %uint 10
+%uint_15 = OpConstant %uint 15
+%48 = OpTypeFunction %void
+%PS_OUTPUT = OpTypeStruct %v4float
+%54 = OpTypeFunction %PS_OUTPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%a = OpVariable %_ptr_Private_v4float Private
+;CHECK-NOT: %a = OpVariable %_ptr_Private_v4float Private
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+;CHECK: [[dbg_none:%\w+]] = OpExtInst %void %1 DebugInfoNone
+%18 = OpExtInst %void %1 DebugExpression
+%19 = OpExtInst %void %1 DebugSource %5
+%20 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %19 %uint_5
+%25 = OpExtInst %void %1 DebugTypeBasic %24 %uint_32 %uint_3 %uint_0
+%28 = OpExtInst %void %1 DebugTypeVector %25 %uint_4
+%31 = OpExtInst %void %1 DebugTypeMember %29 %28 %19 %uint_5 %uint_12 %uint_0 %uint_128 %uint_3
+%34 = OpExtInst %void %1 DebugTypeComposite %33 %uint_1 %19 %uint_3 %uint_8 %20 %33 %uint_128 %uint_3 %31
+%36 = OpExtInst %void %1 DebugTypeFunction %uint_3 %34
+%39 = OpExtInst %void %1 DebugFunction %37 %36 %19 %uint_8 %uint_1 %20 %38 %uint_3 %uint_9
+%41 = OpExtInst %void %1 DebugLexicalBlock %19 %uint_9 %uint_1 %39
+%43 = OpExtInst %void %1 DebugLocalVariable %42 %34 %19 %uint_10 %uint_15 %41 %uint_4
+%47 = OpExtInst %void %1 DebugGlobalVariable %46 %28 %19 %uint_1 %uint_15 %20 %46 %a %uint_8
+;CHECK: %47 = OpExtInst %void %1 DebugGlobalVariable %46 %28 %19 %uint_1 %uint_15 %20 %46 [[dbg_none]] %uint_8
+%MainPs = OpFunction %void None %48
+%49 = OpLabel
+%65 = OpVariable %_ptr_Function_PS_OUTPUT Function
+%66 = OpVariable %_ptr_Function_PS_OUTPUT Function
+OpStore %a %8
+%72 = OpExtInst %void %1 DebugScope %41
+%69 = OpExtInst %void %1 DebugDeclare %43 %65 %18
+OpLine %5 11 5
+%70 = OpAccessChain %_ptr_Function_v4float %65 %int_0
+OpStore %70 %10
+OpLine %5 12 12
+%71 = OpLoad %PS_OUTPUT %65
+OpLine %5 12 5
+OpStore %66 %71
+%73 = OpExtInst %void %1 DebugNoLine
+%74 = OpExtInst %void %1 DebugNoScope
+%51 = OpLoad %PS_OUTPUT %66
+%53 = OpCompositeExtract %v4float %51 0
+OpStore %out_var_SV_Target0 %53
+OpLine %5 13 1
+OpReturn
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_2);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
+}
+
TEST_F(AggressiveDCETest, DebugInfoDeclareKeepsStore) {
// Verify that local variable tc and its store are kept by DebugDeclare.
//
@@ -7584,11 +7453,178 @@ TEST_F(AggressiveDCETest, KeepDebugScopeParent) {
SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
}
-// TODO(greg-lunarg): Add tests to verify handling of these cases:
-//
-// Check that logical addressing required
-// Check that function calls inhibit optimization
-// Others?
+TEST_F(AggressiveDCETest, KeepExportFunctions) {
+ // All functions are reachable. In particular, ExportedFunc and Constant are
+ // reachable because ExportedFunc is exported. Nothing should be removed.
+ const std::vector<const char*> text = {
+ // clang-format off
+ "OpCapability Shader",
+ "OpCapability Linkage",
+ "OpMemoryModel Logical GLSL450",
+ "OpEntryPoint Fragment %main \"main\"",
+ "OpName %main \"main\"",
+ "OpName %ExportedFunc \"ExportedFunc\"",
+ "OpName %Live \"Live\"",
+ "OpDecorate %ExportedFunc LinkageAttributes \"ExportedFunc\" Export",
+ "%void = OpTypeVoid",
+ "%7 = OpTypeFunction %void",
+ "%main = OpFunction %void None %7",
+ "%15 = OpLabel",
+ "OpReturn",
+ "OpFunctionEnd",
+"%ExportedFunc = OpFunction %void None %7",
+ "%19 = OpLabel",
+ "%16 = OpFunctionCall %void %Live",
+ "OpReturn",
+ "OpFunctionEnd",
+ "%Live = OpFunction %void None %7",
+ "%20 = OpLabel",
+ "OpReturn",
+ "OpFunctionEnd"
+ // clang-format on
+ };
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ std::string assembly = JoinAllInsts(text);
+ auto result = SinglePassRunAndDisassemble<AggressiveDCEPass>(
+ assembly, /* skip_nop = */ true, /* do_validation = */ false);
+ EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
+ EXPECT_EQ(assembly, std::get<0>(result));
+}
+
+TEST_F(AggressiveDCETest, KeepPrivateVarInExportFunctions) {
+ // The loads and stores from the private variable should not be removed
+ // because the functions are exported and could be called.
+ const std::string text = R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpSource HLSL 630
+OpName %privateVar "privateVar"
+OpName %ReadPrivate "ReadPrivate"
+OpName %WritePrivate "WritePrivate"
+OpName %value "value"
+OpDecorate %ReadPrivate LinkageAttributes "ReadPrivate" Export
+OpDecorate %WritePrivate LinkageAttributes "WritePrivate" Export
+%int = OpTypeInt 32 1
+%_ptr_Private_int = OpTypePointer Private %int
+%6 = OpTypeFunction %int
+%void = OpTypeVoid
+%_ptr_Function_int = OpTypePointer Function %int
+%10 = OpTypeFunction %void %_ptr_Function_int
+%privateVar = OpVariable %_ptr_Private_int Private
+%ReadPrivate = OpFunction %int None %6
+%12 = OpLabel
+%8 = OpLoad %int %privateVar
+OpReturnValue %8
+OpFunctionEnd
+%WritePrivate = OpFunction %void None %10
+%value = OpFunctionParameter %_ptr_Function_int
+%13 = OpLabel
+%14 = OpLoad %int %value
+OpStore %privateVar %14
+OpReturn
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ auto result = SinglePassRunAndDisassemble<AggressiveDCEPass>(
+ text, /* skip_nop = */ true, /* do_validation = */ false);
+ EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
+ EXPECT_EQ(text, std::get<0>(result));
+}
+
+TEST_F(AggressiveDCETest, KeepLableNames) {
+ const std::string text = R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpSource HLSL 630
+OpName %WritePrivate "WritePrivate"
+OpName %entry "entry"
+OpName %target "target"
+OpDecorate %WritePrivate LinkageAttributes "WritePrivate" Export
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%WritePrivate = OpFunction %void None %3
+%entry = OpLabel
+OpBranch %target
+%target = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ auto result = SinglePassRunAndDisassemble<AggressiveDCEPass>(
+ text, /* skip_nop = */ true, /* do_validation = */ false);
+ EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
+ EXPECT_EQ(text, std::get<0>(result));
+}
+
+TEST_F(AggressiveDCETest, PreserveInterface) {
+ // Set preserve_interface to true. Verify that unused uniform
+ // constant in entry point interface is not eliminated.
+ const std::string text = R"(OpCapability RayTracingKHR
+OpExtension "SPV_KHR_ray_tracing"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint RayGenerationNV %2 "main" %3 %4
+OpDecorate %3 Location 0
+OpDecorate %4 DescriptorSet 2
+OpDecorate %4 Binding 0
+%void = OpTypeVoid
+%6 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%float = OpTypeFloat 32
+%_ptr_CallableDataNV_float = OpTypePointer CallableDataNV %float
+%3 = OpVariable %_ptr_CallableDataNV_float CallableDataNV
+%13 = OpTypeAccelerationStructureKHR
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%4 = OpVariable %_ptr_UniformConstant_13 UniformConstant
+%2 = OpFunction %void None %6
+%15 = OpLabel
+OpExecuteCallableKHR %uint_0 %3
+OpReturn
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_2);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ auto result = SinglePassRunAndDisassemble<AggressiveDCEPass>(
+ text, /* skip_nop = */ true, /* do_validation = */ false,
+ /* preserve_interface */ true);
+ EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
+ EXPECT_EQ(text, std::get<0>(result));
+}
+
+TEST_F(AggressiveDCETest, EmptyContinueWithConditionalBranch) {
+ 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
+%false = OpConstantFalse %bool
+%2 = OpFunction %void None %4
+%9 = OpLabel
+OpBranch %10
+%10 = OpLabel
+OpLoopMerge %11 %12 None
+OpBranch %13
+%13 = OpLabel
+OpKill
+%12 = OpLabel
+OpBranchConditional %false %10 %10
+%11 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_2);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndCheck<AggressiveDCEPass>(text, text, false);
+}
} // namespace
} // namespace opt
diff --git a/test/opt/assembly_builder.h b/test/opt/assembly_builder.h
index 1673c092..b94e90f5 100644
--- a/test/opt/assembly_builder.h
+++ b/test/opt/assembly_builder.h
@@ -70,7 +70,7 @@ class AssemblyBuilder {
static const uint32_t SPEC_ID_BASE = 200;
public:
- // Initalize a minimal SPIR-V assembly code as the template. The minimal
+ // Initialize a minimal SPIR-V assembly code as the template. The minimal
// module contains an empty main function and some predefined names for the
// main function.
AssemblyBuilder()
@@ -102,7 +102,7 @@ class AssemblyBuilder {
});
}
- // Appends OpName instructions to this builder. Instrcution strings that do
+ // Appends OpName instructions to this builder. Instruction strings that do
// not start with 'OpName ' will be skipped. Returns the references of this
// assembly builder.
AssemblyBuilder& AppendNames(const std::vector<std::string>& vec_asm_code) {
diff --git a/test/opt/block_merge_test.cpp b/test/opt/block_merge_test.cpp
index 140a5c09..9698fed2 100644
--- a/test/opt/block_merge_test.cpp
+++ b/test/opt/block_merge_test.cpp
@@ -90,6 +90,63 @@ OpFunctionEnd
true);
}
+TEST_F(BlockMergeTest, BlockMergeForLinkage) {
+ const std::string before =
+ R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpSource HLSL 630
+OpName %main "main"
+OpName %BaseColor "BaseColor"
+OpName %bb_entry "bb.entry"
+OpName %v "v"
+OpDecorate %main LinkageAttributes "main" Export
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%8 = OpTypeFunction %v4float %_ptr_Function_v4float
+%main = OpFunction %v4float None %8
+%BaseColor = OpFunctionParameter %_ptr_Function_v4float
+%bb_entry = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+%9 = OpLoad %v4float %BaseColor
+OpStore %v %9
+OpBranch %10
+%10 = OpLabel
+%11 = OpLoad %v4float %v
+OpBranch %12
+%12 = OpLabel
+OpReturnValue %11
+OpFunctionEnd
+)";
+
+ const std::string after =
+ R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpSource HLSL 630
+OpName %main "main"
+OpName %BaseColor "BaseColor"
+OpName %bb_entry "bb.entry"
+OpName %v "v"
+OpDecorate %main LinkageAttributes "main" Export
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%8 = OpTypeFunction %v4float %_ptr_Function_v4float
+%main = OpFunction %v4float None %8
+%BaseColor = OpFunctionParameter %_ptr_Function_v4float
+%bb_entry = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+%9 = OpLoad %v4float %BaseColor
+OpStore %v %9
+%11 = OpLoad %v4float %v
+OpReturnValue %11
+OpFunctionEnd
+)";
+ SinglePassRunAndCheck<BlockMergePass>(before, after, true, true);
+}
+
TEST_F(BlockMergeTest, EmptyBlock) {
// Note: SPIR-V hand edited to insert empty block
// after two statements in main.
@@ -827,7 +884,7 @@ TEST_F(BlockMergeTest, MergeHeaders) {
; CHECK-NEXT: [[header]] = OpLabel
; CHECK-NEXT: OpSelectionMerge [[merge:%\w+]]
; CHECK: [[merge]] = OpLabel
-; CHEKC: OpReturn
+; CHECK: OpReturn
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
@@ -1038,6 +1095,112 @@ OpFunctionEnd
SinglePassRunAndCheck<BlockMergePass>(spirv, spirv, true, true);
}
+TEST_F(BlockMergeTest, DebugMerge) {
+ // Verify merge can be done completely, cleanly and validly in presence of
+ // NonSemantic.Shader.DebugInfo.100 instructions
+ const std::string text = R"(
+; CHECK: OpLoopMerge
+; CHECK-NEXT: OpBranch
+; CHECK-NOT: OpBranch
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET
+OpExecutionMode %main OriginUpperLeft
+%5 = OpString "lexblock.hlsl"
+%20 = OpString "float"
+%32 = OpString "main"
+%33 = OpString ""
+%46 = OpString "b"
+%49 = OpString "a"
+%58 = OpString "c"
+%63 = OpString "color"
+OpName %in_var_COLOR "in.var.COLOR"
+OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+OpName %main "main"
+OpDecorate %in_var_COLOR Location 0
+OpDecorate %out_var_SV_TARGET Location 0
+%float = OpTypeFloat 32
+%float_0 = OpConstant %float 0
+%v4float = OpTypeVector %float 4
+%9 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%float_1 = OpConstant %float 1
+%13 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%uint_3 = OpConstant %uint 3
+%uint_0 = OpConstant %uint 0
+%uint_4 = OpConstant %uint 4
+%uint_1 = OpConstant %uint 1
+%uint_5 = OpConstant %uint 5
+%uint_12 = OpConstant %uint 12
+%uint_13 = OpConstant %uint 13
+%uint_20 = OpConstant %uint 20
+%uint_15 = OpConstant %uint 15
+%uint_17 = OpConstant %uint 17
+%uint_16 = OpConstant %uint 16
+%uint_14 = OpConstant %uint 14
+%uint_10 = OpConstant %uint 10
+%65 = OpTypeFunction %void
+%in_var_COLOR = OpVariable %_ptr_Input_v4float Input
+%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+%62 = OpExtInst %void %1 DebugExpression
+%22 = OpExtInst %void %1 DebugTypeBasic %20 %uint_32 %uint_3 %uint_0
+%25 = OpExtInst %void %1 DebugTypeVector %22 %uint_4
+%27 = OpExtInst %void %1 DebugTypeFunction %uint_3 %25 %25
+%28 = OpExtInst %void %1 DebugSource %5
+%29 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %28 %uint_5
+%34 = OpExtInst %void %1 DebugFunction %32 %27 %28 %uint_12 %uint_1 %29 %33 %uint_3 %uint_13
+%37 = OpExtInst %void %1 DebugLexicalBlock %28 %uint_13 %uint_1 %34
+%52 = OpExtInst %void %1 DebugLexicalBlock %28 %uint_15 %uint_12 %37
+%54 = OpExtInst %void %1 DebugLocalVariable %46 %25 %28 %uint_17 %uint_12 %52 %uint_4
+%56 = OpExtInst %void %1 DebugLocalVariable %49 %25 %28 %uint_16 %uint_12 %52 %uint_4
+%59 = OpExtInst %void %1 DebugLocalVariable %58 %25 %28 %uint_14 %uint_10 %37 %uint_4
+%64 = OpExtInst %void %1 DebugLocalVariable %63 %25 %28 %uint_12 %uint_20 %34 %uint_4 %uint_1
+%main = OpFunction %void None %65
+%66 = OpLabel
+%69 = OpLoad %v4float %in_var_COLOR
+%168 = OpExtInst %void %1 DebugValue %64 %69 %62
+%169 = OpExtInst %void %1 DebugScope %37
+OpLine %5 14 10
+%164 = OpExtInst %void %1 DebugValue %59 %9 %62
+OpLine %5 15 3
+OpBranch %150
+%150 = OpLabel
+%165 = OpPhi %v4float %9 %66 %158 %159
+%167 = OpExtInst %void %1 DebugValue %59 %165 %62
+%170 = OpExtInst %void %1 DebugScope %37
+OpLine %5 15 12
+%171 = OpExtInst %void %1 DebugNoScope
+OpLoopMerge %160 %159 None
+OpBranch %151
+%151 = OpLabel
+OpLine %5 16 12
+%162 = OpExtInst %void %1 DebugValue %56 %9 %62
+OpLine %5 17 12
+%163 = OpExtInst %void %1 DebugValue %54 %13 %62
+OpLine %5 18 15
+%158 = OpFAdd %v4float %165 %13
+OpLine %5 18 5
+%166 = OpExtInst %void %1 DebugValue %59 %158 %62
+%172 = OpExtInst %void %1 DebugScope %37
+OpLine %5 19 3
+OpBranch %159
+%159 = OpLabel
+OpLine %5 19 3
+OpBranch %150
+%160 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<BlockMergePass>(text, true);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// More complex control flow
diff --git a/test/opt/ccp_test.cpp b/test/opt/ccp_test.cpp
index ef734353..ae7043b9 100644
--- a/test/opt/ccp_test.cpp
+++ b/test/opt/ccp_test.cpp
@@ -1208,6 +1208,112 @@ TEST_F(CCPTest, CCPNoChangeFailureWithUnfoldableInstr) {
auto result = SinglePassRunAndMatch<CCPPass>(text, true);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
}
+
+TEST_F(CCPTest, FunctionDeclaration) {
+ // Make sure the pass works with a function declaration that is called.
+ const std::string text = R"(OpCapability Addresses
+OpCapability Linkage
+OpCapability Kernel
+OpCapability Int8
+%1 = OpExtInstImport "OpenCL.std"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool"
+OpExecutionMode %2 ContractionOff
+OpSource Unknown 0
+OpDecorate %3 LinkageAttributes "julia_error_7712" Import
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%3 = OpFunction %void None %5
+OpFunctionEnd
+%2 = OpFunction %void None %5
+%6 = OpLabel
+%7 = OpFunctionCall %void %3
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<CCPPass>(text, text, false);
+}
+
+// Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/4462.
+// The test was causing a lateral movement in the constant lattice, which was
+// not being detected as varying by CCP. In this test, FClamp is evaluated
+// twice. On the first evaluation, if computes FClamp(0.5, 0.5, -1) which
+// returns -1. On the second evaluation, it computes FClamp(0.5, 0.5, VARYING)
+// which returns 0.5.
+//
+// Both fold() computations are correct given the semantics of FClamp() but
+// this causes a lateral transition in the constant lattice which was not being
+// considered VARYING by CCP.
+TEST_F(CCPTest, LateralLatticeTransition) {
+ const std::string text = R"(OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %gl_FragCoord %outColor
+ OpExecutionMode %main OriginUpperLeft
+ OpSource ESSL 310
+ OpName %main "main"
+ OpName %gl_FragCoord "gl_FragCoord"
+ OpName %outColor "outColor"
+ OpDecorate %gl_FragCoord BuiltIn FragCoord
+ OpDecorate %outColor Location 0
+ %void = OpTypeVoid
+ %6 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %float_0_5 = OpConstant %float 0.5
+ %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+ %uint = OpTypeInt 32 0
+ %uint_0 = OpConstant %uint 0
+%_ptr_Input_float = OpTypePointer Input %float
+ %float_0 = OpConstant %float 0
+ %bool = OpTypeBool
+ %float_n1 = OpConstant %float -1
+ %float_1 = OpConstant %float 1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %outColor = OpVariable %_ptr_Output_v4float Output
+
+; This constant is created during the first evaluation of the CompositeConstruct
+; CHECK: [[new_constant:%\d+]] = OpConstantComposite %v4float %float_n1 %float_0_5 %float_0 %float_1
+
+ %main = OpFunction %void None %6
+ %19 = OpLabel
+ %20 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0
+ %21 = OpLoad %float %20
+ %22 = OpFOrdLessThan %bool %21 %float_0
+ OpSelectionMerge %23 None
+ OpBranchConditional %22 %24 %25
+ %24 = OpLabel
+ OpBranch %23
+ %25 = OpLabel
+ OpBranch %26
+ %26 = OpLabel
+ OpBranch %23
+ %23 = OpLabel
+ %27 = OpPhi %float %float_n1 %24 %float_0_5 %26
+ %28 = OpExtInst %float %1 FClamp %float_0_5 %float_0_5 %27
+
+ ; On first evaluation, the result from FClamp will return 0.5.
+ ; But on second evaluation, FClamp should return VARYING. Check
+ ; that CCP is not keeping the first result.
+ ; CHECK-NOT: %29 = OpCompositeConstruct %v4float %float_0_5 %float_0_5 %float_0 %float_1
+ %29 = OpCompositeConstruct %v4float %28 %float_0_5 %float_0 %float_1
+
+ ; CHECK-NOT: OpCopyObject %v4float [[new_constant]]
+ %42 = OpCopyObject %v4float %29
+
+ ; CHECK-NOT: OpStore %outColor [[new_constant]]
+ OpStore %outColor %42
+
+ OpReturn
+ OpFunctionEnd
+)";
+
+ auto result = SinglePassRunAndMatch<CCPPass>(text, true);
+ EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/code_sink_test.cpp b/test/opt/code_sink_test.cpp
index f1bd1275..bf5029b6 100644
--- a/test/opt/code_sink_test.cpp
+++ b/test/opt/code_sink_test.cpp
@@ -378,7 +378,7 @@ TEST_F(CodeSinkTest, MoveReadOnlyLoadWithSync) {
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_4 = OpConstant %uint 4
-%mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire
+%mem_semantics = OpConstant %uint 0x42 ; Uniform memory arquire
%_arr_uint_uint_4 = OpTypeArray %uint %uint_4
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4
@@ -419,7 +419,7 @@ TEST_F(CodeSinkTest, DontMoveBecauseOfSync) {
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_4 = OpConstant %uint 4
-%mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire
+%mem_semantics = OpConstant %uint 0x42 ; Uniform memory arquire
%_arr_uint_uint_4 = OpTypeStruct %uint
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4
@@ -460,7 +460,7 @@ TEST_F(CodeSinkTest, DontMoveBecauseOfAtomicWithSync) {
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_4 = OpConstant %uint 4
-%mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire
+%mem_semantics = OpConstant %uint 0x42 ; Uniform memory arquire
%_arr_uint_uint_4 = OpTypeStruct %uint
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4
diff --git a/test/opt/combine_access_chains_test.cpp b/test/opt/combine_access_chains_test.cpp
index aed14c9d..5be3ba63 100644
--- a/test/opt/combine_access_chains_test.cpp
+++ b/test/opt/combine_access_chains_test.cpp
@@ -768,6 +768,32 @@ OpFunctionEnd
SinglePassRunAndMatch<CombineAccessChains>(text, true);
}
+TEST_F(CombineAccessChainsTest, FunctionDeclaration) {
+ // Make sure the pass works with a function declaration that is called.
+ const std::string text = R"(OpCapability Addresses
+OpCapability Linkage
+OpCapability Kernel
+OpCapability Int8
+%1 = OpExtInstImport "OpenCL.std"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool"
+OpExecutionMode %2 ContractionOff
+OpSource Unknown 0
+OpDecorate %3 LinkageAttributes "julia_error_7712" Import
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%3 = OpFunction %void None %5
+OpFunctionEnd
+%2 = OpFunction %void None %5
+%6 = OpLabel
+%7 = OpFunctionCall %void %3
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<CombineAccessChains>(text, text, false);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/control_dependence.cpp b/test/opt/control_dependence.cpp
new file mode 100644
index 00000000..46655472
--- /dev/null
+++ b/test/opt/control_dependence.cpp
@@ -0,0 +1,306 @@
+// Copyright (c) 2021 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/opt/control_dependence.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "gmock/gmock-matchers.h"
+#include "gtest/gtest.h"
+#include "source/opt/build_module.h"
+#include "source/opt/cfg.h"
+#include "test/opt/function_utils.h"
+
+namespace spvtools {
+namespace opt {
+
+namespace {
+void GatherEdges(const ControlDependenceAnalysis& cdg,
+ std::vector<ControlDependence>& ret) {
+ cdg.ForEachBlockLabel([&](uint32_t label) {
+ ret.reserve(ret.size() + cdg.GetDependenceTargets(label).size());
+ ret.insert(ret.end(), cdg.GetDependenceTargets(label).begin(),
+ cdg.GetDependenceTargets(label).end());
+ });
+ std::sort(ret.begin(), ret.end());
+ // Verify that reverse graph is the same.
+ std::vector<ControlDependence> reverse_edges;
+ reverse_edges.reserve(ret.size());
+ cdg.ForEachBlockLabel([&](uint32_t label) {
+ reverse_edges.insert(reverse_edges.end(),
+ cdg.GetDependenceSources(label).begin(),
+ cdg.GetDependenceSources(label).end());
+ });
+ std::sort(reverse_edges.begin(), reverse_edges.end());
+ ASSERT_THAT(reverse_edges, testing::ElementsAreArray(ret));
+}
+
+using ControlDependenceTest = ::testing::Test;
+
+TEST(ControlDependenceTest, DependenceSimpleCFG) {
+ const std::string text = R"(
+ OpCapability Addresses
+ OpCapability Kernel
+ OpMemoryModel Physical64 OpenCL
+ OpEntryPoint Kernel %1 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeBool
+ %5 = OpTypeInt 32 0
+ %6 = OpConstant %5 0
+ %7 = OpConstantFalse %4
+ %8 = OpConstantTrue %4
+ %9 = OpConstant %5 1
+ %1 = OpFunction %2 None %3
+ %10 = OpLabel
+ OpBranch %11
+ %11 = OpLabel
+ OpSwitch %6 %12 1 %13
+ %12 = OpLabel
+ OpBranch %14
+ %13 = OpLabel
+ OpBranch %14
+ %14 = OpLabel
+ OpBranchConditional %8 %15 %16
+ %15 = OpLabel
+ OpBranch %19
+ %16 = OpLabel
+ OpBranchConditional %8 %17 %18
+ %17 = OpLabel
+ OpBranch %18
+ %18 = OpLabel
+ OpBranch %19
+ %19 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ // CFG: (all edges pointing downward)
+ // %10
+ // |
+ // %11
+ // / \ (R: %6 == 1, L: default)
+ // %12 %13
+ // \ /
+ // %14
+ // T/ \F
+ // %15 %16
+ // | T/ |F
+ // | %17|
+ // | \ |
+ // | %18
+ // | /
+ // %19
+
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ Module* module = context->module();
+ EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+ << text << std::endl;
+ const Function* fn = spvtest::GetFunction(module, 1);
+ const BasicBlock* entry = spvtest::GetBasicBlock(fn, 10);
+ EXPECT_EQ(entry, fn->entry().get())
+ << "The entry node is not the expected one";
+
+ {
+ PostDominatorAnalysis pdom;
+ const CFG& cfg = *context->cfg();
+ pdom.InitializeTree(cfg, fn);
+ ControlDependenceAnalysis cdg;
+ cdg.ComputeControlDependenceGraph(cfg, pdom);
+
+ // Test HasBlock.
+ for (uint32_t id = 10; id <= 19; id++) {
+ EXPECT_TRUE(cdg.HasBlock(id));
+ }
+ EXPECT_TRUE(cdg.HasBlock(ControlDependenceAnalysis::kPseudoEntryBlock));
+ // Check blocks before/after valid range.
+ EXPECT_FALSE(cdg.HasBlock(5));
+ EXPECT_FALSE(cdg.HasBlock(25));
+ EXPECT_FALSE(cdg.HasBlock(UINT32_MAX));
+
+ // Test ForEachBlockLabel.
+ std::set<uint32_t> block_labels;
+ cdg.ForEachBlockLabel([&block_labels](uint32_t id) {
+ bool inserted = block_labels.insert(id).second;
+ EXPECT_TRUE(inserted); // Should have no duplicates.
+ });
+ EXPECT_THAT(block_labels, testing::ElementsAre(0, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19));
+
+ {
+ // Test WhileEachBlockLabel.
+ uint32_t iters = 0;
+ EXPECT_TRUE(cdg.WhileEachBlockLabel([&iters](uint32_t) {
+ ++iters;
+ return true;
+ }));
+ EXPECT_EQ((uint32_t)block_labels.size(), iters);
+ iters = 0;
+ EXPECT_FALSE(cdg.WhileEachBlockLabel([&iters](uint32_t) {
+ ++iters;
+ return false;
+ }));
+ EXPECT_EQ(1, iters);
+ }
+
+ // Test IsDependent.
+ EXPECT_TRUE(cdg.IsDependent(12, 11));
+ EXPECT_TRUE(cdg.IsDependent(13, 11));
+ EXPECT_TRUE(cdg.IsDependent(15, 14));
+ EXPECT_TRUE(cdg.IsDependent(16, 14));
+ EXPECT_TRUE(cdg.IsDependent(18, 14));
+ EXPECT_TRUE(cdg.IsDependent(17, 16));
+ EXPECT_TRUE(cdg.IsDependent(10, 0));
+ EXPECT_TRUE(cdg.IsDependent(11, 0));
+ EXPECT_TRUE(cdg.IsDependent(14, 0));
+ EXPECT_TRUE(cdg.IsDependent(19, 0));
+ EXPECT_FALSE(cdg.IsDependent(14, 11));
+ EXPECT_FALSE(cdg.IsDependent(17, 14));
+ EXPECT_FALSE(cdg.IsDependent(19, 14));
+ EXPECT_FALSE(cdg.IsDependent(12, 0));
+
+ // Test GetDependenceSources/Targets.
+ std::vector<ControlDependence> edges;
+ GatherEdges(cdg, edges);
+ EXPECT_THAT(edges,
+ testing::ElementsAre(
+ ControlDependence(0, 10), ControlDependence(0, 11, 10),
+ ControlDependence(0, 14, 10), ControlDependence(0, 19, 10),
+ ControlDependence(11, 12), ControlDependence(11, 13),
+ ControlDependence(14, 15), ControlDependence(14, 16),
+ ControlDependence(14, 18, 16), ControlDependence(16, 17)));
+
+ const uint32_t expected_condition_ids[] = {
+ 0, 0, 0, 0, 6, 6, 8, 8, 8, 8,
+ };
+
+ for (uint32_t i = 0; i < edges.size(); i++) {
+ EXPECT_EQ(expected_condition_ids[i], edges[i].GetConditionID(cfg));
+ }
+ }
+}
+
+TEST(ControlDependenceTest, DependencePaperCFG) {
+ const std::string text = R"(
+ OpCapability Addresses
+ OpCapability Kernel
+ OpMemoryModel Physical64 OpenCL
+ OpEntryPoint Kernel %101 "main"
+ %102 = OpTypeVoid
+ %103 = OpTypeFunction %102
+ %104 = OpTypeBool
+ %108 = OpConstantTrue %104
+ %101 = OpFunction %102 None %103
+ %1 = OpLabel
+ OpBranch %2
+ %2 = OpLabel
+ OpBranchConditional %108 %3 %7
+ %3 = OpLabel
+ OpBranchConditional %108 %4 %5
+ %4 = OpLabel
+ OpBranch %6
+ %5 = OpLabel
+ OpBranch %6
+ %6 = OpLabel
+ OpBranch %8
+ %7 = OpLabel
+ OpBranch %8
+ %8 = OpLabel
+ OpBranch %9
+ %9 = OpLabel
+ OpBranchConditional %108 %10 %11
+ %10 = OpLabel
+ OpBranch %11
+ %11 = OpLabel
+ OpBranchConditional %108 %12 %9
+ %12 = OpLabel
+ OpBranchConditional %108 %13 %2
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ // CFG: (edges pointing downward if no arrow)
+ // %1
+ // |
+ // %2 <----+
+ // T/ \F |
+ // %3 \ |
+ // T/ \F \ |
+ // %4 %5 %7 |
+ // \ / / |
+ // %6 / |
+ // \ / |
+ // %8 |
+ // | |
+ // %9 <-+ |
+ // T/ | | |
+ // %10 | | |
+ // \ | | |
+ // %11-F+ |
+ // T| |
+ // %12-F---+
+ // T|
+ // %13
+
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ Module* module = context->module();
+ EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+ << text << std::endl;
+ const Function* fn = spvtest::GetFunction(module, 101);
+ const BasicBlock* entry = spvtest::GetBasicBlock(fn, 1);
+ EXPECT_EQ(entry, fn->entry().get())
+ << "The entry node is not the expected one";
+
+ {
+ PostDominatorAnalysis pdom;
+ const CFG& cfg = *context->cfg();
+ pdom.InitializeTree(cfg, fn);
+ ControlDependenceAnalysis cdg;
+ cdg.ComputeControlDependenceGraph(cfg, pdom);
+
+ std::vector<ControlDependence> edges;
+ GatherEdges(cdg, edges);
+ EXPECT_THAT(
+ edges, testing::ElementsAre(
+ ControlDependence(0, 1), ControlDependence(0, 2, 1),
+ ControlDependence(0, 8, 1), ControlDependence(0, 9, 1),
+ ControlDependence(0, 11, 1), ControlDependence(0, 12, 1),
+ ControlDependence(0, 13, 1), ControlDependence(2, 3),
+ ControlDependence(2, 6, 3), ControlDependence(2, 7),
+ ControlDependence(3, 4), ControlDependence(3, 5),
+ ControlDependence(9, 10), ControlDependence(11, 9),
+ ControlDependence(11, 11, 9), ControlDependence(12, 2),
+ ControlDependence(12, 8, 2), ControlDependence(12, 9, 2),
+ ControlDependence(12, 11, 2), ControlDependence(12, 12, 2)));
+
+ const uint32_t expected_condition_ids[] = {
+ 0, 0, 0, 0, 0, 0, 0, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
+ };
+
+ for (uint32_t i = 0; i < edges.size(); i++) {
+ EXPECT_EQ(expected_condition_ids[i], edges[i].GetConditionID(cfg));
+ }
+ }
+}
+
+} // namespace
+} // namespace opt
+} // namespace spvtools
diff --git a/test/opt/convert_relaxed_to_half_test.cpp b/test/opt/convert_relaxed_to_half_test.cpp
index ca6ee583..6a06de84 100644
--- a/test/opt/convert_relaxed_to_half_test.cpp
+++ b/test/opt/convert_relaxed_to_half_test.cpp
@@ -204,6 +204,98 @@ OpFunctionEnd
defs_after + func_after, true, true);
}
+TEST_F(ConvertToHalfTest, ConvertToHalfForLinkage) {
+ const std::string before =
+ R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpSource HLSL 630
+OpName %type_cbuff "type.cbuff"
+OpMemberName %type_cbuff 0 "c"
+OpName %cbuff "cbuff"
+OpName %main "main"
+OpName %BaseColor "BaseColor"
+OpName %bb_entry "bb.entry"
+OpName %v "v"
+OpDecorate %main LinkageAttributes "main" Export
+OpDecorate %cbuff DescriptorSet 0
+OpDecorate %cbuff Binding 0
+OpMemberDecorate %type_cbuff 0 Offset 0
+OpDecorate %type_cbuff Block
+OpDecorate %18 RelaxedPrecision
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%type_cbuff = OpTypeStruct %float
+%_ptr_Uniform_type_cbuff = OpTypePointer Uniform %type_cbuff
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%9 = OpTypeFunction %v4float %_ptr_Function_v4float
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%cbuff = OpVariable %_ptr_Uniform_type_cbuff Uniform
+%main = OpFunction %v4float None %9
+%BaseColor = OpFunctionParameter %_ptr_Function_v4float
+%bb_entry = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+%14 = OpLoad %v4float %BaseColor
+%16 = OpAccessChain %_ptr_Uniform_float %cbuff %int_0
+%17 = OpLoad %float %16
+%18 = OpVectorTimesScalar %v4float %14 %17
+OpStore %v %18
+%19 = OpLoad %v4float %v
+OpReturnValue %19
+OpFunctionEnd
+)";
+
+ const std::string after =
+ R"(OpCapability Shader
+OpCapability Linkage
+OpCapability Float16
+OpMemoryModel Logical GLSL450
+OpSource HLSL 630
+OpName %type_cbuff "type.cbuff"
+OpMemberName %type_cbuff 0 "c"
+OpName %cbuff "cbuff"
+OpName %main "main"
+OpName %BaseColor "BaseColor"
+OpName %bb_entry "bb.entry"
+OpName %v "v"
+OpDecorate %main LinkageAttributes "main" Export
+OpDecorate %cbuff DescriptorSet 0
+OpDecorate %cbuff Binding 0
+OpMemberDecorate %type_cbuff 0 Offset 0
+OpDecorate %type_cbuff Block
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%type_cbuff = OpTypeStruct %float
+%_ptr_Uniform_type_cbuff = OpTypePointer Uniform %type_cbuff
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%14 = OpTypeFunction %v4float %_ptr_Function_v4float
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%cbuff = OpVariable %_ptr_Uniform_type_cbuff Uniform
+%half = OpTypeFloat 16
+%v4half = OpTypeVector %half 4
+%main = OpFunction %v4float None %14
+%BaseColor = OpFunctionParameter %_ptr_Function_v4float
+%bb_entry = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+%16 = OpLoad %v4float %BaseColor
+%17 = OpAccessChain %_ptr_Uniform_float %cbuff %int_0
+%18 = OpLoad %float %17
+%22 = OpFConvert %v4half %16
+%23 = OpFConvert %half %18
+%7 = OpVectorTimesScalar %v4half %22 %23
+%24 = OpFConvert %v4float %7
+OpStore %v %24
+%19 = OpLoad %v4float %v
+OpReturnValue %19
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<ConvertToHalfPass>(before, after, true, true);
+}
TEST_F(ConvertToHalfTest, ConvertToHalfWithDrefSample) {
// The resulting SPIR-V was processed with --relax-float-ops.
//
@@ -1397,6 +1489,87 @@ TEST_F(ConvertToHalfTest, RemoveRelaxDec) {
EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
}
+TEST_F(ConvertToHalfTest, HandleNonRelaxedPhi) {
+ // See https://github.com/KhronosGroup/SPIRV-Tools/issues/4452
+
+ // This test is a case with a non-relaxed phi with a relaxed operand.
+ // A convert must be inserted at the end of the block associated with
+ // the operand.
+ const std::string test =
+ R"(
+; CHECK: [[fcvt:%\w+]] = OpFConvert %v3float {{%\w+}}
+; CHECK-NEXT: OpSelectionMerge {{%\w+}} None
+; CHECK: {{%\w+}} = OpPhi %v3float [[fcvt]] {{%\w+}} {{%\w+}} {{%\w+}}
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %output_color
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %MaterialParams "MaterialParams"
+ OpMemberName %MaterialParams 0 "foo"
+ OpName %materialParams "materialParams"
+ OpName %output_color "output_color"
+ OpMemberDecorate %MaterialParams 0 Offset 0
+ OpDecorate %MaterialParams Block
+ OpDecorate %materialParams DescriptorSet 0
+ OpDecorate %materialParams Binding 5
+ OpDecorate %output_color Location 0
+ OpDecorate %57 RelaxedPrecision
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v3float = OpTypeVector %float 3
+%MaterialParams = OpTypeStruct %float
+%_ptr_Uniform_MaterialParams = OpTypePointer Uniform %MaterialParams
+%materialParams = OpVariable %_ptr_Uniform_MaterialParams Uniform
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+ %float_0 = OpConstant %float 0
+ %bool = OpTypeBool
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%output_color = OpVariable %_ptr_Output_v4float Output
+ %uint = OpTypeInt 32 0
+ %uint_0 = OpConstant %uint 0
+%_ptr_Output_float = OpTypePointer Output %float
+ %uint_1 = OpConstant %uint 1
+ %uint_2 = OpConstant %uint 2
+ %float_0_5 = OpConstant %float 0.5
+ %61 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %55 = OpAccessChain %_ptr_Uniform_float %materialParams %int_0
+ %56 = OpLoad %float %55
+ %57 = OpCompositeConstruct %v3float %56 %56 %56
+ %31 = OpFOrdGreaterThan %bool %56 %float_0
+ OpSelectionMerge %33 None
+ OpBranchConditional %31 %32 %33
+ %32 = OpLabel
+ %37 = OpFMul %v3float %57 %61
+ OpBranch %33
+ %33 = OpLabel
+ %58 = OpPhi %v3float %57 %5 %37 %32
+ %45 = OpAccessChain %_ptr_Output_float %output_color %uint_0
+ %46 = OpCompositeExtract %float %58 0
+ OpStore %45 %46
+ %48 = OpAccessChain %_ptr_Output_float %output_color %uint_1
+ %49 = OpCompositeExtract %float %58 1
+ OpStore %48 %49
+ %51 = OpAccessChain %_ptr_Output_float %output_color %uint_2
+ %52 = OpCompositeExtract %float %58 2
+ OpStore %51 %52
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ auto result = SinglePassRunAndMatch<ConvertToHalfPass>(test, true);
+ EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/convert_to_sampled_image_test.cpp b/test/opt/convert_to_sampled_image_test.cpp
new file mode 100644
index 00000000..37f65601
--- /dev/null
+++ b/test/opt/convert_to_sampled_image_test.cpp
@@ -0,0 +1,353 @@
+// Copyright (c) 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "source/opt/convert_to_sampled_image_pass.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using testing::Eq;
+using VectorOfDescriptorSetAndBindingPairs =
+ std::vector<DescriptorSetAndBinding>;
+
+struct DescriptorSetAndBindingStringParsingTestCase {
+ const char* descriptor_set_binding_str;
+ bool expect_success;
+ VectorOfDescriptorSetAndBindingPairs expected_descriptor_set_binding_pairs;
+};
+
+using DescriptorSetAndBindingStringParsingTest =
+ ::testing::TestWithParam<DescriptorSetAndBindingStringParsingTestCase>;
+
+TEST_P(DescriptorSetAndBindingStringParsingTest, TestCase) {
+ const auto& tc = GetParam();
+ auto actual_descriptor_set_binding_pairs =
+ ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString(
+ tc.descriptor_set_binding_str);
+ if (tc.expect_success) {
+ EXPECT_NE(nullptr, actual_descriptor_set_binding_pairs);
+ if (actual_descriptor_set_binding_pairs) {
+ EXPECT_THAT(*actual_descriptor_set_binding_pairs,
+ Eq(tc.expected_descriptor_set_binding_pairs));
+ }
+ } else {
+ EXPECT_EQ(nullptr, actual_descriptor_set_binding_pairs);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ValidString, DescriptorSetAndBindingStringParsingTest,
+ ::testing::ValuesIn(std::vector<
+ DescriptorSetAndBindingStringParsingTestCase>{
+ // 0. empty vector
+ {"", true, VectorOfDescriptorSetAndBindingPairs({})},
+ // 1. one pair
+ {"100:1024", true,
+ VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100,
+ 1024}})},
+ // 2. two pairs
+ {"100:1024 200:2048", true,
+ VectorOfDescriptorSetAndBindingPairs(
+ {DescriptorSetAndBinding{100, 1024},
+ DescriptorSetAndBinding{200, 2048}})},
+ // 3. spaces between entries
+ {"100:1024 \n \r \t \v \f 200:2048", true,
+ VectorOfDescriptorSetAndBindingPairs(
+ {DescriptorSetAndBinding{100, 1024},
+ DescriptorSetAndBinding{200, 2048}})},
+ // 4. \t, \n, \r and spaces before spec id
+ {" \n \r\t \t \v \f 100:1024", true,
+ VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100,
+ 1024}})},
+ // 5. \t, \n, \r and spaces after value string
+ {"100:1024 \n \r\t \t \v \f ", true,
+ VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{100,
+ 1024}})},
+ // 6. maximum spec id
+ {"4294967295:0", true,
+ VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{
+ 4294967295, 0}})},
+ // 7. minimum spec id
+ {"0:100", true,
+ VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{0,
+ 100}})},
+ // 8. multiple entries
+ {"101:1 102:2 103:3 104:4 200:201 9999:1000", true,
+ VectorOfDescriptorSetAndBindingPairs(
+ {DescriptorSetAndBinding{101, 1}, DescriptorSetAndBinding{102, 2},
+ DescriptorSetAndBinding{103, 3}, DescriptorSetAndBinding{104, 4},
+ DescriptorSetAndBinding{200, 201},
+ DescriptorSetAndBinding{9999, 1000}})},
+ }));
+
+INSTANTIATE_TEST_SUITE_P(
+ InvalidString, DescriptorSetAndBindingStringParsingTest,
+ ::testing::ValuesIn(
+ std::vector<DescriptorSetAndBindingStringParsingTestCase>{
+ // 0. missing default value
+ {"100:", false, VectorOfDescriptorSetAndBindingPairs{}},
+ // 1. descriptor set is not an integer
+ {"100.0:200", false, VectorOfDescriptorSetAndBindingPairs{}},
+ // 2. descriptor set is not a number
+ {"something_not_a_number:1", false,
+ VectorOfDescriptorSetAndBindingPairs{}},
+ // 3. only descriptor set number
+ {"100", false, VectorOfDescriptorSetAndBindingPairs{}},
+ // 4. empty descriptor set
+ {":3", false, VectorOfDescriptorSetAndBindingPairs{}},
+ // 5. only colon
+ {":", false, VectorOfDescriptorSetAndBindingPairs{}},
+ // 6. descriptor set overflow
+ {"4294967296:200", false, VectorOfDescriptorSetAndBindingPairs{}},
+ // 7. descriptor set less than 0
+ {"-1:200", false, VectorOfDescriptorSetAndBindingPairs{}},
+ // 8. nullptr
+ {nullptr, false, VectorOfDescriptorSetAndBindingPairs{}},
+ // 9. only a number is invalid
+ {"1234", false, VectorOfDescriptorSetAndBindingPairs{}},
+ // 10. invalid entry separator
+ {"12:34;23:14", false, VectorOfDescriptorSetAndBindingPairs{}},
+ // 11. invalid descriptor set and default value separator
+ {"12@34", false, VectorOfDescriptorSetAndBindingPairs{}},
+ // 12. spaces before colon
+ {"100 :1024", false, VectorOfDescriptorSetAndBindingPairs{}},
+ // 13. spaces after colon
+ {"100: 1024", false, VectorOfDescriptorSetAndBindingPairs{}},
+ // 14. descriptor set represented in hex float format is invalid
+ {"0x3p10:200", false, VectorOfDescriptorSetAndBindingPairs{}},
+ }));
+
+std::string BuildShader(const char* shader_decorate_instructions,
+ const char* shader_image_and_sampler_variables,
+ const char* shader_body) {
+ // Base HLSL code:
+ //
+ // SamplerState sam : register(s2);
+ // Texture2D <float4> texture : register(t5);
+ //
+ // float4 main() : SV_TARGET {
+ // return texture.SampleLevel(sam, float2(1, 2), 10, 2);
+ // }
+ std::stringstream ss;
+ ss << R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %out_var_SV_TARGET
+ OpExecutionMode %main OriginUpperLeft
+ OpSource HLSL 600
+ OpName %type_sampler "type.sampler"
+ OpName %type_2d_image "type.2d.image"
+ OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+ OpName %main "main"
+ OpName %type_sampled_image "type.sampled.image"
+ OpDecorate %out_var_SV_TARGET Location 0
+ )";
+ ss << shader_decorate_instructions;
+ ss << R"(
+ %float = OpTypeFloat 32
+ %float_1 = OpConstant %float 1
+ %float_2 = OpConstant %float 2
+ %v2float = OpTypeVector %float 2
+ %12 = OpConstantComposite %v2float %float_1 %float_2
+ %float_10 = OpConstant %float 10
+ %int = OpTypeInt 32 1
+ %int_2 = OpConstant %int 2
+ %v2int = OpTypeVector %int 2
+ %17 = OpConstantComposite %v2int %int_2 %int_2
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %23 = OpTypeFunction %void
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+ )";
+ ss << shader_image_and_sampler_variables;
+ ss << R"(
+%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+ %main = OpFunction %void None %23
+ %24 = OpLabel
+ )";
+ ss << shader_body;
+ ss << R"(
+ OpReturn
+ OpFunctionEnd
+ )";
+ return ss.str();
+}
+
+using ConvertToSampledImageTest = PassTest<::testing::Test>;
+
+TEST_F(ConvertToSampledImageTest, Texture2DAndSamplerToSampledImage) {
+ const std::string shader = BuildShader(
+ R"(
+ OpDecorate %sam DescriptorSet 0
+ OpDecorate %sam Binding 5
+ OpDecorate %texture DescriptorSet 0
+ OpDecorate %texture Binding 5
+ )",
+ R"(
+ ; CHECK-NOT: OpVariable %_ptr_UniformConstant_type_2d_image
+
+ ; CHECK: [[tex:%\w+]] = OpVariable %_ptr_UniformConstant_type_sampled_image UniformConstant
+ %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+ %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ )",
+ R"(
+ ; CHECK: [[load:%\w+]] = OpLoad %type_sampled_image [[tex]]
+ ; CHECK: OpImageSampleExplicitLod %v4float [[load]]
+ %25 = OpLoad %type_2d_image %texture
+ %26 = OpLoad %type_sampler %sam
+ %27 = OpSampledImage %type_sampled_image %25 %26
+ %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17
+ OpStore %out_var_SV_TARGET %28
+ )");
+
+ auto result = SinglePassRunAndMatch<ConvertToSampledImagePass>(
+ shader, /* do_validate = */ true,
+ VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}});
+
+ EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(ConvertToSampledImageTest, Texture2DToSampledImage) {
+ const std::string shader = BuildShader(
+ R"(
+ OpDecorate %sam DescriptorSet 0
+ OpDecorate %sam Binding 2
+ OpDecorate %texture DescriptorSet 0
+ OpDecorate %texture Binding 5
+ )",
+ R"(
+ ; CHECK: [[tex:%\w+]] = OpVariable %_ptr_UniformConstant_type_sampled_image UniformConstant
+ %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+ %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ )",
+ R"(
+ ; CHECK: [[load:%\w+]] = OpLoad %type_sampled_image [[tex]]
+ ; CHECK: [[image_extraction:%\w+]] = OpImage %type_2d_image [[load]]
+ ; CHECK: OpSampledImage %type_sampled_image [[image_extraction]]
+ %25 = OpLoad %type_2d_image %texture
+ %26 = OpLoad %type_sampler %sam
+ %27 = OpSampledImage %type_sampled_image %25 %26
+ %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17
+ OpStore %out_var_SV_TARGET %28
+ )");
+
+ auto result = SinglePassRunAndMatch<ConvertToSampledImagePass>(
+ shader, /* do_validate = */ true,
+ VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}});
+
+ EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(ConvertToSampledImageTest, SamplerToSampledImage) {
+ const std::string shader = BuildShader(
+ R"(
+ OpDecorate %sam DescriptorSet 0
+ OpDecorate %sam Binding 2
+ OpDecorate %texture DescriptorSet 0
+ OpDecorate %texture Binding 5
+ )",
+ R"(
+ %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+ %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ )",
+ R"(
+ %25 = OpLoad %type_2d_image %texture
+ %26 = OpLoad %type_sampler %sam
+ %27 = OpSampledImage %type_sampled_image %25 %26
+ %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17
+ OpStore %out_var_SV_TARGET %28
+ )");
+
+ auto result = SinglePassRunToBinary<ConvertToSampledImagePass>(
+ shader, /* skip_nop = */ false,
+ VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 2}});
+
+ EXPECT_EQ(std::get<1>(result), Pass::Status::Failure);
+}
+
+TEST_F(ConvertToSampledImageTest, TwoImagesWithDuplicatedDescriptorSetBinding) {
+ const std::string shader = BuildShader(
+ R"(
+ OpDecorate %sam DescriptorSet 0
+ OpDecorate %sam Binding 2
+ OpDecorate %texture0 DescriptorSet 0
+ OpDecorate %texture0 Binding 5
+ OpDecorate %texture1 DescriptorSet 0
+ OpDecorate %texture1 Binding 5
+ )",
+ R"(
+ %sam = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+ %texture0 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ %texture1 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ )",
+ R"(
+ %25 = OpLoad %type_2d_image %texture0
+ %26 = OpLoad %type_sampler %sam
+ %27 = OpSampledImage %type_sampled_image %25 %26
+ %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17
+ OpStore %out_var_SV_TARGET %28
+ )");
+
+ auto result = SinglePassRunToBinary<ConvertToSampledImagePass>(
+ shader, /* skip_nop = */ false,
+ VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 5}});
+
+ EXPECT_EQ(std::get<1>(result), Pass::Status::Failure);
+}
+
+TEST_F(ConvertToSampledImageTest,
+ TwoSamplersWithDuplicatedDescriptorSetBinding) {
+ const std::string shader = BuildShader(
+ R"(
+ OpDecorate %sam0 DescriptorSet 0
+ OpDecorate %sam0 Binding 2
+ OpDecorate %sam1 DescriptorSet 0
+ OpDecorate %sam1 Binding 2
+ OpDecorate %texture DescriptorSet 0
+ OpDecorate %texture Binding 5
+ )",
+ R"(
+ %sam0 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+ %sam1 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+ %texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ )",
+ R"(
+ %25 = OpLoad %type_2d_image %texture
+ %26 = OpLoad %type_sampler %sam0
+ %27 = OpSampledImage %type_sampled_image %25 %26
+ %28 = OpImageSampleExplicitLod %v4float %27 %12 Lod|ConstOffset %float_10 %17
+ OpStore %out_var_SV_TARGET %28
+ )");
+
+ auto result = SinglePassRunToBinary<ConvertToSampledImagePass>(
+ shader, /* skip_nop = */ false,
+ VectorOfDescriptorSetAndBindingPairs{DescriptorSetAndBinding{0, 2}});
+
+ EXPECT_EQ(std::get<1>(result), Pass::Status::Failure);
+}
+
+} // namespace
+} // namespace opt
+} // namespace spvtools
diff --git a/test/opt/copy_prop_array_test.cpp b/test/opt/copy_prop_array_test.cpp
index 72bc7f6f..a4599f0f 100644
--- a/test/opt/copy_prop_array_test.cpp
+++ b/test/opt/copy_prop_array_test.cpp
@@ -1814,6 +1814,31 @@ OpFunctionEnd
SinglePassRunAndMatch<CopyPropagateArrays>(before, false);
}
+TEST_F(CopyPropArrayPassTest, FunctionDeclaration) {
+ // Make sure the pass works with a function declaration that is called.
+ const std::string text = R"(OpCapability Addresses
+OpCapability Linkage
+OpCapability Kernel
+OpCapability Int8
+%1 = OpExtInstImport "OpenCL.std"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool"
+OpExecutionMode %2 ContractionOff
+OpSource Unknown 0
+OpDecorate %3 LinkageAttributes "julia_error_7712" Import
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%3 = OpFunction %void None %5
+OpFunctionEnd
+%2 = OpFunction %void None %5
+%6 = OpLabel
+%7 = OpFunctionCall %void %3
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<CopyPropagateArrays>(text, text, false);
+}
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/dataflow.cpp b/test/opt/dataflow.cpp
new file mode 100644
index 00000000..4742015a
--- /dev/null
+++ b/test/opt/dataflow.cpp
@@ -0,0 +1,225 @@
+// Copyright (c) 2021 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/opt/dataflow.h"
+
+#include <map>
+#include <set>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "opt/function_utils.h"
+#include "source/opt/build_module.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using DataFlowTest = ::testing::Test;
+
+// Simple analyses for testing:
+
+// Stores the result IDs of visited instructions in visit order.
+struct VisitOrder : public ForwardDataFlowAnalysis {
+ std::vector<uint32_t> visited_result_ids;
+
+ VisitOrder(IRContext& context, LabelPosition label_position)
+ : ForwardDataFlowAnalysis(context, label_position) {}
+
+ VisitResult Visit(Instruction* inst) override {
+ if (inst->HasResultId()) {
+ visited_result_ids.push_back(inst->result_id());
+ }
+ return DataFlowAnalysis::VisitResult::kResultFixed;
+ }
+};
+
+// For each block, stores the set of blocks it can be preceded by.
+// For example, with the following CFG:
+// V-----------.
+// -> 11 -> 12 -> 13 -> 15
+// \-> 14 ---^
+//
+// The answer is:
+// 11: 11, 12, 13
+// 12: 11, 12, 13
+// 13: 11, 12, 13
+// 14: 11, 12, 13
+// 15: 11, 12, 13, 14
+struct BackwardReachability : public ForwardDataFlowAnalysis {
+ std::map<uint32_t, std::set<uint32_t>> reachable_from;
+
+ BackwardReachability(IRContext& context)
+ : ForwardDataFlowAnalysis(
+ context, ForwardDataFlowAnalysis::LabelPosition::kLabelsOnly) {}
+
+ VisitResult Visit(Instruction* inst) override {
+ // Conditional branches can be enqueued from labels, so skip them.
+ if (inst->opcode() != SpvOpLabel)
+ return DataFlowAnalysis::VisitResult::kResultFixed;
+ uint32_t id = inst->result_id();
+ VisitResult ret = DataFlowAnalysis::VisitResult::kResultFixed;
+ std::set<uint32_t>& precedents = reachable_from[id];
+ for (uint32_t pred : context().cfg()->preds(id)) {
+ bool pred_inserted = precedents.insert(pred).second;
+ if (pred_inserted) {
+ ret = DataFlowAnalysis::VisitResult::kResultChanged;
+ }
+ for (uint32_t block : reachable_from[pred]) {
+ bool inserted = precedents.insert(block).second;
+ if (inserted) {
+ ret = DataFlowAnalysis::VisitResult::kResultChanged;
+ }
+ }
+ }
+ return ret;
+ }
+
+ void InitializeWorklist(Function* function,
+ bool is_first_iteration) override {
+ // Since successor function is exact, only need one pass.
+ if (is_first_iteration) {
+ ForwardDataFlowAnalysis::InitializeWorklist(function, true);
+ }
+ }
+};
+
+TEST_F(DataFlowTest, ReversePostOrder) {
+ // Note: labels and IDs are intentionally out of order.
+ //
+ // CFG: (order of branches is from bottom to top)
+ // V-----------.
+ // -> 50 -> 40 -> 20 -> 60 -> 70
+ // \-> 30 ---^
+
+ // DFS tree with RPO numbering:
+ // -> 50[0] -> 40[1] -> 20[2] 60[4] -> 70[5]
+ // \-> 30[3] ---^
+
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ OpSource GLSL 430
+ %3 = OpTypeVoid
+ %4 = OpTypeFunction %3
+ %6 = OpTypeBool
+ %5 = OpConstantTrue %6
+ %2 = OpFunction %3 None %4
+ %50 = OpLabel
+ %51 = OpUndef %6
+ %52 = OpUndef %6
+ OpBranch %40
+ %70 = OpLabel
+ %69 = OpUndef %6
+ OpReturn
+ %60 = OpLabel
+ %61 = OpUndef %6
+ OpBranchConditional %5 %70 %40
+ %30 = OpLabel
+ %29 = OpUndef %6
+ OpBranch %60
+ %20 = OpLabel
+ %21 = OpUndef %6
+ OpBranch %60
+ %40 = OpLabel
+ %39 = OpUndef %6
+ OpBranchConditional %5 %30 %20
+ OpFunctionEnd
+ )";
+
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ASSERT_NE(context, nullptr);
+
+ Function* function = spvtest::GetFunction(context->module(), 2);
+
+ std::map<ForwardDataFlowAnalysis::LabelPosition, std::vector<uint32_t>>
+ expected_order;
+ expected_order[ForwardDataFlowAnalysis::LabelPosition::kLabelsOnly] = {
+ 50, 40, 20, 30, 60, 70,
+ };
+ expected_order[ForwardDataFlowAnalysis::LabelPosition::kLabelsAtBeginning] = {
+ 50, 51, 52, 40, 39, 20, 21, 30, 29, 60, 61, 70, 69,
+ };
+ expected_order[ForwardDataFlowAnalysis::LabelPosition::kLabelsAtEnd] = {
+ 51, 52, 50, 39, 40, 21, 20, 29, 30, 61, 60, 69, 70,
+ };
+ expected_order[ForwardDataFlowAnalysis::LabelPosition::kNoLabels] = {
+ 51, 52, 39, 21, 29, 61, 69,
+ };
+
+ for (const auto& test_case : expected_order) {
+ VisitOrder analysis(*context, test_case.first);
+ analysis.Run(function);
+ EXPECT_EQ(test_case.second, analysis.visited_result_ids);
+ }
+}
+
+TEST_F(DataFlowTest, BackwardReachability) {
+ // CFG:
+ // V-----------.
+ // -> 11 -> 12 -> 13 -> 15
+ // \-> 14 ---^
+
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ OpSource GLSL 430
+ %3 = OpTypeVoid
+ %4 = OpTypeFunction %3
+ %6 = OpTypeBool
+ %5 = OpConstantTrue %6
+ %2 = OpFunction %3 None %4
+ %11 = OpLabel
+ OpBranch %12
+ %12 = OpLabel
+ OpBranchConditional %5 %14 %13
+ %13 = OpLabel
+ OpBranchConditional %5 %15 %11
+ %14 = OpLabel
+ OpBranch %15
+ %15 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ASSERT_NE(context, nullptr);
+
+ Function* function = spvtest::GetFunction(context->module(), 2);
+
+ BackwardReachability analysis(*context);
+ analysis.Run(function);
+
+ std::map<uint32_t, std::set<uint32_t>> expected_result;
+ expected_result[11] = {11, 12, 13};
+ expected_result[12] = {11, 12, 13};
+ expected_result[13] = {11, 12, 13};
+ expected_result[14] = {11, 12, 13};
+ expected_result[15] = {11, 12, 13, 14};
+ EXPECT_EQ(expected_result, analysis.reachable_from);
+}
+
+} // namespace
+} // namespace opt
+} // namespace spvtools
diff --git a/test/opt/dead_branch_elim_test.cpp b/test/opt/dead_branch_elim_test.cpp
index f89befb4..b04c8f5e 100644
--- a/test/opt/dead_branch_elim_test.cpp
+++ b/test/opt/dead_branch_elim_test.cpp
@@ -1613,7 +1613,7 @@ OpBranch %7
%11 = OpLogicalOr %bool %true %false
OpBranch %7
%7 = OpLabel
-; This phi is in a block preceeding the merge %14!
+; This phi is in a block preceding the merge %14!
%8 = OpPhi %bool %10 %5 %11 %6
OpBranch %14
%14 = OpLabel
@@ -3403,6 +3403,71 @@ OpFunctionEnd
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
}
+TEST_F(DeadBranchElimTest, DontTransferDecorations) {
+ // When replacing %4 with %14, we don't want %14 to inherit %4's decorations.
+ const std::string text = R"(
+; CHECK-NOT: OpDecorate {{%\w+}} RelaxedPrecision
+; CHECK: [[div:%\w+]] = OpFDiv
+; CHECK: {{%\w+}} = OpCopyObject %float [[div]]
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ %3 = OpString "STEVEN"
+ OpDecorate %4 RelaxedPrecision
+ %float = OpTypeFloat 32
+ %uint = OpTypeInt 32 0
+ %void = OpTypeVoid
+ %float_1 = OpConstant %float 1
+ %uint_0 = OpConstant %uint 0
+ %10 = OpTypeFunction %void
+ %2 = OpFunction %void None %10
+ %11 = OpLabel
+ OpSelectionMerge %12 None
+ OpSwitch %uint_0 %13
+ %13 = OpLabel
+ %14 = OpFDiv %float %float_1 %float_1
+ OpLine %3 0 0
+ OpBranch %12
+ %15 = OpLabel
+ OpBranch %12
+ %12 = OpLabel
+ %4 = OpPhi %float %float_1 %15 %14 %13
+ %16 = OpCopyObject %float %4
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
+}
+
+TEST_F(DeadBranchElimTest, FunctionDeclaration) {
+ // Make sure the pass works with a function declaration that is called.
+ const std::string text = R"(OpCapability Addresses
+OpCapability Linkage
+OpCapability Kernel
+OpCapability Int8
+%1 = OpExtInstImport "OpenCL.std"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool"
+OpExecutionMode %2 ContractionOff
+OpSource Unknown 0
+OpDecorate %3 LinkageAttributes "julia_error_7712" Import
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%3 = OpFunction %void None %5
+OpFunctionEnd
+%2 = OpFunction %void None %5
+%6 = OpLabel
+%7 = OpFunctionCall %void %3
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<DeadBranchElimPass>(text, text, false);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// More complex control flow
diff --git a/test/opt/dead_insert_elim_test.cpp b/test/opt/dead_insert_elim_test.cpp
index 9ea948a4..268e6590 100644
--- a/test/opt/dead_insert_elim_test.cpp
+++ b/test/opt/dead_insert_elim_test.cpp
@@ -170,6 +170,72 @@ OpFunctionEnd
after_predefs + after, true, true);
}
+TEST_F(DeadInsertElimTest, DeadInsertForLinkage) {
+ const std::string before =
+ R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpSource HLSL 630
+OpName %main "main"
+OpName %BaseColor "BaseColor"
+OpName %bb_entry "bb.entry"
+OpName %v "v"
+OpDecorate %main LinkageAttributes "main" Export
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%float_0 = OpConstant %float 0
+%v2float = OpTypeVector %float 2
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%14 = OpTypeFunction %v2float %_ptr_Function_v2float
+%_ptr_Function_float = OpTypePointer Function %float
+%main = OpFunction %v2float None %14
+%BaseColor = OpFunctionParameter %_ptr_Function_v2float
+%bb_entry = OpLabel
+%v = OpVariable %_ptr_Function_v2float Function
+%16 = OpLoad %v2float %v
+%17 = OpAccessChain %_ptr_Function_float %BaseColor %int_1
+%18 = OpLoad %float %17
+%19 = OpCompositeInsert %v2float %18 %16 0
+%20 = OpCompositeInsert %v2float %float_0 %19 0
+OpReturnValue %20
+OpFunctionEnd
+)";
+ const std::string after =
+ R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpSource HLSL 630
+OpName %main "main"
+OpName %BaseColor "BaseColor"
+OpName %bb_entry "bb.entry"
+OpName %v "v"
+OpDecorate %main LinkageAttributes "main" Export
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%float_0 = OpConstant %float 0
+%v2float = OpTypeVector %float 2
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%14 = OpTypeFunction %v2float %_ptr_Function_v2float
+%_ptr_Function_float = OpTypePointer Function %float
+%main = OpFunction %v2float None %14
+%BaseColor = OpFunctionParameter %_ptr_Function_v2float
+%bb_entry = OpLabel
+%v = OpVariable %_ptr_Function_v2float Function
+%16 = OpLoad %v2float %v
+%20 = OpCompositeInsert %v2float %float_0 %16 0
+OpReturnValue %20
+OpFunctionEnd
+)";
+ SinglePassRunAndCheck<DeadInsertElimPass>(before, after, true, true);
+}
+
TEST_F(DeadInsertElimTest, DeadInsertInChainWithPhi) {
// Dead insert eliminated with phi in insertion chain.
//
diff --git a/test/opt/debug_info_manager_test.cpp b/test/opt/debug_info_manager_test.cpp
index 49407fd5..e87d0bea 100644
--- a/test/opt/debug_info_manager_test.cpp
+++ b/test/opt/debug_info_manager_test.cpp
@@ -185,6 +185,122 @@ void main(float in_var_color : COLOR) {
100U);
}
+TEST(DebugInfoManager, CreateDebugInlinedAtWithConstantManager) {
+ // Show that CreateDebugInlinedAt will use the Constant manager to generate
+ // its line operand if the Constant and DefUse managers are valid. This is
+ // proven by checking that the id for the line operand 7 is the same as the
+ // existing constant 7.
+ //
+ // int function1() {
+ // return 1;
+ // }
+ //
+ // void main() {
+ // function1();
+ // }
+ const std::string text = R"(OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+%3 = OpString "parent3.hlsl"
+%8 = OpString "int"
+%19 = OpString "function1"
+%20 = 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_2 = OpConstant %uint 2
+%uint_17 = OpConstant %uint 17
+%uint_6 = OpConstant %uint 6
+%uint_13 = OpConstant %uint 13
+%100 = OpConstant %uint 7
+%31 = OpTypeFunction %void
+%42 = OpTypeFunction %int
+%10 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 %uint_4 %uint_0
+%13 = OpExtInst %void %1 DebugTypeFunction %uint_3 %10
+%15 = OpExtInst %void %1 DebugSource %3
+%16 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %15 %uint_5
+%21 = OpExtInst %void %1 DebugFunction %19 %13 %15 %uint_2 %uint_1 %16 %20 %uint_3 %uint_2
+%23 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_2 %uint_17 %21
+%25 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void
+%27 = OpExtInst %void %1 DebugFunction %26 %25 %15 %uint_6 %uint_1 %16 %20 %uint_3 %uint_6
+%29 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_6 %uint_13 %27
+%main = OpFunction %void None %31
+%32 = OpLabel
+%33 = OpFunctionCall %void %src_main
+OpLine %3 8 1
+OpReturn
+OpFunctionEnd
+OpLine %3 6 1
+%src_main = OpFunction %void None %31
+OpNoLine
+%bb_entry = OpLabel
+%47 = OpExtInst %void %1 DebugScope %27
+%37 = OpExtInst %void %1 DebugFunctionDefinition %27 %src_main
+%48 = OpExtInst %void %1 DebugScope %29
+OpLine %3 7 3
+%39 = OpFunctionCall %int %function1
+%49 = OpExtInst %void %1 DebugScope %27
+OpLine %3 8 1
+OpReturn
+%50 = OpExtInst %void %1 DebugNoScope
+OpFunctionEnd
+OpLine %3 2 1
+%function1 = OpFunction %int None %42
+OpNoLine
+%bb_entry_0 = OpLabel
+%51 = OpExtInst %void %1 DebugScope %21
+%45 = OpExtInst %void %1 DebugFunctionDefinition %21 %function1
+%52 = OpExtInst %void %1 DebugScope %23
+OpLine %3 3 3
+OpReturnValue %int_1
+%53 = OpExtInst %void %1 DebugNoScope
+OpFunctionEnd
+ )";
+
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ const uint32_t line_number = 7U;
+ Instruction line(context.get(), SpvOpLine);
+ line.SetInOperands({
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {5U}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {0U}},
+ });
+
+ DebugScope scope(29U, 0U);
+
+ auto db_manager = context.get()->get_debug_info_mgr();
+ auto du_manager = context.get()->get_def_use_mgr();
+ auto c_manager = context.get()->get_constant_mgr();
+
+ (void)du_manager;
+ (void)c_manager;
+
+ uint32_t inlined_at_id = db_manager->CreateDebugInlinedAt(&line, scope);
+ auto* inlined_at = db_manager->GetDebugInlinedAt(inlined_at_id);
+ EXPECT_NE(inlined_at, nullptr);
+ EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex),
+ 100);
+}
+
TEST(DebugInfoManager, GetDebugInfoNone) {
const std::string text = R"(
OpCapability Shader
diff --git a/test/opt/decoration_manager_test.cpp b/test/opt/decoration_manager_test.cpp
index fcfbff06..c9fabe78 100644
--- a/test/opt/decoration_manager_test.cpp
+++ b/test/opt/decoration_manager_test.cpp
@@ -118,7 +118,7 @@ class DecorationManagerTest : public ::testing::Test {
TEST_F(DecorationManagerTest,
ComparingDecorationsWithDiffOpcodesDecorateDecorateId) {
IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
- // This parameter can be interprated both as { SpvDecorationConstant }
+ // This parameter can be interpreted both as { SpvDecorationConstant }
// and also as a list of IDs: { 22 }
const std::vector<uint32_t> param{SpvDecorationConstant};
// OpDecorate %1 Constant
@@ -137,7 +137,7 @@ TEST_F(DecorationManagerTest,
TEST_F(DecorationManagerTest,
ComparingDecorationsWithDiffOpcodesDecorateDecorateString) {
IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer());
- // This parameter can be interprated both as { SpvDecorationConstant }
+ // This parameter can be interpreted both as { SpvDecorationConstant }
// and also as a null-terminated string with a single character with value 22.
const std::vector<uint32_t> param{SpvDecorationConstant};
// OpDecorate %1 Constant
diff --git a/test/opt/def_use_test.cpp b/test/opt/def_use_test.cpp
index cfdad74a..0210095d 100644
--- a/test/opt/def_use_test.cpp
+++ b/test/opt/def_use_test.cpp
@@ -1656,7 +1656,7 @@ INSTANTIATE_TEST_SUITE_P(
"OpGroupDecorate %1 %2 %3",
},
},
- // memeber decorate
+ // member decorate
{
// code
"OpMemberDecorate %1 0 RelaxedPrecision "
@@ -1707,9 +1707,10 @@ TEST_F(UpdateUsesTest, KeepOldUses) {
def->SetInOperands({{SPV_OPERAND_TYPE_ID, {25}}});
context->UpdateDefUse(def);
- auto users = def_use_mgr->id_to_users();
- UserEntry entry = {def, use};
- EXPECT_THAT(users, Contains(entry));
+ auto scanUser = [&](Instruction* user) { return user != use; };
+ bool userFound = !def_use_mgr->WhileEachUser(def, scanUser);
+
+ EXPECT_TRUE(userFound);
}
// clang-format on
diff --git a/test/opt/desc_sroa_test.cpp b/test/opt/desc_sroa_test.cpp
index b35ad474..91c950e8 100644
--- a/test/opt/desc_sroa_test.cpp
+++ b/test/opt/desc_sroa_test.cpp
@@ -770,6 +770,156 @@ TEST_F(DescriptorScalarReplacementTest, BindingForResourceArrayOfStructs) {
SinglePassRunAndMatch<DescriptorScalarReplacement>(shader, true);
}
+TEST_F(DescriptorScalarReplacementTest, MemberDecorationForResourceStruct) {
+ // Check that an OpMemberDecorate instruction is correctly converted to a
+ // OpDecorate instruction.
+
+ const std::string shader = R"(
+; CHECK: OpDecorate [[t:%\w+]] DescriptorSet 0
+; CHECK: OpDecorate [[t]] Binding 0
+; CHECK: OpDecorate [[t]] RelaxedPrecision
+; CHECK: OpDecorate [[s:%\w+]] DescriptorSet 0
+; CHECK: OpDecorate [[s]] Binding 1
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %PSMain "PSMain" %in_var_TEXCOORD %out_var_SV_Target
+ OpExecutionMode %PSMain OriginUpperLeft
+ OpSource HLSL 600
+ OpName %sampler2D_h "sampler2D_h"
+ OpMemberName %sampler2D_h 0 "t"
+ OpMemberName %sampler2D_h 1 "s"
+ OpName %type_2d_image "type.2d.image"
+ OpName %type_sampler "type.sampler"
+ OpName %_MainTex "_MainTex"
+ OpName %in_var_TEXCOORD "in.var.TEXCOORD"
+ OpName %out_var_SV_Target "out.var.SV_Target"
+ OpName %PSMain "PSMain"
+ OpName %type_sampled_image "type.sampled.image"
+ OpDecorate %in_var_TEXCOORD Location 0
+ OpDecorate %out_var_SV_Target Location 0
+ OpDecorate %_MainTex DescriptorSet 0
+ OpDecorate %_MainTex Binding 0
+ OpMemberDecorate %sampler2D_h 0 RelaxedPrecision
+ OpDecorate %out_var_SV_Target RelaxedPrecision
+ OpDecorate %69 RelaxedPrecision
+ %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%type_sampler = OpTypeSampler
+%sampler2D_h = OpTypeStruct %type_2d_image %type_sampler
+%_ptr_UniformConstant_sampler2D_h = OpTypePointer UniformConstant %sampler2D_h
+ %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %35 = OpTypeFunction %void
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+ %_MainTex = OpVariable %_ptr_UniformConstant_sampler2D_h UniformConstant
+%in_var_TEXCOORD = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+ %PSMain = OpFunction %void None %35
+ %43 = OpLabel
+ %44 = OpLoad %v2float %in_var_TEXCOORD
+ %57 = OpLoad %sampler2D_h %_MainTex
+ %72 = OpCompositeExtract %type_2d_image %57 0
+ %73 = OpCompositeExtract %type_sampler %57 1
+ %68 = OpSampledImage %type_sampled_image %72 %73
+ %69 = OpImageSampleImplicitLod %v4float %68 %44 None
+ OpStore %out_var_SV_Target %69
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<DescriptorScalarReplacement>(shader, true);
+}
+
+TEST_F(DescriptorScalarReplacementTest, DecorateStringForReflect) {
+ // Check that an OpDecorateString instruction is correctly cloned to new
+ // variable.
+
+ const std::string shader = R"(
+; CHECK: OpName %g_testTextures_0_ "g_testTextures[0]"
+; CHECK: OpDecorate %g_testTextures_0_ DescriptorSet 0
+; CHECK: OpDecorate %g_testTextures_0_ Binding 0
+; CHECK: OpDecorateString %g_testTextures_0_ UserTypeGOOGLE "texture2d"
+ OpCapability Shader
+ OpExtension "SPV_GOOGLE_hlsl_functionality1"
+ OpExtension "SPV_GOOGLE_user_type"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %gl_FragCoord %out_var_SV_Target
+ OpExecutionMode %main OriginUpperLeft
+ OpSource HLSL 600
+ OpName %type_2d_image "type.2d.image"
+ OpName %g_testTextures "g_testTextures"
+ OpName %out_var_SV_Target "out.var.SV_Target"
+ OpName %main "main"
+ OpName %param_var_vPixelPos "param.var.vPixelPos"
+ OpName %src_main "src.main"
+ OpName %vPixelPos "vPixelPos"
+ OpName %bb_entry "bb.entry"
+ OpDecorate %gl_FragCoord BuiltIn FragCoord
+ OpDecorateString %gl_FragCoord UserSemantic "SV_Position"
+ OpDecorateString %out_var_SV_Target UserSemantic "SV_Target"
+ OpDecorate %out_var_SV_Target Location 0
+ OpDecorate %g_testTextures DescriptorSet 0
+ OpDecorate %g_testTextures Binding 0
+ OpDecorateString %g_testTextures UserTypeGOOGLE "texture2d"
+ %uint = OpTypeInt 32 0
+ %uint_0 = OpConstant %uint 0
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %uint_2 = OpConstant %uint 2
+ %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_arr_type_2d_image_uint_2 = OpTypeArray %type_2d_image %uint_2
+%_ptr_UniformConstant__arr_type_2d_image_uint_2 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_2
+ %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %18 = OpTypeFunction %void
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+ %25 = OpTypeFunction %v4float %_ptr_Function_v4float
+ %v2float = OpTypeVector %float 2
+ %v3uint = OpTypeVector %uint 3
+ %v3int = OpTypeVector %int 3
+ %v2int = OpTypeVector %int 2
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%g_testTextures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_2 UniformConstant
+%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+ %main = OpFunction %void None %18
+ %19 = OpLabel
+%param_var_vPixelPos = OpVariable %_ptr_Function_v4float Function
+ %22 = OpLoad %v4float %gl_FragCoord
+ OpStore %param_var_vPixelPos %22
+ %23 = OpFunctionCall %v4float %src_main %param_var_vPixelPos
+ OpStore %out_var_SV_Target %23
+ OpReturn
+ OpFunctionEnd
+ %src_main = OpFunction %v4float None %25
+ %vPixelPos = OpFunctionParameter %_ptr_Function_v4float
+ %bb_entry = OpLabel
+ %28 = OpLoad %v4float %vPixelPos
+ %30 = OpVectorShuffle %v2float %28 %28 0 1
+ %31 = OpCompositeExtract %float %30 0
+ %32 = OpCompositeExtract %float %30 1
+ %33 = OpConvertFToU %uint %31
+ %34 = OpConvertFToU %uint %32
+ %36 = OpCompositeConstruct %v3uint %33 %34 %uint_0
+ %38 = OpBitcast %v3int %36
+ %40 = OpVectorShuffle %v2int %38 %38 0 1
+ %41 = OpCompositeExtract %int %38 2
+ %43 = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_testTextures %int_0
+ %44 = OpLoad %type_2d_image %43
+ %45 = OpImageFetch %v4float %44 %40 Lod %41
+ OpReturnValue %45
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<DescriptorScalarReplacement>(shader, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/dominator_tree/generated.cpp b/test/opt/dominator_tree/generated.cpp
index 534f770b..4fccef05 100644
--- a/test/opt/dominator_tree/generated.cpp
+++ b/test/opt/dominator_tree/generated.cpp
@@ -57,7 +57,7 @@ void check_dominance(const DominatorAnalysisBase& dom_tree, const Function* fn,
}
}
-// Check that x does not dominates y and vise versa
+// Check that x does not dominates y and vice versa
void check_no_dominance(const DominatorAnalysisBase& dom_tree,
const Function* fn, uint32_t x, uint32_t y) {
SCOPED_TRACE("Check no domination for Basic Block " + std::to_string(x) +
diff --git a/test/opt/eliminate_dead_const_test.cpp b/test/opt/eliminate_dead_const_test.cpp
index 59f06f9f..87aab545 100644
--- a/test/opt/eliminate_dead_const_test.cpp
+++ b/test/opt/eliminate_dead_const_test.cpp
@@ -547,7 +547,7 @@ INSTANTIATE_TEST_SUITE_P(
},
// Uint vector type spec constants. One vector has all component dead,
- // another vector has one dead unsigend integer and one used unsigned
+ // another vector has one dead unsigned integer and one used unsigned
// integer.
{
/* .used_consts = */
diff --git a/test/opt/eliminate_dead_member_test.cpp b/test/opt/eliminate_dead_member_test.cpp
index 7728782e..e277999e 100644
--- a/test/opt/eliminate_dead_member_test.cpp
+++ b/test/opt/eliminate_dead_member_test.cpp
@@ -576,7 +576,6 @@ TEST_F(EliminateDeadMemberTest, RemoveMembersUpdateArrayLength) {
%_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform
%main = OpFunction %void None %9
%10 = OpLabel
- %11 = OpLoad %type__Globals %_Globals
%12 = OpArrayLength %uint %_Globals 2
OpReturn
OpFunctionEnd
@@ -627,6 +626,67 @@ TEST_F(EliminateDeadMemberTest, KeepMembersOpStore) {
EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result));
}
+TEST_F(EliminateDeadMemberTest, KeepStorageBufferMembers) {
+ // Test that all members of the storage buffer struct %S are kept.
+ // No change expected.
+ const std::string text = R"(
+ OpCapability Shader
+ OpExtension "SPV_GOOGLE_hlsl_functionality1"
+ OpExtension "SPV_GOOGLE_user_type"
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %PSMain "PSMain" %out_var_SV_TARGET
+ OpExecutionMode %PSMain OriginUpperLeft
+ OpSource HLSL 600
+ OpName %type_StructuredBuffer_S "type.StructuredBuffer.S"
+ OpName %S "S"
+ OpMemberName %S 0 "A"
+ OpMemberName %S 1 "B"
+ OpName %Buf "Buf"
+ OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+ OpName %PSMain "PSMain"
+ OpDecorateString %out_var_SV_TARGET UserSemantic "SV_TARGET"
+ OpDecorate %out_var_SV_TARGET Location 0
+ OpDecorate %Buf DescriptorSet 0
+ OpDecorate %Buf Binding 0
+ OpMemberDecorate %S 0 Offset 0
+ OpMemberDecorate %S 1 Offset 16
+ OpDecorate %_runtimearr_S ArrayStride 32
+ OpMemberDecorate %type_StructuredBuffer_S 0 Offset 0
+ OpMemberDecorate %type_StructuredBuffer_S 0 NonWritable
+ OpDecorate %type_StructuredBuffer_S BufferBlock
+ OpDecorateString %Buf UserTypeGOOGLE "structuredbuffer"
+ %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
+ %v4float = OpTypeVector %float 4
+ %S = OpTypeStruct %v4float %v4float
+%_runtimearr_S = OpTypeRuntimeArray %S
+%type_StructuredBuffer_S = OpTypeStruct %_runtimearr_S
+%_ptr_Uniform_type_StructuredBuffer_S = OpTypePointer Uniform %type_StructuredBuffer_S
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %18 = OpTypeFunction %void
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+ %Buf = OpVariable %_ptr_Uniform_type_StructuredBuffer_S Uniform
+%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+ %PSMain = OpFunction %void None %18
+ %20 = OpLabel
+ %21 = OpAccessChain %_ptr_Uniform_v4float %Buf %int_0 %uint_0 %int_1
+ %22 = OpLoad %v4float %21
+ OpStore %out_var_SV_TARGET %22
+ OpReturn
+ OpFunctionEnd
+)";
+
+ auto result = SinglePassRunAndDisassemble<opt::EliminateDeadMembersPass>(
+ text, /* skip_nop = */ true, /* do_validation = */ true);
+ EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result));
+}
+
TEST_F(EliminateDeadMemberTest, KeepMembersOpCopyMemory) {
// Test that all members are kept because of an OpCopyMemory.
// No change expected.
diff --git a/test/opt/flatten_decoration_test.cpp b/test/opt/flatten_decoration_test.cpp
index d8d88677..63207fd2 100644
--- a/test/opt/flatten_decoration_test.cpp
+++ b/test/opt/flatten_decoration_test.cpp
@@ -43,7 +43,7 @@ OpName %Camera "Camera"
)";
}
-// Retuns types
+// Returns types
std::string TypesAndFunctionsAssembly() {
return
R"(%void = OpTypeVoid
diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp
index 8457bbfe..df8f3b12 100644
--- a/test/opt/fold_test.cpp
+++ b/test/opt/fold_test.cpp
@@ -137,6 +137,7 @@ OpName %main "main"
%int = OpTypeInt 32 1
%long = OpTypeInt 64 1
%uint = OpTypeInt 32 0
+%ulong = OpTypeInt 64 0
%v2int = OpTypeVector %int 2
%v4int = OpTypeVector %int 4
%v4float = OpTypeVector %float 4
@@ -154,6 +155,7 @@ OpName %main "main"
%_ptr_double = OpTypePointer Function %double
%_ptr_half = OpTypePointer Function %half
%_ptr_long = OpTypePointer Function %long
+%_ptr_ulong = OpTypePointer Function %ulong
%_ptr_v2int = OpTypePointer Function %v2int
%_ptr_v4int = OpTypePointer Function %v4int
%_ptr_v4float = OpTypePointer Function %v4float
@@ -171,12 +173,23 @@ OpName %main "main"
%int_2 = OpConstant %int 2
%int_3 = OpConstant %int 3
%int_4 = OpConstant %int 4
+%int_10 = OpConstant %int 10
+%int_1073741824 = OpConstant %int 1073741824
+%int_n1 = OpConstant %int -1
%int_n24 = OpConstant %int -24
+%int_n858993459 = OpConstant %int -858993459
%int_min = OpConstant %int -2147483648
%int_max = OpConstant %int 2147483647
%long_0 = OpConstant %long 0
+%long_1 = OpConstant %long 1
%long_2 = OpConstant %long 2
%long_3 = OpConstant %long 3
+%long_10 = OpConstant %long 10
+%long_4611686018427387904 = OpConstant %long 4611686018427387904
+%long_n1 = OpConstant %long -1
+%long_n3689348814741910323 = OpConstant %long -3689348814741910323
+%long_min = OpConstant %long -9223372036854775808
+%long_max = OpConstant %long 9223372036854775807
%uint_0 = OpConstant %uint 0
%uint_1 = OpConstant %uint 1
%uint_2 = OpConstant %uint 2
@@ -184,7 +197,13 @@ OpName %main "main"
%uint_4 = OpConstant %uint 4
%uint_32 = OpConstant %uint 32
%uint_42 = OpConstant %uint 42
+%uint_2147483649 = OpConstant %uint 2147483649
%uint_max = OpConstant %uint 4294967295
+%ulong_0 = OpConstant %ulong 0
+%ulong_1 = OpConstant %ulong 1
+%ulong_2 = OpConstant %ulong 2
+%ulong_9223372036854775809 = OpConstant %ulong 9223372036854775809
+%ulong_max = OpConstant %ulong 18446744073709551615
%v2int_undef = OpUndef %v2int
%v2int_0_0 = OpConstantComposite %v2int %int_0 %int_0
%v2int_1_0 = OpConstantComposite %v2int %int_1 %int_0
@@ -205,6 +224,7 @@ OpName %main "main"
%104 = OpConstant %float 0 ; Need a def with an numerical id to define id maps.
%float_null = OpConstantNull %float
%float_0 = OpConstant %float 0
+%float_n0 = OpConstant %float -0.0
%float_1 = OpConstant %float 1
%float_2 = OpConstant %float 2
%float_3 = OpConstant %float 3
@@ -230,6 +250,7 @@ OpName %main "main"
%105 = OpConstant %double 0 ; Need a def with an numerical id to define id maps.
%double_null = OpConstantNull %double
%double_0 = OpConstant %double 0
+%double_n0 = OpConstant %double -0.0
%double_1 = OpConstant %double 1
%double_2 = OpConstant %double 2
%double_3 = OpConstant %double 3
@@ -1968,7 +1989,39 @@ INSTANTIATE_TEST_SUITE_P(FloatConstantFoldingTest, FloatInstructionFoldingTest,
"%2 = OpExtInst %float %1 Pow %float_2 %float_3\n" +
"OpReturn\n" +
"OpFunctionEnd",
- 2, 8.0)
+ 2, 8.0),
+ // Test case 43: Fold 1.0 / -0.0.
+ InstructionFoldingCase<float>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpFDiv %float %float_1 %float_n0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, -std::numeric_limits<float>::infinity()),
+ // Test case 44: Fold -1.0 / -0.0
+ InstructionFoldingCase<float>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpFDiv %float %float_n1 %float_n0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, std::numeric_limits<float>::infinity()),
+ // Test case 45: Fold 0.0 / 0.0
+ InstructionFoldingCase<float>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpFDiv %float %float_0 %float_0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, std::numeric_limits<float>::quiet_NaN()),
+ // Test case 46: Fold 0.0 / -0.0
+ InstructionFoldingCase<float>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpFDiv %float %float_0 %float_n0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, std::numeric_limits<float>::quiet_NaN())
));
// clang-format on
@@ -2000,7 +2053,11 @@ TEST_P(DoubleInstructionFoldingTest, Case) {
const_mrg->GetConstantFromInst(inst)->AsFloatConstant();
EXPECT_NE(result, nullptr);
if (result != nullptr) {
- EXPECT_EQ(result->GetDoubleValue(), tc.expected_result);
+ if (!std::isnan(tc.expected_result)) {
+ EXPECT_EQ(result->GetDoubleValue(), tc.expected_result);
+ } else {
+ EXPECT_TRUE(std::isnan(result->GetDoubleValue()));
+ }
}
}
}
@@ -2203,7 +2260,39 @@ INSTANTIATE_TEST_SUITE_P(DoubleConstantFoldingTest, DoubleInstructionFoldingTest
"%2 = OpExtInst %double %1 Pow %double_2 %double_3\n" +
"OpReturn\n" +
"OpFunctionEnd",
- 2, 8.0)
+ 2, 8.0),
+ // Test case 23: Fold 1.0 / -0.0.
+ InstructionFoldingCase<double>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpFDiv %double %double_1 %double_n0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, -std::numeric_limits<double>::infinity()),
+ // Test case 24: Fold -1.0 / -0.0
+ InstructionFoldingCase<double>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpFDiv %double %double_n1 %double_n0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, std::numeric_limits<double>::infinity()),
+ // Test case 25: Fold 0.0 / 0.0
+ InstructionFoldingCase<double>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpFDiv %double %double_0 %double_0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, std::numeric_limits<double>::quiet_NaN()),
+ // Test case 26: Fold 0.0 / -0.0
+ InstructionFoldingCase<double>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpFDiv %double %double_0 %double_n0\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 2, std::numeric_limits<double>::quiet_NaN())
));
// clang-format on
@@ -3589,7 +3678,19 @@ INSTANTIATE_TEST_SUITE_P(CompositeExtractFoldingTest, GeneralInstructionFoldingT
"%4 = OpCompositeExtract %int %3 2\n" +
"OpReturn\n" +
"OpFunctionEnd",
- 4, INT_0_ID)
+ 4, INT_0_ID),
+ // Test case 15:
+ // Don't fold extract fed by construct with vector result if the index is
+ // past the last element.
+ InstructionFoldingCase<uint32_t>(
+ Header() + "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%2 = OpCompositeConstruct %v2int %int_0 %int_0\n" +
+ "%3 = OpCompositeConstruct %v4int %2 %100 %int_0\n" +
+ "%4 = OpCompositeExtract %int %3 4\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd",
+ 4, 0)
));
INSTANTIATE_TEST_SUITE_P(CompositeConstructFoldingTest, GeneralInstructionFoldingTest,
@@ -5572,7 +5673,109 @@ INSTANTIATE_TEST_SUITE_P(MergeMulTest, MatchingInstructionFoldingTest,
"%5 = OpFMul %float %4 %2\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
- 5, true)
+ 5, true),
+ // Test case 25: fold overflowing signed 32 bit imuls
+ // (x * 1073741824) * 2 = x * int_min
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" +
+ "; CHECK: [[int_min:%\\w+]] = OpConstant [[int]] -2147483648\n" +
+ "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
+ "; CHECK: %4 = OpIMul [[int]] [[ld]] [[int_min]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%var = OpVariable %_ptr_int Function\n" +
+ "%2 = OpLoad %int %var\n" +
+ "%3 = OpIMul %int %2 %int_1073741824\n" +
+ "%4 = OpIMul %int %3 %int_2\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
+ 4, true),
+ // Test case 26: fold overflowing signed 64 bit imuls
+ // (x * 4611686018427387904) * 2 = x * long_min
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" +
+ "; CHECK: [[long_min:%\\w+]] = OpConstant [[long]] -9223372036854775808\n" +
+ "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" +
+ "; CHECK: %4 = OpIMul [[long]] [[ld]] [[long_min]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%var = OpVariable %_ptr_long Function\n" +
+ "%2 = OpLoad %long %var\n" +
+ "%3 = OpIMul %long %2 %long_4611686018427387904\n" +
+ "%4 = OpIMul %long %3 %long_2\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
+ 4, true),
+ // Test case 27: fold overflowing 32 bit unsigned imuls
+ // (x * 2147483649) * 2 = x * 2
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+ "; CHECK: [[uint_2:%\\w+]] = OpConstant [[uint]] 2\n" +
+ "; CHECK: [[ld:%\\w+]] = OpLoad [[uint]]\n" +
+ "; CHECK: %4 = OpIMul [[uint]] [[ld]] [[uint_2]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%var = OpVariable %_ptr_uint Function\n" +
+ "%2 = OpLoad %uint %var\n" +
+ "%3 = OpIMul %uint %2 %uint_2147483649\n" +
+ "%4 = OpIMul %uint %3 %uint_2\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
+ 4, true),
+ // Test case 28: fold overflowing 64 bit unsigned imuls
+ // (x * 9223372036854775809) * 2 = x * 2
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[ulong:%\\w+]] = OpTypeInt 64 0\n" +
+ "; CHECK: [[ulong_2:%\\w+]] = OpConstant [[ulong]] 2\n" +
+ "; CHECK: [[ld:%\\w+]] = OpLoad [[ulong]]\n" +
+ "; CHECK: %4 = OpIMul [[ulong]] [[ld]] [[ulong_2]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%var = OpVariable %_ptr_ulong Function\n" +
+ "%2 = OpLoad %ulong %var\n" +
+ "%3 = OpIMul %ulong %2 %ulong_9223372036854775809\n" +
+ "%4 = OpIMul %ulong %3 %ulong_2\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
+ 4, true),
+ // Test case 29: fold underflowing signed 32 bit imuls
+ // (x * (-858993459)) * 10 = x * 2
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" +
+ "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" +
+ "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
+ "; CHECK: %4 = OpIMul [[int]] [[ld]] [[int_2]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%var = OpVariable %_ptr_int Function\n" +
+ "%2 = OpLoad %int %var\n" +
+ "%3 = OpIMul %int %2 %int_n858993459\n" +
+ "%4 = OpIMul %int %3 %int_10\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
+ 4, true),
+ // Test case 30: fold underflowing signed 64 bit imuls
+ // (x * (-3689348814741910323)) * 10 = x * 2
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" +
+ "; CHECK: [[long_2:%\\w+]] = OpConstant [[long]] 2\n" +
+ "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" +
+ "; CHECK: %4 = OpIMul [[long]] [[ld]] [[long_2]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%var = OpVariable %_ptr_long Function\n" +
+ "%2 = OpLoad %long %var\n" +
+ "%3 = OpIMul %long %2 %long_n3689348814741910323\n" +
+ "%4 = OpIMul %long %3 %long_10\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
+ 4, true)
));
INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest,
@@ -5732,15 +5935,11 @@ INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest,
"OpReturn\n" +
"OpFunctionEnd\n",
4, false),
- // Test case 11: merge sdiv of snegate
- // (-x) / 2 = x / -2
+ // Test case 11: Do not merge sdiv of snegate. If %2 is INT_MIN, then the
+ // sign of %3 will be the same as %2. This cannot be accounted for in OpSDiv.
+ // Specifically, (-INT_MIN) / 2 != INT_MIN / -2.
InstructionFoldingCase<bool>(
Header() +
- "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
- "; CHECK: OpConstant [[int]] -2147483648\n" +
- "; CHECK: [[int_n2:%\\w+]] = OpConstant [[int]] -2{{[[:space:]]}}\n" +
- "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
- "; CHECK: %4 = OpSDiv [[int]] [[ld]] [[int_n2]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var = OpVariable %_ptr_int Function\n" +
@@ -5749,16 +5948,12 @@ INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest,
"%4 = OpSDiv %int %3 %int_2\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
- 4, true),
- // Test case 12: merge sdiv of snegate
- // 2 / (-x) = -2 / x
+ 4, false),
+ // Test case 12: Do not merge sdiv of snegate. If %2 is INT_MIN, then the
+ // sign of %3 will be the same as %2. This cannot be accounted for in OpSDiv.
+ // Specifically, 2 / (-INT_MIN) != -2 / INT_MIN.
InstructionFoldingCase<bool>(
Header() +
- "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
- "; CHECK: OpConstant [[int]] -2147483648\n" +
- "; CHECK: [[int_n2:%\\w+]] = OpConstant [[int]] -2{{[[:space:]]}}\n" +
- "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
- "; CHECK: %4 = OpSDiv [[int]] [[int_n2]] [[ld]]\n" +
"%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%var = OpVariable %_ptr_int Function\n" +
@@ -5767,7 +5962,7 @@ INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest,
"%4 = OpSDiv %int %int_2 %3\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
- 4, true),
+ 4, false),
// Test case 13: Don't merge
// (x / {null}) / {null}
InstructionFoldingCase<bool>(
@@ -5818,7 +6013,33 @@ INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest,
"%5 = OpFDiv %float %4 %2\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
- 5, true)
+ 5, true),
+ // Test case 16: Do not merge udiv of snegate
+ // (-x) / 2u
+ InstructionFoldingCase<bool>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%var = OpVariable %_ptr_uint Function\n" +
+ "%2 = OpLoad %uint %var\n" +
+ "%3 = OpSNegate %uint %2\n" +
+ "%4 = OpUDiv %uint %3 %uint_2\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
+ 4, false),
+ // Test case 17: Do not merge udiv of snegate
+ // 2u / (-x)
+ InstructionFoldingCase<bool>(
+ Header() +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%var = OpVariable %_ptr_uint Function\n" +
+ "%2 = OpLoad %uint %var\n" +
+ "%3 = OpSNegate %uint %2\n" +
+ "%4 = OpUDiv %uint %uint_2 %3\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
+ 4, false)
));
INSTANTIATE_TEST_SUITE_P(MergeAddTest, MatchingInstructionFoldingTest,
@@ -6026,6 +6247,108 @@ INSTANTIATE_TEST_SUITE_P(MergeAddTest, MatchingInstructionFoldingTest,
"%4 = OpFAdd %float %float_2 %3\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
+ 4, true),
+ // Test case 12: fold overflowing signed 32 bit iadds
+ // (x + int_max) + 1 = x + int_min
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" +
+ "; CHECK: [[int_min:%\\w+]] = OpConstant [[int]] -2147483648\n" +
+ "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
+ "; CHECK: %4 = OpIAdd [[int]] [[ld]] [[int_min]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%var = OpVariable %_ptr_int Function\n" +
+ "%2 = OpLoad %int %var\n" +
+ "%3 = OpIAdd %int %2 %int_max\n" +
+ "%4 = OpIAdd %int %3 %int_1\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
+ 4, true),
+ // Test case 13: fold overflowing signed 64 bit iadds
+ // (x + long_max) + 1 = x + long_min
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" +
+ "; CHECK: [[long_min:%\\w+]] = OpConstant [[long]] -9223372036854775808\n" +
+ "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" +
+ "; CHECK: %4 = OpIAdd [[long]] [[ld]] [[long_min]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%var = OpVariable %_ptr_long Function\n" +
+ "%2 = OpLoad %long %var\n" +
+ "%3 = OpIAdd %long %2 %long_max\n" +
+ "%4 = OpIAdd %long %3 %long_1\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
+ 4, true),
+ // Test case 14: fold overflowing 32 bit unsigned iadds
+ // (x + uint_max) + 2 = x + 1
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+ "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+ "; CHECK: [[ld:%\\w+]] = OpLoad [[uint]]\n" +
+ "; CHECK: %4 = OpIAdd [[uint]] [[ld]] [[uint_1]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%var = OpVariable %_ptr_uint Function\n" +
+ "%2 = OpLoad %uint %var\n" +
+ "%3 = OpIAdd %uint %2 %uint_max\n" +
+ "%4 = OpIAdd %uint %3 %uint_2\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
+ 4, true),
+ // Test case 15: fold overflowing 64 bit unsigned iadds
+ // (x + ulong_max) + 2 = x + 1
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[ulong:%\\w+]] = OpTypeInt 64 0\n" +
+ "; CHECK: [[ulong_1:%\\w+]] = OpConstant [[ulong]] 1\n" +
+ "; CHECK: [[ld:%\\w+]] = OpLoad [[ulong]]\n" +
+ "; CHECK: %4 = OpIAdd [[ulong]] [[ld]] [[ulong_1]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%var = OpVariable %_ptr_ulong Function\n" +
+ "%2 = OpLoad %ulong %var\n" +
+ "%3 = OpIAdd %ulong %2 %ulong_max\n" +
+ "%4 = OpIAdd %ulong %3 %ulong_2\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
+ 4, true),
+ // Test case 16: fold underflowing signed 32 bit iadds
+ // (x + int_min) + (-1) = x + int_max
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" +
+ "; CHECK: [[int_max:%\\w+]] = OpConstant [[int]] 2147483647\n" +
+ "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
+ "; CHECK: %4 = OpIAdd [[int]] [[ld]] [[int_max]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%var = OpVariable %_ptr_int Function\n" +
+ "%2 = OpLoad %int %var\n" +
+ "%3 = OpIAdd %int %2 %int_min\n" +
+ "%4 = OpIAdd %int %3 %int_n1\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
+ 4, true),
+ // Test case 17: fold underflowing signed 64 bit iadds
+ // (x + long_min) + (-1) = x + long_max
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" +
+ "; CHECK: [[long_max:%\\w+]] = OpConstant [[long]] 9223372036854775807\n" +
+ "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" +
+ "; CHECK: %4 = OpIAdd [[long]] [[ld]] [[long_max]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%var = OpVariable %_ptr_long Function\n" +
+ "%2 = OpLoad %long %var\n" +
+ "%3 = OpIAdd %long %2 %long_min\n" +
+ "%4 = OpIAdd %long %3 %long_n1\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
4, true)
));
@@ -6394,6 +6717,40 @@ INSTANTIATE_TEST_SUITE_P(MergeSubTest, MatchingInstructionFoldingTest,
"%4 = OpISub %int %int_2 %3\n" +
"OpReturn\n" +
"OpFunctionEnd\n",
+ 4, true),
+ // Test case 14: fold overflowing signed 32 bit isubs
+ // (x - int_max) - 1 = x - int_min
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[int:%\\w+]] = OpTypeInt 32\n" +
+ "; CHECK: [[int_min:%\\w+]] = OpConstant [[int]] -2147483648\n" +
+ "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" +
+ "; CHECK: %4 = OpISub [[int]] [[ld]] [[int_min]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%var = OpVariable %_ptr_int Function\n" +
+ "%2 = OpLoad %int %var\n" +
+ "%3 = OpISub %int %2 %int_max\n" +
+ "%4 = OpISub %int %3 %int_1\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
+ 4, true),
+ // Test case 15: fold overflowing signed 64 bit isubs
+ // (x - long_max) - 1 = x - long_min
+ InstructionFoldingCase<bool>(
+ Header() +
+ "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" +
+ "; CHECK: [[long_min:%\\w+]] = OpConstant [[long]] -9223372036854775808\n" +
+ "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" +
+ "; CHECK: %4 = OpISub [[long]] [[ld]] [[long_min]]\n" +
+ "%main = OpFunction %void None %void_func\n" +
+ "%main_lab = OpLabel\n" +
+ "%var = OpVariable %_ptr_long Function\n" +
+ "%2 = OpLoad %long %var\n" +
+ "%3 = OpISub %long %2 %long_max\n" +
+ "%4 = OpISub %long %3 %long_1\n" +
+ "OpReturn\n" +
+ "OpFunctionEnd\n",
4, true)
));
@@ -6735,7 +7092,7 @@ using MatchingInstructionWithNoResultFoldingTest =
// Test folding instructions that do not have a result. The instruction
// that will be folded is the last instruction before the return. If there
-// are multiple returns, there is not guarentee which one is used.
+// are multiple returns, there is not guarantee which one is used.
TEST_P(MatchingInstructionWithNoResultFoldingTest, Case) {
const auto& tc = GetParam();
diff --git a/test/opt/graphics_robust_access_test.cpp b/test/opt/graphics_robust_access_test.cpp
index 3c23347d..057b909d 100644
--- a/test/opt/graphics_robust_access_test.cpp
+++ b/test/opt/graphics_robust_access_test.cpp
@@ -1242,7 +1242,7 @@ TEST_F(GraphicsRobustAccessTest, ACArrayRTArrayStructVectorElem) {
; CHECK-DAG: %int_9 = OpConstant %int 9
; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
; CHECK: OpLabel
- ; This access chain is manufatured only so we can compute the array length.
+ ; This access chain is manufactured only so we can compute the array length.
; Note that the %int_9 is already clamped
; CHECK: %[[ssbo_base:\w+]] = )" << ac
<< R"( %[[ssbo_p]] %var %int_9
diff --git a/test/opt/inline_opaque_test.cpp b/test/opt/inline_opaque_test.cpp
index 8cb8925c..e4db4325 100644
--- a/test/opt/inline_opaque_test.cpp
+++ b/test/opt/inline_opaque_test.cpp
@@ -226,6 +226,115 @@ OpFunctionEnd
predefs + before + post_defs, predefs + after + post_defs, true, true);
}
+TEST_F(InlineOpaqueTest, InlineOpaqueForLinkage) {
+ const std::string predefs_1 =
+ R"(OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpSource HLSL 630
+OpName %main "main"
+OpName %S_t "S_t"
+OpMemberName %S_t 0 "v0"
+OpMemberName %S_t 1 "v1"
+OpMemberName %S_t 2 "smp"
+OpName %foo_struct_S_t_vf2_vf21_ "foo(struct-S_t-vf2-vf21;"
+OpName %s "s"
+OpName %outColor "outColor"
+OpName %sampler15 "sampler15"
+OpName %s0 "s0"
+OpName %texCoords "texCoords"
+OpName %param "param"
+OpDecorate %main LinkageAttributes "main" Export
+)";
+
+ const std::string name = R"(OpName %return_value "return_value"
+)";
+
+ const std::string predefs_2 = R"(OpDecorate %sampler15 DescriptorSet 0
+%void = OpTypeVoid
+%13 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%outColor = OpVariable %_ptr_Output_v4float Output
+%18 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%19 = OpTypeSampledImage %18
+%S_t = OpTypeStruct %v2float %v2float %19
+%_ptr_Function_S_t = OpTypePointer Function %S_t
+%21 = OpTypeFunction %void %_ptr_Function_S_t
+%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19
+%_ptr_Function_19 = OpTypePointer Function %19
+%sampler15 = OpVariable %_ptr_UniformConstant_19 UniformConstant
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%int_2 = OpConstant %int 2
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+%texCoords = OpVariable %_ptr_Input_v2float Input
+)";
+
+ const std::string before =
+ R"(%main = OpFunction %void None %13
+%29 = OpLabel
+%s0 = OpVariable %_ptr_Function_S_t Function
+%param = OpVariable %_ptr_Function_S_t Function
+%30 = OpLoad %v2float %texCoords
+%31 = OpAccessChain %_ptr_Function_v2float %s0 %int_0
+OpStore %31 %30
+%32 = OpLoad %19 %sampler15
+%33 = OpAccessChain %_ptr_Function_19 %s0 %int_2
+OpStore %33 %32
+%34 = OpLoad %S_t %s0
+OpStore %param %34
+%return_value = OpFunctionCall %void %foo_struct_S_t_vf2_vf21_ %param
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string after =
+ R"(%main = OpFunction %void None %13
+%29 = OpLabel
+%s0 = OpVariable %_ptr_Function_S_t Function
+%param = OpVariable %_ptr_Function_S_t Function
+%30 = OpLoad %v2float %texCoords
+%31 = OpAccessChain %_ptr_Function_v2float %s0 %int_0
+OpStore %31 %30
+%32 = OpLoad %19 %sampler15
+%33 = OpAccessChain %_ptr_Function_19 %s0 %int_2
+OpStore %33 %32
+%34 = OpLoad %S_t %s0
+OpStore %param %34
+%42 = OpAccessChain %_ptr_Function_19 %param %int_2
+%43 = OpLoad %19 %42
+%44 = OpAccessChain %_ptr_Function_v2float %param %int_0
+%45 = OpLoad %v2float %44
+%46 = OpImageSampleImplicitLod %v4float %43 %45
+OpStore %outColor %46
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string post_defs =
+ R"(%foo_struct_S_t_vf2_vf21_ = OpFunction %void None %21
+%s = OpFunctionParameter %_ptr_Function_S_t
+%35 = OpLabel
+%36 = OpAccessChain %_ptr_Function_19 %s %int_2
+%37 = OpLoad %19 %36
+%38 = OpAccessChain %_ptr_Function_v2float %s %int_0
+%39 = OpLoad %v2float %38
+%40 = OpImageSampleImplicitLod %v4float %37 %39
+OpStore %outColor %40
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<InlineOpaquePass>(
+ predefs_1 + name + predefs_2 + before + post_defs,
+ predefs_1 + predefs_2 + after + post_defs, true, true);
+}
+
TEST_F(InlineOpaqueTest, InlineInNonEntryPointFunction) {
// This demonstrates opaque inlining in a function that is not
// an entry point function (main2) but is in the call tree of an
diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp
index 29399013..cefd8e54 100644
--- a/test/opt/inline_test.cpp
+++ b/test/opt/inline_test.cpp
@@ -2300,7 +2300,6 @@ TEST_F(InlineTest, DontInlineDirectlyRecursiveFunc) {
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
-OpDecorate %2 DescriptorSet 439418829
%void = OpTypeVoid
%4 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -2330,7 +2329,6 @@ TEST_F(InlineTest, DontInlineInDirectlyRecursiveFunc) {
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
-OpDecorate %2 DescriptorSet 439418829
%void = OpTypeVoid
%4 = OpTypeFunction %void
%float = OpTypeFloat 32
@@ -2581,6 +2579,132 @@ OpFunctionEnd
SinglePassRunAndCheck<InlineExhaustivePass>(before, after, false, true);
}
+TEST_F(InlineTest, InlineForLinkage) {
+ const std::string before =
+ R"(OpCapability SampledBuffer
+OpCapability ImageBuffer
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpSource HLSL 630
+OpName %type_buffer_image "type.buffer.image"
+OpName %output "output"
+OpName %main "main"
+OpName %color "color"
+OpName %bb_entry "bb.entry"
+OpName %param_var_color "param.var.color"
+OpName %fn "fn"
+OpName %color_0 "color"
+OpName %bb_entry_0 "bb.entry"
+OpName %v "v"
+OpDecorate %main LinkageAttributes "main" Export
+OpDecorate %output DescriptorSet 0
+OpDecorate %output Binding 1
+%float = OpTypeFloat 32
+%float_0_200000003 = OpConstant %float 0.200000003
+%v4float = OpTypeVector %float 4
+%6 = OpConstantComposite %v4float %float_0_200000003 %float_0_200000003 %float_0_200000003 %float_0_200000003
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%type_buffer_image = OpTypeImage %float Buffer 2 0 0 2 Rgba32f
+%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%11 = OpTypeFunction %float %_ptr_Function_v4float
+%_ptr_Function_float = OpTypePointer Function %float
+%output = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant
+%main = OpFunction %float None %11
+%color = OpFunctionParameter %_ptr_Function_v4float
+%bb_entry = OpLabel
+%param_var_color = OpVariable %_ptr_Function_v4float Function
+%16 = OpLoad %v4float %color
+OpStore %param_var_color %16
+%17 = OpFunctionCall %float %fn %param_var_color
+OpReturnValue %17
+OpFunctionEnd
+%fn = OpFunction %float None %11
+%color_0 = OpFunctionParameter %_ptr_Function_v4float
+%bb_entry_0 = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+%22 = OpLoad %v4float %color_0
+OpStore %v %22
+%23 = OpLoad %v4float %v
+%24 = OpFMul %v4float %23 %6
+OpStore %v %24
+%26 = OpAccessChain %_ptr_Function_float %v %int_0
+%27 = OpLoad %float %26
+OpReturnValue %27
+OpFunctionEnd
+ )";
+
+ const std::string after =
+ R"(OpCapability SampledBuffer
+OpCapability ImageBuffer
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpSource HLSL 630
+OpName %type_buffer_image "type.buffer.image"
+OpName %output "output"
+OpName %main "main"
+OpName %color "color"
+OpName %bb_entry "bb.entry"
+OpName %param_var_color "param.var.color"
+OpName %fn "fn"
+OpName %color_0 "color"
+OpName %bb_entry_0 "bb.entry"
+OpName %v "v"
+OpDecorate %main LinkageAttributes "main" Export
+OpDecorate %output DescriptorSet 0
+OpDecorate %output Binding 1
+%float = OpTypeFloat 32
+%float_0_200000003 = OpConstant %float 0.200000003
+%v4float = OpTypeVector %float 4
+%6 = OpConstantComposite %v4float %float_0_200000003 %float_0_200000003 %float_0_200000003 %float_0_200000003
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%type_buffer_image = OpTypeImage %float Buffer 2 0 0 2 Rgba32f
+%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%11 = OpTypeFunction %float %_ptr_Function_v4float
+%_ptr_Function_float = OpTypePointer Function %float
+%output = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant
+%main = OpFunction %float None %11
+%color = OpFunctionParameter %_ptr_Function_v4float
+%bb_entry = OpLabel
+%28 = OpVariable %_ptr_Function_v4float Function
+%29 = OpVariable %_ptr_Function_float Function
+%param_var_color = OpVariable %_ptr_Function_v4float Function
+%16 = OpLoad %v4float %color
+OpStore %param_var_color %16
+%31 = OpLoad %v4float %param_var_color
+OpStore %28 %31
+%32 = OpLoad %v4float %28
+%33 = OpFMul %v4float %32 %6
+OpStore %28 %33
+%34 = OpAccessChain %_ptr_Function_float %28 %int_0
+%35 = OpLoad %float %34
+OpStore %29 %35
+%17 = OpLoad %float %29
+OpReturnValue %17
+OpFunctionEnd
+%fn = OpFunction %float None %11
+%color_0 = OpFunctionParameter %_ptr_Function_v4float
+%bb_entry_0 = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+%22 = OpLoad %v4float %color_0
+OpStore %v %22
+%23 = OpLoad %v4float %v
+%24 = OpFMul %v4float %23 %6
+OpStore %v %24
+%26 = OpAccessChain %_ptr_Function_float %v %int_0
+%27 = OpLoad %float %26
+OpReturnValue %27
+OpFunctionEnd
+)";
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndCheck<InlineExhaustivePass>(before, after, false, true);
+}
+
TEST_F(InlineTest, InlineFuncWithOpTerminateRayNotInContinue) {
const std::string text =
R"(
@@ -3259,6 +3383,82 @@ TEST_F(InlineTest, DebugSimple) {
SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
}
+TEST_F(InlineTest, ShaderDebugSimple) {
+ // Same as DebugSimple but for NonSemantic.Shader.DebugInfo.100.
+ const std::string text = R"(
+; CHECK: [[main_name:%\d+]] = OpString "main"
+; CHECK: [[foo_name:%\d+]] = OpString "foo"
+; CHECK: [[dbg_main:%\d+]] = OpExtInst %void {{%\d+}} DebugFunction [[main_name]] {{%\d+}} {{%\d+}} %uint_4 %uint_1 {{%\d+}} [[main_name]] %uint_3 %uint_4
+; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void {{%\d+}} DebugFunction [[foo_name]] {{%\d+}} {{%\d+}} %uint_1 %uint_1 {{%\d+}} [[foo_name]] %uint_3 %uint_1
+; CHECK: [[foo_bb:%\d+]] = OpExtInst %void {{%\d+}} DebugLexicalBlock {{%\d+}} %uint_1 %uint_14 [[dbg_foo]]
+; CHECK: [[inlined_at:%\d+]] = OpExtInst %void {{%\d+}} DebugInlinedAt %uint_4 [[dbg_main]]
+; CHECK: [[main:%\d+]] = OpFunction %void None
+; CHECK: {{%\d+}} = OpExtInst %void {{%\d+}} DebugScope [[foo_bb]] [[inlined_at]]
+; CHECK: [[foo:%\d+]] = OpFunction %v4float None
+ OpCapability Shader
+ OpExtension "SPV_KHR_non_semantic_info"
+ %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %3 %4
+ OpExecutionMode %main OriginUpperLeft
+ %5 = OpString "ps.hlsl"
+ OpSource HLSL 600 %5
+ %6 = OpString "float"
+ %main_name = OpString "main"
+ %foo_name = OpString "foo"
+ OpDecorate %3 Location 0
+ OpDecorate %4 Location 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
+ %uint_4 = OpConstant %uint 4
+ %uint_5 = OpConstant %uint 5
+ %uint_14 = OpConstant %uint 14
+ %uint_32 = OpConstant %uint 32
+ %float = OpTypeFloat 32
+ %float_1 = OpConstant %float 1
+ %v4float = OpTypeVector %float 4
+ %14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %18 = OpTypeFunction %void
+ %19 = OpTypeFunction %v4float
+ %3 = OpVariable %_ptr_Input_v4float Input
+ %4 = OpVariable %_ptr_Output_v4float Output
+ %20 = OpExtInst %void %1 DebugSource %5
+ %21 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %20 %uint_5
+ %22 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 %uint_3 %uint_0
+ %23 = OpExtInst %void %1 DebugTypeVector %22 %uint_4
+ %24 = OpExtInst %void %1 DebugTypeFunction %uint_3 %23 %23
+ %25 = OpExtInst %void %1 DebugTypeFunction %uint_3 %23
+ %dbg_main = OpExtInst %void %1 DebugFunction %main_name %24 %20 %uint_4 %uint_1 %21 %main_name %uint_3 %uint_4
+ %dbg_foo = OpExtInst %void %1 DebugFunction %foo_name %25 %20 %uint_1 %uint_1 %21 %foo_name %uint_3 %uint_1
+ %29 = OpExtInst %void %1 DebugLexicalBlock %20 %uint_1 %uint_14 %dbg_foo
+ %main = OpFunction %void None %18
+ %30 = OpLabel
+%dbg_main_def = OpExtInst %void %1 DebugFunctionDefinition %dbg_main %main
+ %31 = OpExtInst %void %1 DebugScope %dbg_main
+ %32 = OpFunctionCall %v4float %foo
+ %33 = OpLoad %v4float %3
+ %34 = OpFAdd %v4float %32 %33
+ OpStore %4 %34
+ OpReturn
+ OpFunctionEnd
+ %foo = OpFunction %v4float None %19
+ %36 = OpLabel
+%dbg_foo_def = OpExtInst %void %1 DebugFunctionDefinition %dbg_foo %foo
+ %35 = OpExtInst %void %1 DebugScope %dbg_foo
+ %37 = OpExtInst %void %1 DebugScope %29
+ OpReturnValue %14
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
+}
+
TEST_F(InlineTest, DebugNested) {
// When function main() calls function zoo() and function zoo() calls
// function bar() and function bar() calls function foo(), check that
@@ -3462,6 +3662,103 @@ float4 main(float4 color : COLOR) : SV_TARGET {
SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
}
+TEST_F(InlineTest, ShaderDebugSimpleHLSLPixelShader) {
+ // Same as DebugSimpleHLSLPixelShader but for
+ // NonSemantic.Shader.DebugInfo.100.
+ const std::string text = R"(
+; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} %uint_1 %uint_1 {{%\d+}} {{%\d+}} %uint_3 %uint_1
+; CHECK: [[lex_blk:%\d+]] = OpExtInst %void [[ext]] DebugLexicalBlock {{%\d+}} %uint_1 %uint_47 [[dbg_main]]
+; CHECK: %main = OpFunction %void None
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]]
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare {{%\d+}} %param_var_color
+; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[lex_blk]]
+; CHECK: {{%\d+}} = OpExtInst %void %1 DebugLine {{%\d+}} %uint_2 %uint_2 %uint_10 %uint_10
+; CHECK: {{%\d+}} = OpLoad %v4float %param_var_color
+; CHECK: {{%\d+}} = OpExtInst %void %1 DebugLine {{%\d+}} %uint_2 %uint_2 %uint_3 %uint_3
+; CHECK: OpFunctionEnd
+; CHECK: %src_main = OpFunction %v4float None
+ OpCapability Shader
+ OpExtension "SPV_KHR_non_semantic_info"
+ %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET
+ OpExecutionMode %main OriginUpperLeft
+ %5 = OpString "ps.hlsl"
+ OpSource HLSL 600 %5
+ %14 = OpString "#line 1 \"ps.hlsl\"
+float4 main(float4 color : COLOR) : SV_TARGET {
+ return color;
+}
+"
+ %17 = OpString "float"
+ %21 = OpString "src.main"
+ %24 = OpString "color"
+ OpName %in_var_COLOR "in.var.COLOR"
+ OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+ OpName %main "main"
+ OpName %param_var_color "param.var.color"
+ OpName %src_main "src.main"
+ OpName %color "color"
+ OpName %bb_entry "bb.entry"
+ OpDecorate %in_var_COLOR Location 0
+ OpDecorate %out_var_SV_TARGET Location 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
+ %uint_4 = OpConstant %uint 4
+ %uint_5 = OpConstant %uint 5
+ %uint_10 = OpConstant %uint 10
+ %uint_20 = OpConstant %uint 20
+ %uint_32 = OpConstant %uint 32
+ %uint_47 = OpConstant %uint 47
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %27 = OpTypeFunction %void
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+ %33 = OpTypeFunction %v4float %_ptr_Function_v4float
+%in_var_COLOR = OpVariable %_ptr_Input_v4float Input
+%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+ %13 = OpExtInst %void %1 DebugExpression
+ %15 = OpExtInst %void %1 DebugSource %5 %14
+ %16 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %15 %uint_5
+ %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 %uint_3 %uint_0
+ %19 = OpExtInst %void %1 DebugTypeVector %18 %uint_4
+ %20 = OpExtInst %void %1 DebugTypeFunction %uint_3 %19 %19
+ %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 %uint_1 %uint_1 %16 %21 %uint_3 %uint_1
+ %25 = OpExtInst %void %1 DebugLocalVariable %24 %19 %15 %uint_1 %uint_20 %22 %uint_4 %uint_0
+ %26 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_1 %uint_47 %22
+ %main = OpFunction %void None %27
+ %28 = OpLabel
+%param_var_color = OpVariable %_ptr_Function_v4float Function
+ %31 = OpLoad %v4float %in_var_COLOR
+ OpStore %param_var_color %31
+ %32 = OpFunctionCall %v4float %src_main %param_var_color
+ OpStore %out_var_SV_TARGET %32
+ OpReturn
+ OpFunctionEnd
+ %src_main = OpFunction %v4float None %33
+ %color = OpFunctionParameter %_ptr_Function_v4float
+ %bb_entry = OpLabel
+ %140 = OpExtInst %void %1 DebugFunctionDefinition %22 %src_main
+ %141 = OpExtInst %void %1 DebugLine %5 %uint_1 %uint_1 %uint_1 %uint_1
+ %34 = OpExtInst %void %1 DebugScope %22
+ %36 = OpExtInst %void %1 DebugDeclare %25 %color %13
+ %38 = OpExtInst %void %1 DebugScope %26
+ %142 = OpExtInst %void %1 DebugLine %5 %uint_2 %uint_2 %uint_10 %uint_10
+ %39 = OpLoad %v4float %color
+ %143 = OpExtInst %void %1 DebugLine %5 %uint_2 %uint_2 %uint_3 %uint_3
+ OpReturnValue %39
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
+}
+
TEST_F(InlineTest, DebugDeclareForCalleeFunctionParam) {
// Check that InlinePass correctly generates DebugDeclare instructions
// for callee function's parameters and maps them to corresponding
@@ -3937,6 +4234,105 @@ OpFunctionEnd
SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
}
+TEST_F(InlineTest, CreateConstantForInlinedAt) {
+ // This shader causes CreateDebugInlinedAt to generate a constant.
+ // Using the Constant manager would attempt to build the invalidated
+ // DefUse manager during inlining which could cause an assert because
+ // the function is in an inconsistent state. This test verifies that
+ // CreateDebugInlinedAt detects that the DefUse manager is disabled
+ // and creates a duplicate constant safely without the Constant manager.
+ //
+ // int function1() {
+ // return 1;
+ // }
+ //
+ // void main() {
+ // function1();
+ // }
+
+ const std::string text = R"(OpCapability Shader
+; CHECK: %uint_7 = OpConstant %uint 7
+; CHECK: %uint_7_0 = OpConstant %uint 7
+; CHECK: OpExtInst %void %1 DebugInlinedAt %uint_7_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 "parent3.hlsl"
+%8 = OpString "int"
+%19 = OpString "function1"
+%20 = 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_2 = OpConstant %uint 2
+%uint_17 = OpConstant %uint 17
+%uint_6 = OpConstant %uint 6
+%uint_13 = OpConstant %uint 13
+%uint_7 = OpConstant %uint 7
+%31 = OpTypeFunction %void
+%42 = OpTypeFunction %int
+%10 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 %uint_4 %uint_0
+%13 = OpExtInst %void %1 DebugTypeFunction %uint_3 %10
+%15 = OpExtInst %void %1 DebugSource %3
+%16 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %15 %uint_5
+%21 = OpExtInst %void %1 DebugFunction %19 %13 %15 %uint_2 %uint_1 %16 %20 %uint_3 %uint_2
+%23 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_2 %uint_17 %21
+%25 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void
+%27 = OpExtInst %void %1 DebugFunction %26 %25 %15 %uint_6 %uint_1 %16 %20 %uint_3 %uint_6
+%29 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_6 %uint_13 %27
+%main = OpFunction %void None %31
+%32 = OpLabel
+%33 = OpFunctionCall %void %src_main
+OpLine %3 8 1
+OpReturn
+OpFunctionEnd
+OpLine %3 6 1
+%src_main = OpFunction %void None %31
+OpNoLine
+%bb_entry = OpLabel
+%47 = OpExtInst %void %1 DebugScope %27
+%37 = OpExtInst %void %1 DebugFunctionDefinition %27 %src_main
+%48 = OpExtInst %void %1 DebugScope %29
+OpLine %3 7 3
+%39 = OpFunctionCall %int %function1
+%49 = OpExtInst %void %1 DebugScope %27
+OpLine %3 8 1
+OpReturn
+%50 = OpExtInst %void %1 DebugNoScope
+OpFunctionEnd
+OpLine %3 2 1
+%function1 = OpFunction %int None %42
+OpNoLine
+%bb_entry_0 = OpLabel
+%51 = OpExtInst %void %1 DebugScope %21
+%45 = OpExtInst %void %1 DebugFunctionDefinition %21 %function1
+%52 = OpExtInst %void %1 DebugScope %23
+OpLine %3 3 3
+OpReturnValue %int_1
+%53 = OpExtInst %void %1 DebugNoScope
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_2);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ 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 1a42329b..4c271dee 100644
--- a/test/opt/inst_bindless_check_test.cpp
+++ b/test/opt/inst_bindless_check_test.cpp
@@ -9984,8 +9984,8 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) {
//
// Compute shader
// Geometry shader
-// Tesselation control shader
-// Tesselation eval shader
+// Tessellation control shader
+// Tessellation eval shader
// OpImage
// SampledImage variable
diff --git a/test/opt/inst_buff_addr_check_test.cpp b/test/opt/inst_buff_addr_check_test.cpp
index 41ead67a..95114b23 100644
--- a/test/opt/inst_buff_addr_check_test.cpp
+++ b/test/opt/inst_buff_addr_check_test.cpp
@@ -615,6 +615,212 @@ OpFunctionEnd
true, 7u, 23u);
}
+TEST_F(InstBuffAddrTest, StructLoad) {
+ // #version 450
+ // #extension GL_EXT_buffer_reference : enable
+ // #extension GL_ARB_gpu_shader_int64 : enable
+ // struct Test {
+ // float a;
+ // };
+ //
+ // layout(buffer_reference, std430, buffer_reference_align = 16) buffer
+ // TestBuffer { Test test; };
+ //
+ // Test GetTest(uint64_t ptr) {
+ // return TestBuffer(ptr).test;
+ // }
+ //
+ // void main() {
+ // GetTest(0xe0000000);
+ // }
+
+ const std::string defs =
+ R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability PhysicalStorageBufferAddresses
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Fragment %main "main"
+; CHECK: OpEntryPoint Fragment %main "main" %60 %99 %gl_FragCoord
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+OpSourceExtension "GL_ARB_gpu_shader_int64"
+OpSourceExtension "GL_EXT_buffer_reference"
+OpName %main "main"
+OpName %Test "Test"
+OpMemberName %Test 0 "a"
+OpName %Test_0 "Test"
+OpMemberName %Test_0 0 "a"
+OpName %TestBuffer "TestBuffer"
+OpMemberName %TestBuffer 0 "test"
+)";
+
+ 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
+; 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
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+)";
+
+ const std::string globals =
+ R"(
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%ulong = OpTypeInt 64 0
+%float = OpTypeFloat 32
+%Test = OpTypeStruct %float
+OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_TestBuffer PhysicalStorageBuffer
+%Test_0 = OpTypeStruct %float
+%TestBuffer = OpTypeStruct %Test_0
+%_ptr_PhysicalStorageBuffer_TestBuffer = OpTypePointer PhysicalStorageBuffer %TestBuffer
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%_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
+; CHECK: %90 = OpTypeFunction %void %uint %uint %uint %uint
+; CHECK: %_struct_97 = OpTypeStruct %uint %_runtimearr_uint
+; CHECK: %99 = OpVariable %_ptr_StorageBuffer__struct_97 StorageBuffer
+; CHECK: %143 = OpConstantNull %Test_0
+)";
+
+ const std::string main =
+ R"(
+%main = OpFunction %void None %3
+%5 = OpLabel
+%37 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_TestBuffer %ulong_18446744073172680704
+%38 = OpAccessChain %_ptr_PhysicalStorageBuffer_Test_0 %37 %int_0
+%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: OpSelectionMerge %81 None
+; CHECK: OpBranchConditional %80 %82 %83
+; CHECK: %82 = OpLabel
+; CHECK: %84 = OpLoad %Test_0 %38 Aligned 16
+; CHECK: OpBranch %81
+; CHECK: %83 = OpLabel
+; 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: OpBranch %81
+; CHECK: %81 = OpLabel
+; CHECK: %144 = OpPhi %Test_0 %84 %82 %143 %83
+%40 = OpCopyLogical %Test %39
+; CHECK-NOT: %40 = OpCopyLogical %Test %39
+; CHECK: %40 = OpCopyLogical %Test %144
+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
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_2);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<InstBuffAddrCheckPass>(
+ defs + decorates + globals + main + output_funcs, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/inst_debug_printf_test.cpp b/test/opt/inst_debug_printf_test.cpp
index 8123ffbb..c5fd6799 100644
--- a/test/opt/inst_debug_printf_test.cpp
+++ b/test/opt/inst_debug_printf_test.cpp
@@ -206,8 +206,8 @@ OpFunctionEnd
//
// Compute shader
// Geometry shader
-// Tesselation control shader
-// Tesselation eval shader
+// Tessellation control shader
+// Tessellation eval shader
// Vertex shader
} // namespace
diff --git a/test/opt/instruction_test.cpp b/test/opt/instruction_test.cpp
index c5b92efb..2a48134d 100644
--- a/test/opt/instruction_test.cpp
+++ b/test/opt/instruction_test.cpp
@@ -62,12 +62,6 @@ TEST(InstructionTest, CreateWithOpcodeAndNoOperands) {
EXPECT_EQ(inst.end(), inst.begin());
}
-TEST(InstructionTest, OperandAsCString) {
- Operand::OperandData abcde{0x64636261, 0x65};
- Operand operand(SPV_OPERAND_TYPE_LITERAL_STRING, std::move(abcde));
- EXPECT_STREQ("abcde", operand.AsCString());
-}
-
TEST(InstructionTest, OperandAsString) {
Operand::OperandData abcde{0x64636261, 0x65};
Operand operand(SPV_OPERAND_TYPE_LITERAL_STRING, std::move(abcde));
diff --git a/test/opt/ir_loader_test.cpp b/test/opt/ir_loader_test.cpp
index 475dd235..ccdd032e 100644
--- a/test/opt/ir_loader_test.cpp
+++ b/test/opt/ir_loader_test.cpp
@@ -105,6 +105,22 @@ TEST(IrBuilder, RoundTripIncompleteFunction) {
DoRoundTripCheck("%2 = OpFunction %1 None %3\n");
}
+TEST(IrBuilder, RoundTripFunctionPointer) {
+ DoRoundTripCheck(
+ "OpCapability Linkage\n"
+ "OpCapability FunctionPointersINTEL\n"
+ "OpName %some_function \"some_function\"\n"
+ "OpName %ptr_to_function \"ptr_to_function\"\n"
+ "OpDecorate %some_function LinkageAttributes \"some_function\" Import\n"
+ "%float = OpTypeFloat 32\n"
+ "%4 = OpTypeFunction %float %float\n"
+ "%_ptr_Function_4 = OpTypePointer Function %4\n"
+ "%ptr_to_function = OpConstantFunctionPointerINTEL %_ptr_Function_4 "
+ "%some_function\n"
+ "%some_function = OpFunction %float Const %4\n"
+ "%6 = OpFunctionParameter %float\n"
+ "OpFunctionEnd\n");
+}
TEST(IrBuilder, KeepLineDebugInfo) {
// #version 310 es
// void main() {}
diff --git a/test/opt/local_single_block_elim.cpp b/test/opt/local_single_block_elim.cpp
index 8e1cee61..28b8a07d 100644
--- a/test/opt/local_single_block_elim.cpp
+++ b/test/opt/local_single_block_elim.cpp
@@ -84,6 +84,56 @@ OpFunctionEnd
predefs_before + before, predefs_before + after, true, true);
}
+TEST_F(LocalSingleBlockLoadStoreElimTest, LSBElimForLinkage) {
+ const std::string predefs_before =
+ R"(OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpSource HLSL 630
+OpName %main "main"
+OpName %v "v"
+OpName %BaseColor "BaseColor"
+OpName %gl_FragColor "gl_FragColor"
+OpDecorate %main LinkageAttributes "main" Export
+%void = OpTypeVoid
+%7 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+)";
+
+ const std::string before =
+ R"(%main = OpFunction %void None %7
+%13 = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+%14 = OpLoad %v4float %BaseColor
+OpStore %v %14
+%15 = OpLoad %v4float %v
+OpStore %gl_FragColor %15
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string after =
+ R"(%main = OpFunction %void None %7
+%13 = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+%14 = OpLoad %v4float %BaseColor
+OpStore %v %14
+OpStore %gl_FragColor %14
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<LocalSingleBlockLoadStoreElimPass>(
+ predefs_before + before, predefs_before + after, true, true);
+}
+
TEST_F(LocalSingleBlockLoadStoreElimTest, SimpleLoadLoadElim) {
// #version 140
//
diff --git a/test/opt/local_single_store_elim_test.cpp b/test/opt/local_single_store_elim_test.cpp
index c94ff372..5d910c4e 100644
--- a/test/opt/local_single_store_elim_test.cpp
+++ b/test/opt/local_single_store_elim_test.cpp
@@ -126,6 +126,91 @@ OpFunctionEnd
predefs + after, true, true);
}
+TEST_F(LocalSingleStoreElimTest, LSSElimForLinkage) {
+ const std::string predefs =
+ R"(OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpSource HLSL 630
+OpName %main "main"
+OpName %v "v"
+OpName %BaseColor "BaseColor"
+OpName %f "f"
+OpName %fi "fi"
+OpName %gl_FragColor "gl_FragColor"
+OpDecorate %main LinkageAttributes "main" Export
+%void = OpTypeVoid
+%9 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%_ptr_Function_float = OpTypePointer Function %float
+%_ptr_Input_float = OpTypePointer Input %float
+%fi = OpVariable %_ptr_Input_float Input
+%float_0 = OpConstant %float 0
+%bool = OpTypeBool
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+)";
+
+ const std::string before =
+ R"(%main = OpFunction %void None %9
+%19 = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+%f = OpVariable %_ptr_Function_float Function
+%20 = OpLoad %v4float %BaseColor
+OpStore %v %20
+%21 = OpLoad %float %fi
+OpStore %f %21
+%22 = OpLoad %float %f
+%23 = OpFOrdLessThan %bool %22 %float_0
+OpSelectionMerge %24 None
+OpBranchConditional %23 %25 %24
+%25 = OpLabel
+OpStore %f %float_0
+OpBranch %24
+%24 = OpLabel
+%26 = OpLoad %v4float %v
+%27 = OpLoad %float %f
+%28 = OpCompositeConstruct %v4float %27 %27 %27 %27
+%29 = OpFAdd %v4float %26 %28
+OpStore %gl_FragColor %29
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string after =
+ R"(%main = OpFunction %void None %9
+%19 = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+%f = OpVariable %_ptr_Function_float Function
+%20 = OpLoad %v4float %BaseColor
+OpStore %v %20
+%21 = OpLoad %float %fi
+OpStore %f %21
+%22 = OpLoad %float %f
+%23 = OpFOrdLessThan %bool %22 %float_0
+OpSelectionMerge %24 None
+OpBranchConditional %23 %25 %24
+%25 = OpLabel
+OpStore %f %float_0
+OpBranch %24
+%24 = OpLabel
+%27 = OpLoad %float %f
+%28 = OpCompositeConstruct %v4float %27 %27 %27 %27
+%29 = OpFAdd %v4float %20 %28
+OpStore %gl_FragColor %29
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<LocalSingleStoreElimPass>(predefs + before,
+ predefs + after, true, true);
+}
+
TEST_F(LocalSingleStoreElimTest, ThreeStores) {
// Three stores to multiple loads of v is not optimized.
diff --git a/test/opt/local_ssa_elim_test.cpp b/test/opt/local_ssa_elim_test.cpp
index ca9aba33..4b7542fe 100644
--- a/test/opt/local_ssa_elim_test.cpp
+++ b/test/opt/local_ssa_elim_test.cpp
@@ -2165,6 +2165,140 @@ OpFunctionEnd
SinglePassRunAndMatch<SSARewritePass>(text, true);
}
+TEST_F(LocalSSAElimTest, ShaderDebugForLoop) {
+ const std::string text = R"(
+; CHECK: [[f_name:%\w+]] = OpString "f"
+; CHECK: [[i_name:%\w+]] = OpString "i"
+; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugLocalVariable [[f_name]]
+; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]]
+
+; CHECK: OpStore %f %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
+
+; CHECK-NOT: DebugDeclare
+
+; CHECK: [[loop_head:%\w+]] = OpLabel
+; CHECK: [[phi0:%\w+]] = OpPhi %float %float_0
+; CHECK: [[phi1:%\w+]] = OpPhi %int %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[phi0]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[phi1]]
+; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] None
+; CHECK-NEXT: OpBranch [[loop_body:%\w+]]
+
+; CHECK-NEXT: [[loop_body]] = OpLabel
+; CHECK: OpBranchConditional {{%\w+}} [[bb:%\w+]] [[loop_merge]]
+
+; CHECK: [[bb]] = OpLabel
+; CHECK: OpStore %f [[f_val:%\w+]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_val]]
+; CHECK-NEXT: OpBranch [[loop_cont]]
+
+; CHECK: [[loop_cont]] = OpLabel
+; CHECK: OpStore %i [[i_val:%\w+]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[i_val]]
+; CHECK-NEXT: OpBranch [[loop_head]]
+
+; CHECK: [[loop_merge]] = OpLabel
+
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BC %fo
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+OpSource GLSL 140
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+%i_name = OpString "i"
+OpName %main "main"
+OpName %f "f"
+OpName %i "i"
+OpName %BC "BC"
+OpName %fo "fo"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%uint_3 = OpConstant %uint 3
+%uint_4 = OpConstant %uint 4
+%uint_5 = OpConstant %uint 5
+%uint_10 = OpConstant %uint 10
+%uint_32 = OpConstant %uint 32
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BC = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit %uint_1 %uint_4 %src %uint_5
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 %uint_3 %uint_0
+%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf %uint_4
+%main_ty = OpExtInst %void %ext DebugTypeFunction %uint_3 %dbg_v4f %dbg_v4f
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src %uint_0 %uint_0 %cu %main_name %uint_3 %uint_10
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src %uint_0 %uint_0 %dbg_main %uint_4
+%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src %uint_0 %uint_0 %dbg_main %uint_4
+%main = OpFunction %void None %8
+%22 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%f = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+OpStore %f %float_0
+OpStore %i %int_0
+%decl0 = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr
+%decl1 = OpExtInst %void %ext DebugDeclare %dbg_i %i %null_expr
+OpBranch %23
+%23 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpLoopMerge %24 %25 None
+OpBranch %26
+%26 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%27 = OpLoad %int %i
+%28 = OpSLessThan %bool %27 %int_4
+OpBranchConditional %28 %29 %24
+%29 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%30 = OpLoad %float %f
+%31 = OpLoad %int %i
+%32 = OpAccessChain %_ptr_Input_float %BC %31
+%33 = OpLoad %float %32
+%34 = OpFAdd %float %30 %33
+OpStore %f %34
+OpBranch %25
+%25 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%35 = OpLoad %int %i
+%36 = OpIAdd %int %35 %int_1
+OpStore %i %36
+OpBranch %23
+%24 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpLoad %float %f
+OpStore %fo %37
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
TEST_F(LocalSSAElimTest, AddDebugValueForFunctionParameterWithPhi) {
// Test the distribution of DebugValue for a parameter of an inlined function
// and the visibility of Phi instruction. The ssa-rewrite pass must add
@@ -4091,6 +4225,32 @@ TEST_F(LocalSSAElimTest, PointerVariables) {
SinglePassRunAndMatch<SSARewritePass>(text, true);
}
+TEST_F(LocalSSAElimTest, FunctionDeclaration) {
+ // Make sure the pass works with a function declaration that is called.
+ const std::string text = R"(OpCapability Addresses
+OpCapability Linkage
+OpCapability Kernel
+OpCapability Int8
+%1 = OpExtInstImport "OpenCL.std"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool"
+OpExecutionMode %2 ContractionOff
+OpSource Unknown 0
+OpDecorate %3 LinkageAttributes "julia_error_7712" Import
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%3 = OpFunction %void None %5
+OpFunctionEnd
+%2 = OpFunction %void None %5
+%6 = OpLabel
+%7 = OpFunctionCall %void %3
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<SSARewritePass>(text, text, false);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// No optimization in the presence of
diff --git a/test/opt/loop_optimizations/loop_descriptions.cpp b/test/opt/loop_optimizations/loop_descriptions.cpp
index 4d2f989a..b3f4f440 100644
--- a/test/opt/loop_optimizations/loop_descriptions.cpp
+++ b/test/opt/loop_optimizations/loop_descriptions.cpp
@@ -298,7 +298,7 @@ TEST_F(PassClassTest, NoLoop) {
/*
Generated from following GLSL with latch block artificially inserted to be
-seperate from continue.
+separate from continue.
#version 430
void main(void) {
float x[10];
diff --git a/test/opt/loop_optimizations/loop_fission.cpp b/test/opt/loop_optimizations/loop_fission.cpp
index 55b9c263..bc3ec39b 100644
--- a/test/opt/loop_optimizations/loop_fission.cpp
+++ b/test/opt/loop_optimizations/loop_fission.cpp
@@ -692,7 +692,7 @@ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
SinglePassRunAndCheck<LoopFissionPass>(source, expected, true);
// By passing 1 as argument we are using the constructor which makes the
-// critera to split the loop be if the registers in the loop exceede 1. By
+// criteria to split the loop be if the registers in the loop exceede 1. By
// using this constructor we are also enabling multiple passes (disabled by
// default).
SinglePassRunAndCheck<LoopFissionPass>(source, expected_multiple_passes, true,
diff --git a/test/opt/loop_optimizations/unroll_assumptions.cpp b/test/opt/loop_optimizations/unroll_assumptions.cpp
index 0f933021..159e4a14 100644
--- a/test/opt/loop_optimizations/unroll_assumptions.cpp
+++ b/test/opt/loop_optimizations/unroll_assumptions.cpp
@@ -42,6 +42,10 @@ class PartialUnrollerTestPass : public Pass {
Status Process() override {
bool changed = false;
for (Function& f : *context()->module()) {
+ if (f.IsDeclaration()) {
+ continue;
+ }
+
LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(&f);
for (auto& loop : loop_descriptor) {
LoopUtils loop_utils{context(), &loop};
@@ -1510,6 +1514,33 @@ OpFunctionEnd
SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(text, text, false);
}
+TEST_F(PassClassTest, FunctionDeclaration) {
+ // Make sure the pass works with a function declaration that is called.
+ const std::string text = R"(OpCapability Addresses
+OpCapability Linkage
+OpCapability Kernel
+OpCapability Int8
+%1 = OpExtInstImport "OpenCL.std"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool"
+OpExecutionMode %2 ContractionOff
+OpSource Unknown 0
+OpDecorate %3 LinkageAttributes "julia_error_7712" Import
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%3 = OpFunction %void None %5
+OpFunctionEnd
+%2 = OpFunction %void None %5
+%6 = OpLabel
+%7 = OpFunctionCall %void %3
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<LoopUnroller>(text, text, false);
+ SinglePassRunAndCheck<PartialUnrollerTestPass<1>>(text, text, false);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/loop_optimizations/unroll_simple.cpp b/test/opt/loop_optimizations/unroll_simple.cpp
index 6a3cb6ee..b72305c8 100644
--- a/test/opt/loop_optimizations/unroll_simple.cpp
+++ b/test/opt/loop_optimizations/unroll_simple.cpp
@@ -378,6 +378,185 @@ OpFunctionEnd)";
SinglePassRunAndMatch<LoopUnroller>(text, true);
}
+TEST_F(PassClassTest, SimpleFullyUnrollWithShaderDebugInstructions) {
+ // We must preserve the debug information including
+ // NonSemantic.Shader.DebugInfo.100 instructions and DebugLine instructions.
+ const std::string text = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main" %3
+OpExecutionMode %2 OriginUpperLeft
+OpSource GLSL 330
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+%i_name = OpString "i"
+OpName %2 "main"
+OpName %5 "x"
+OpName %3 "c"
+OpDecorate %3 Location 0
+%6 = OpTypeVoid
+%7 = OpTypeFunction %6
+%8 = OpTypeInt 32 1
+%9 = OpTypePointer Function %8
+%10 = OpConstant %8 0
+%11 = OpConstant %8 4
+%12 = OpTypeBool
+%13 = OpTypeFloat 32
+%14 = OpTypeInt 32 0
+%uint_0 = OpConstant %14 0
+%uint_1 = OpConstant %14 1
+%uint_2 = OpConstant %14 2
+%uint_3 = OpConstant %14 3
+%uint_4 = OpConstant %14 4
+%uint_5 = OpConstant %14 5
+%uint_6 = OpConstant %14 6
+%uint_7 = OpConstant %14 7
+%uint_8 = OpConstant %14 8
+%uint_10 = OpConstant %14 10
+%uint_32 = OpConstant %14 32
+%15 = OpConstant %14 4
+%16 = OpTypeArray %13 %15
+%17 = OpTypePointer Function %16
+%18 = OpConstant %13 1
+%19 = OpTypePointer Function %13
+%20 = OpConstant %8 1
+%21 = OpTypeVector %13 4
+%22 = OpTypePointer Output %21
+%3 = OpVariable %22 Output
+%null_expr = OpExtInst %6 %ext DebugExpression
+%deref = OpExtInst %6 %ext DebugOperation %uint_0
+%deref_expr = OpExtInst %6 %ext DebugExpression %deref
+%src = OpExtInst %6 %ext DebugSource %file_name
+%cu = OpExtInst %6 %ext DebugCompilationUnit %uint_1 %uint_4 %src %uint_5
+%dbg_tf = OpExtInst %6 %ext DebugTypeBasic %float_name %uint_32 %uint_3 %uint_0
+%dbg_v4f = OpExtInst %6 %ext DebugTypeVector %dbg_tf %uint_4
+%main_ty = OpExtInst %6 %ext DebugTypeFunction %uint_3 %dbg_v4f %dbg_v4f
+%dbg_main = OpExtInst %6 %ext DebugFunction %main_name %main_ty %src %uint_0 %uint_0 %cu %main_name %uint_3 %uint_10
+%bb = OpExtInst %6 %ext DebugLexicalBlock %src %uint_0 %uint_0 %dbg_main
+%dbg_f = OpExtInst %6 %ext DebugLocalVariable %f_name %dbg_v4f %src %uint_0 %uint_0 %dbg_main %uint_4
+%dbg_i = OpExtInst %6 %ext DebugLocalVariable %i_name %dbg_v4f %src %uint_1 %uint_0 %bb %uint_4
+
+; CHECK: [[f:%\w+]] = OpString "f"
+; CHECK: [[i:%\w+]] = OpString "i"
+; CHECK: [[int_0:%\w+]] = OpConstant {{%\w+}} 0
+
+; CHECK: [[null_expr:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugExpression
+; CHECK: [[deref:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugOperation %uint_0
+; CHECK: [[deref_expr:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugExpression [[deref]]
+; CHECK: [[dbg_fn:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugFunction
+; CHECK: [[dbg_bb:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLexicalBlock
+; CHECK: [[dbg_f:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable [[f]] {{%\w+}} {{%\w+}} %uint_0 %uint_0 [[dbg_fn]]
+; CHECK: [[dbg_i:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable [[i]] {{%\w+}} {{%\w+}} %uint_1 %uint_0 [[dbg_bb]]
+
+%2 = OpFunction %6 None %7
+%23 = OpLabel
+
+; The first block has DebugDeclare and DebugValue with Deref
+;
+; CHECK: OpLabel
+; CHECK: %x = OpVariable %_ptr_Function__arr_float_uint_4_0 Function
+; CHECK: OpBranch
+; CHECK: OpLabel
+; CHECK: DebugScope [[dbg_fn]]
+; CHECK: DebugValue [[dbg_f]] [[int_0]] [[null_expr]]
+; CHECK: OpBranch
+; CHECK: DebugScope [[dbg_fn]]
+; CHECK: DebugLine {{%\w+}} %uint_1 %uint_1 %uint_1 %uint_1
+; CHECK: OpSLessThan
+; CHECK: DebugLine {{%\w+}} %uint_2 %uint_2 %uint_0 %uint_0
+; CHECK: OpBranch
+; CHECK: OpLabel
+; CHECK: DebugScope [[dbg_bb]]
+; CHECK: DebugDeclare [[dbg_f]] %x [[null_expr]]
+; CHECK: DebugValue [[dbg_i]] %x [[deref_expr]]
+; CHECK: DebugLine {{%\w+}} %uint_3 %uint_3 %uint_0 %uint_0
+;
+; CHECK: DebugLine {{%\w+}} %uint_6 %uint_6 %uint_0 %uint_0
+; CHECK: [[add:%\w+]] = OpIAdd
+; CHECK: DebugValue [[dbg_f]] [[add]] [[null_expr]]
+; CHECK: DebugLine {{%\w+}} %uint_7 %uint_7 %uint_0 %uint_0
+
+; Other blocks do not have DebugDeclare and DebugValue with Deref
+;
+; CHECK: DebugScope [[dbg_fn]]
+; CHECK: DebugLine {{%\w+}} %uint_1 %uint_1 %uint_1 %uint_1
+; CHECK: OpSLessThan
+; CHECK: DebugLine {{%\w+}} %uint_2 %uint_2 %uint_0 %uint_0
+; CHECK: OpBranch
+; CHECK: OpLabel
+;
+; CHECK: DebugScope [[dbg_bb]]
+; CHECK-NOT: DebugDeclare [[dbg_f]] %x [[null_expr]]
+; CHECK-NOT: DebugValue [[dbg_i]] %x [[deref_expr]]
+; CHECK: DebugLine {{%\w+}} %uint_3 %uint_3 %uint_0 %uint_0
+;
+; CHECK: DebugLine {{%\w+}} %uint_6 %uint_6 %uint_0 %uint_0
+; CHECK: [[add:%\w+]] = OpIAdd
+; CHECK: DebugValue [[dbg_f]] [[add]] [[null_expr]]
+; CHECK: DebugLine {{%\w+}} %uint_7 %uint_7 %uint_0 %uint_0
+;
+; CHECK-NOT: DebugDeclare [[dbg_f]] %x [[null_expr]]
+; CHECK-NOT: DebugValue [[dbg_i]] %x [[deref_expr]]
+; CHECK: DebugScope [[dbg_fn]]
+; CHECK: DebugLine {{%\w+}} %uint_8 %uint_8 %uint_0 %uint_0
+; CHECK: OpReturn
+
+%5 = OpVariable %17 Function
+OpBranch %24
+%24 = OpLabel
+%35 = OpPhi %8 %10 %23 %34 %26
+%s1 = OpExtInst %6 %ext DebugScope %dbg_main
+%d10 = OpExtInst %6 %ext DebugLine %file_name %uint_1 %uint_1 %uint_0 %uint_0
+%value0 = OpExtInst %6 %ext DebugValue %dbg_f %35 %null_expr
+OpLoopMerge %25 %26 Unroll
+OpBranch %27
+%27 = OpLabel
+%s2 = OpExtInst %6 %ext DebugScope %dbg_main
+%d1 = OpExtInst %6 %ext DebugLine %file_name %uint_1 %uint_1 %uint_1 %uint_1
+%29 = OpSLessThan %12 %35 %11
+%d2 = OpExtInst %6 %ext DebugLine %file_name %uint_2 %uint_2 %uint_0 %uint_0
+OpBranchConditional %29 %30 %25
+%30 = OpLabel
+%s3 = OpExtInst %6 %ext DebugScope %bb
+%decl0 = OpExtInst %6 %ext DebugDeclare %dbg_f %5 %null_expr
+%decl1 = OpExtInst %6 %ext DebugValue %dbg_i %5 %deref_expr
+%d3 = OpExtInst %6 %ext DebugLine %file_name %uint_3 %uint_3 %uint_0 %uint_0
+%32 = OpAccessChain %19 %5 %35
+%d4 = OpExtInst %6 %ext DebugLine %file_name %uint_4 %uint_4 %uint_0 %uint_0
+OpStore %32 %18
+%d5 = OpExtInst %6 %ext DebugLine %file_name %uint_5 %uint_5 %uint_0 %uint_0
+OpBranch %26
+%26 = OpLabel
+%s4 = OpExtInst %6 %ext DebugScope %dbg_main
+%d6 = OpExtInst %6 %ext DebugLine %file_name %uint_6 %uint_6 %uint_0 %uint_0
+%34 = OpIAdd %8 %35 %20
+%value1 = OpExtInst %6 %ext DebugValue %dbg_f %34 %null_expr
+%d7 = OpExtInst %6 %ext DebugLine %file_name %uint_7 %uint_7 %uint_0 %uint_0
+OpBranch %24
+%25 = OpLabel
+%s5 = OpExtInst %6 %ext DebugScope %dbg_main
+%d8 = OpExtInst %6 %ext DebugLine %file_name %uint_8 %uint_8 %uint_0 %uint_0
+OpReturn
+OpFunctionEnd)";
+
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ Module* module = context->module();
+ EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
+ << text << std::endl;
+
+ LoopUnroller loop_unroller;
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ SinglePassRunAndMatch<LoopUnroller>(text, true);
+}
+
template <int factor>
class PartialUnrollerTestPass : public Pass {
public:
@@ -707,7 +886,7 @@ OpFunctionEnd
LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
// By unrolling by a factor that doesn't divide evenly into the number of loop
- // iterations we perfom an additional transform when partially unrolling to
+ // iterations we perform an additional transform when partially unrolling to
// account for the remainder.
SinglePassRunAndCheck<PartialUnrollerTestPass<3>>(text, output, false);
}
@@ -2939,7 +3118,7 @@ OpFunctionEnd
/*
Generated from following GLSL with latch block artificially inserted to be
-seperate from continue.
+separate from continue.
#version 430
void main(void) {
float x[10];
diff --git a/test/opt/module_test.cpp b/test/opt/module_test.cpp
index a3c2eed7..17a13650 100644
--- a/test/opt/module_test.cpp
+++ b/test/opt/module_test.cpp
@@ -52,7 +52,7 @@ inline std::unique_ptr<IRContext> BuildModule(std::string text) {
}
TEST(ModuleTest, ComputeIdBound) {
- // Emtpy module case.
+ // Empty module case.
EXPECT_EQ(1u, BuildModule("")->module()->ComputeIdBound());
// Sensitive to result id
EXPECT_EQ(2u, BuildModule("%void = OpTypeVoid")->module()->ComputeIdBound());
diff --git a/test/opt/optimizer_test.cpp b/test/opt/optimizer_test.cpp
index a51638a1..0171c09b 100644
--- a/test/opt/optimizer_test.cpp
+++ b/test/opt/optimizer_test.cpp
@@ -147,7 +147,7 @@ TEST(Optimizer, CanRegisterPassesFromFlags) {
std::vector<std::string> pass_flags = {
"--strip-debug",
- "--strip-reflect",
+ "--strip-nonsemantic",
"--set-spec-const-default-value=23:42 21:12",
"--if-conversion",
"--freeze-spec-const",
diff --git a/test/opt/pass_manager_test.cpp b/test/opt/pass_manager_test.cpp
index 22d5e22e..4f36d5b2 100644
--- a/test/opt/pass_manager_test.cpp
+++ b/test/opt/pass_manager_test.cpp
@@ -30,7 +30,7 @@ namespace {
using spvtest::GetIdBound;
using ::testing::Eq;
-// A null pass whose construtors accept arguments
+// A null pass whose constructors accept arguments
class NullPassWithArgs : public NullPass {
public:
NullPassWithArgs(uint32_t) {}
diff --git a/test/opt/pass_merge_return_test.cpp b/test/opt/pass_merge_return_test.cpp
index fd97efab..21960d17 100644
--- a/test/opt/pass_merge_return_test.cpp
+++ b/test/opt/pass_merge_return_test.cpp
@@ -2567,6 +2567,39 @@ TEST_F(MergeReturnPassTest, ChainedPointerUsedAfterLoop) {
SinglePassRunAndMatch<MergeReturnPass>(before, true);
}
+TEST_F(MergeReturnPassTest, OverflowTest1) {
+ const std::string text =
+ R"(
+; CHECK: OpReturn
+; CHECK-NOT: OpReturn
+; CHECK: OpFunctionEnd
+ OpCapability ClipDistance
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ %void = OpTypeVoid
+ %6 = OpTypeFunction %void
+ %2 = OpFunction %void None %6
+ %4194303 = OpLabel
+ OpBranch %18
+ %18 = OpLabel
+ OpLoopMerge %19 %20 None
+ OpBranch %21
+ %21 = OpLabel
+ OpReturn
+ %20 = OpLabel
+ OpBranch %18
+ %19 = OpLabel
+ OpUnreachable
+ OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ auto result =
+ SinglePassRunToBinary<MergeReturnPass>(text, /* skip_nop = */ true);
+ EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/reduce_load_size_test.cpp b/test/opt/reduce_load_size_test.cpp
index 7672e8f3..abb5cde6 100644
--- a/test/opt/reduce_load_size_test.cpp
+++ b/test/opt/reduce_load_size_test.cpp
@@ -17,6 +17,12 @@
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
+namespace {
+
+const double kDefaultLoadReductionThreshold = 0.9;
+
+} // namespace
+
namespace spvtools {
namespace opt {
namespace {
@@ -104,7 +110,8 @@ TEST_F(ReduceLoadSizeTest, cbuffer_load_extract) {
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
- SinglePassRunAndMatch<ReduceLoadSize>(test, false);
+ SinglePassRunAndMatch<ReduceLoadSize>(test, false,
+ kDefaultLoadReductionThreshold);
}
TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_not_affected_by_debug_instr) {
@@ -202,7 +209,8 @@ TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_not_affected_by_debug_instr) {
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
- SinglePassRunAndMatch<ReduceLoadSize>(test, false);
+ SinglePassRunAndMatch<ReduceLoadSize>(test, false,
+ kDefaultLoadReductionThreshold);
}
TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_vector) {
@@ -280,7 +288,8 @@ OpFunctionEnd
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
- SinglePassRunAndCheck<ReduceLoadSize>(test, test, true, false);
+ SinglePassRunAndCheck<ReduceLoadSize>(test, test, true, false,
+ kDefaultLoadReductionThreshold);
}
TEST_F(ReduceLoadSizeTest, cbuffer_load_5_extract) {
@@ -351,7 +360,8 @@ OpFunctionEnd
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
- SinglePassRunAndCheck<ReduceLoadSize>(test, test, true, false);
+ SinglePassRunAndCheck<ReduceLoadSize>(test, test, true, false,
+ kDefaultLoadReductionThreshold);
}
TEST_F(ReduceLoadSizeTest, cbuffer_load_fully_used) {
@@ -416,7 +426,76 @@ OpFunctionEnd
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
- SinglePassRunAndCheck<ReduceLoadSize>(test, test, true, false);
+ SinglePassRunAndCheck<ReduceLoadSize>(test, test, true, false,
+ kDefaultLoadReductionThreshold);
+}
+
+TEST_F(ReduceLoadSizeTest, replace_cbuffer_load_fully_used) {
+ const std::string test =
+ R"(
+ OpCapability Shader
+ OpCapability SampledBuffer
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %out_var_SV_Target0
+ OpExecutionMode %main OriginUpperLeft
+ OpSource HLSL 600
+ OpName %type_MaterialInstancing_cbuffer "type.MaterialInstancing_cbuffer"
+ OpMemberName %type_MaterialInstancing_cbuffer 0 "MaterialInstancing_constants"
+ OpName %MaterialInstancing_Constants "MaterialInstancing_Constants"
+ OpMemberName %MaterialInstancing_Constants 0 "offset0"
+ OpMemberName %MaterialInstancing_Constants 1 "params"
+ OpName %InstancingParams_Constants "InstancingParams_Constants"
+ OpMemberName %InstancingParams_Constants 0 "offset1"
+ OpName %MaterialInstancing_cbuffer "MaterialInstancing_cbuffer"
+ OpName %out_var_SV_Target0 "out.var.SV_Target0"
+ OpName %main "main"
+ OpDecorate %out_var_SV_Target0 Location 0
+ OpDecorate %MaterialInstancing_cbuffer DescriptorSet 6
+ OpDecorate %MaterialInstancing_cbuffer Binding 0
+ OpMemberDecorate %InstancingParams_Constants 0 Offset 0
+ OpMemberDecorate %MaterialInstancing_Constants 0 Offset 0
+ OpMemberDecorate %MaterialInstancing_Constants 1 Offset 16
+ OpMemberDecorate %type_MaterialInstancing_cbuffer 0 Offset 0
+ OpDecorate %type_MaterialInstancing_cbuffer Block
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %v4int = OpTypeVector %int 4
+%InstancingParams_Constants = OpTypeStruct %v4int
+%MaterialInstancing_Constants = OpTypeStruct %v4int %InstancingParams_Constants
+%type_MaterialInstancing_cbuffer = OpTypeStruct %MaterialInstancing_Constants
+%_ptr_Uniform_type_MaterialInstancing_cbuffer = OpTypePointer Uniform %type_MaterialInstancing_cbuffer
+%_ptr_Output_int = OpTypePointer Output %int
+ %void = OpTypeVoid
+ %60 = OpTypeFunction %void
+%_ptr_Uniform_MaterialInstancing_Constants = OpTypePointer Uniform %MaterialInstancing_Constants
+%MaterialInstancing_cbuffer = OpVariable %_ptr_Uniform_type_MaterialInstancing_cbuffer Uniform
+%out_var_SV_Target0 = OpVariable %_ptr_Output_int Output
+ %main = OpFunction %void None %60
+ %80 = OpLabel
+ %131 = OpAccessChain %_ptr_Uniform_MaterialInstancing_Constants %MaterialInstancing_cbuffer %int_0
+ %132 = OpLoad %MaterialInstancing_Constants %131
+; CHECK: [[ac1:%\w+]] = OpAccessChain {{%\w+}} %MaterialInstancing_cbuffer %int_0
+; CHECK: [[ac2:%\w+]] = OpAccessChain {{%\w+}} [[ac1]] %uint_0
+; CHECK: OpLoad %v4int [[ac2]]
+
+; CHECK: [[ac3:%\w+]] = OpAccessChain {{%\w+}} [[ac1]] %uint_1
+; CHECK: [[ac4:%\w+]] = OpAccessChain {{%\w+}} [[ac3]] %uint_0
+; CHECK: OpLoad %v4int [[ac4]]
+ %134 = OpCompositeExtract %v4int %132 0
+ %135 = OpCompositeExtract %InstancingParams_Constants %132 1
+ %136 = OpCompositeExtract %v4int %135 0
+ %149 = OpCompositeExtract %int %134 0
+ %185 = OpCompositeExtract %int %136 0
+ %156 = OpIAdd %int %149 %185
+ OpStore %out_var_SV_Target0 %156
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ SinglePassRunAndMatch<ReduceLoadSize>(test, false, 1.1);
}
} // namespace
diff --git a/test/opt/redundancy_elimination_test.cpp b/test/opt/redundancy_elimination_test.cpp
index 474f4661..28eda73e 100644
--- a/test/opt/redundancy_elimination_test.cpp
+++ b/test/opt/redundancy_elimination_test.cpp
@@ -335,6 +335,32 @@ TEST_F(RedundancyEliminationTest, OpenCLDebugInfo100) {
SinglePassRunAndMatch<RedundancyEliminationPass>(text, false);
}
+TEST_F(RedundancyEliminationTest, FunctionDeclaration) {
+ // Make sure the pass works with a function declaration that is called.
+ const std::string text = R"(OpCapability Addresses
+OpCapability Linkage
+OpCapability Kernel
+OpCapability Int8
+%1 = OpExtInstImport "OpenCL.std"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool"
+OpExecutionMode %2 ContractionOff
+OpSource Unknown 0
+OpDecorate %3 LinkageAttributes "julia_error_7712" Import
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%3 = OpFunction %void None %5
+OpFunctionEnd
+%2 = OpFunction %void None %5
+%6 = OpLabel
+%7 = OpFunctionCall %void %3
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<RedundancyEliminationPass>(text, text, false);
+}
+
} // namespace
} // namespace opt
-} // namespace spvtools
+} // namespace spvtools \ No newline at end of file
diff --git a/test/opt/relax_float_ops_test.cpp b/test/opt/relax_float_ops_test.cpp
index 14cde0b9..b9cb0de0 100644
--- a/test/opt/relax_float_ops_test.cpp
+++ b/test/opt/relax_float_ops_test.cpp
@@ -137,6 +137,86 @@ OpFunctionEnd
true);
}
+TEST_F(RelaxFloatOpsTest, RelaxFloatOpsForLinkage) {
+ const std::string defs0 =
+ R"(OpCapability Shader
+OpCapability Linkage
+OpCapability Sampled1D
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpSource HLSL 630
+OpName %main "main"
+OpName %g_tTex1df4 "g_tTex1df4"
+OpName %g_sSamp "g_sSamp"
+OpName %i_Tex0 "i.Tex0"
+OpName %i_Tex1 "i.Tex1"
+OpName %_entryPointOutput_Color "@entryPointOutput.Color"
+OpDecorate %main LinkageAttributes "main" Export
+OpDecorate %g_tTex1df4 DescriptorSet 0
+OpDecorate %g_tTex1df4 Binding 0
+OpDecorate %g_sSamp DescriptorSet 0
+OpDecorate %g_sSamp Binding 0
+OpDecorate %i_Tex0 Location 0
+OpDecorate %i_Tex1 Location 1
+OpDecorate %_entryPointOutput_Color Location 0
+)";
+
+ const std::string defs1 =
+ R"(%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%17 = OpTypeImage %float 1D 0 0 0 1 Unknown
+%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17
+%g_tTex1df4 = OpVariable %_ptr_UniformConstant_17 UniformConstant
+%21 = OpTypeSampler
+%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21
+%g_sSamp = OpVariable %_ptr_UniformConstant_21 UniformConstant
+%25 = OpTypeSampledImage %17
+%_ptr_Input_float = OpTypePointer Input %float
+%i_Tex0 = OpVariable %_ptr_Input_float Input
+%i_Tex1 = OpVariable %_ptr_Input_float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output
+%float_0_5 = OpConstant %float 0.5
+%116 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5
+)";
+
+ const std::string relax_decos =
+ R"(OpDecorate %60 RelaxedPrecision
+OpDecorate %63 RelaxedPrecision
+OpDecorate %82 RelaxedPrecision
+OpDecorate %88 RelaxedPrecision
+OpDecorate %91 RelaxedPrecision
+OpDecorate %94 RelaxedPrecision
+)";
+
+ const std::string func_orig =
+ R"(%main = OpFunction %void None %3
+%5 = OpLabel
+%60 = OpLoad %float %i_Tex0
+%63 = OpLoad %float %i_Tex1
+%77 = OpLoad %17 %g_tTex1df4
+%78 = OpLoad %21 %g_sSamp
+%79 = OpSampledImage %25 %77 %78
+%82 = OpImageSampleImplicitLod %v4float %79 %60
+%83 = OpLoad %17 %g_tTex1df4
+%84 = OpLoad %21 %g_sSamp
+%85 = OpSampledImage %25 %83 %84
+%88 = OpImageSampleImplicitLod %v4float %85 %63
+%91 = OpFAdd %v4float %82 %88
+%94 = OpFMul %v4float %91 %116
+OpStore %_entryPointOutput_Color %94
+OpReturn
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndCheck<RelaxFloatOpsPass>(
+ defs0 + defs1 + func_orig, defs0 + relax_decos + defs1 + func_orig, true,
+ true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/remove_unused_interface_variables_test.cpp b/test/opt/remove_unused_interface_variables_test.cpp
new file mode 100644
index 00000000..ddf027f1
--- /dev/null
+++ b/test/opt/remove_unused_interface_variables_test.cpp
@@ -0,0 +1,185 @@
+// Copyright (c) 2021 ZHOU He
+//
+// 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/assembly_builder.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using RemoveUnusedInterfaceVariablesTest = PassTest<::testing::Test>;
+
+static const std::string expected = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %_Z5func1v "_Z5func1v" %out_var_SV_TARGET
+OpEntryPoint Fragment %_Z5func2v "_Z5func2v" %out_var_SV_TARGET_0
+OpExecutionMode %_Z5func1v OriginUpperLeft
+OpExecutionMode %_Z5func2v OriginUpperLeft
+OpSource HLSL 630
+OpName %type_cba "type.cba"
+OpMemberName %type_cba 0 "color"
+OpName %cba "cba"
+OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+OpName %out_var_SV_TARGET_0 "out.var.SV_TARGET"
+OpName %_Z5func1v "_Z5func1v"
+OpName %_Z5func2v "_Z5func2v"
+OpDecorate %out_var_SV_TARGET Location 0
+OpDecorate %out_var_SV_TARGET_0 Location 0
+OpDecorate %cba DescriptorSet 0
+OpDecorate %cba Binding 0
+OpMemberDecorate %type_cba 0 Offset 0
+OpDecorate %type_cba Block
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%type_cba = OpTypeStruct %v4float
+%_ptr_Uniform_type_cba = OpTypePointer Uniform %type_cba
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%14 = OpTypeFunction %void
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+%cba = OpVariable %_ptr_Uniform_type_cba Uniform
+%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+%out_var_SV_TARGET_0 = OpVariable %_ptr_Output_v4float Output
+%_Z5func1v = OpFunction %void None %14
+%16 = OpLabel
+%17 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0
+%18 = OpLoad %v4float %17
+OpStore %out_var_SV_TARGET %18
+OpReturn
+OpFunctionEnd
+%_Z5func2v = OpFunction %void None %14
+%19 = OpLabel
+%20 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0
+%21 = OpLoad %v4float %20
+OpStore %out_var_SV_TARGET_0 %21
+OpReturn
+OpFunctionEnd
+)";
+
+TEST_F(RemoveUnusedInterfaceVariablesTest, RemoveUnusedVariable) {
+ const std::string text = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %_Z5func1v "_Z5func1v" %out_var_SV_TARGET %out_var_SV_TARGET_0
+OpEntryPoint Fragment %_Z5func2v "_Z5func2v" %out_var_SV_TARGET %out_var_SV_TARGET_0
+OpExecutionMode %_Z5func1v OriginUpperLeft
+OpExecutionMode %_Z5func2v OriginUpperLeft
+OpSource HLSL 630
+OpName %type_cba "type.cba"
+OpMemberName %type_cba 0 "color"
+OpName %cba "cba"
+OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+OpName %out_var_SV_TARGET_0 "out.var.SV_TARGET"
+OpName %_Z5func1v "_Z5func1v"
+OpName %_Z5func2v "_Z5func2v"
+OpDecorate %out_var_SV_TARGET Location 0
+OpDecorate %out_var_SV_TARGET_0 Location 0
+OpDecorate %cba DescriptorSet 0
+OpDecorate %cba Binding 0
+OpMemberDecorate %type_cba 0 Offset 0
+OpDecorate %type_cba Block
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%type_cba = OpTypeStruct %v4float
+%_ptr_Uniform_type_cba = OpTypePointer Uniform %type_cba
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%14 = OpTypeFunction %void
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+%cba = OpVariable %_ptr_Uniform_type_cba Uniform
+%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+%out_var_SV_TARGET_0 = OpVariable %_ptr_Output_v4float Output
+%_Z5func1v = OpFunction %void None %14
+%16 = OpLabel
+%17 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0
+%18 = OpLoad %v4float %17
+OpStore %out_var_SV_TARGET %18
+OpReturn
+OpFunctionEnd
+%_Z5func2v = OpFunction %void None %14
+%19 = OpLabel
+%20 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0
+%21 = OpLoad %v4float %20
+OpStore %out_var_SV_TARGET_0 %21
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<RemoveUnusedInterfaceVariablesPass>(text, expected,
+ true, true);
+}
+
+TEST_F(RemoveUnusedInterfaceVariablesTest, FixMissingVariable) {
+ const std::string text = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %_Z5func1v "_Z5func1v"
+OpEntryPoint Fragment %_Z5func2v "_Z5func2v"
+OpExecutionMode %_Z5func1v OriginUpperLeft
+OpExecutionMode %_Z5func2v OriginUpperLeft
+OpSource HLSL 630
+OpName %type_cba "type.cba"
+OpMemberName %type_cba 0 "color"
+OpName %cba "cba"
+OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+OpName %out_var_SV_TARGET_0 "out.var.SV_TARGET"
+OpName %_Z5func1v "_Z5func1v"
+OpName %_Z5func2v "_Z5func2v"
+OpDecorate %out_var_SV_TARGET Location 0
+OpDecorate %out_var_SV_TARGET_0 Location 0
+OpDecorate %cba DescriptorSet 0
+OpDecorate %cba Binding 0
+OpMemberDecorate %type_cba 0 Offset 0
+OpDecorate %type_cba Block
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%type_cba = OpTypeStruct %v4float
+%_ptr_Uniform_type_cba = OpTypePointer Uniform %type_cba
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%14 = OpTypeFunction %void
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+%cba = OpVariable %_ptr_Uniform_type_cba Uniform
+%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+%out_var_SV_TARGET_0 = OpVariable %_ptr_Output_v4float Output
+%_Z5func1v = OpFunction %void None %14
+%16 = OpLabel
+%17 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0
+%18 = OpLoad %v4float %17
+OpStore %out_var_SV_TARGET %18
+OpReturn
+OpFunctionEnd
+%_Z5func2v = OpFunction %void None %14
+%19 = OpLabel
+%20 = OpAccessChain %_ptr_Uniform_v4float %cba %int_0
+%21 = OpLoad %v4float %20
+OpStore %out_var_SV_TARGET_0 %21
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<RemoveUnusedInterfaceVariablesPass>(text, expected,
+ true, 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
new file mode 100644
index 00000000..ca625812
--- /dev/null
+++ b/test/opt/replace_desc_array_access_using_var_index_test.cpp
@@ -0,0 +1,411 @@
+// Copyright (c) 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+
+#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 ReplaceDescArrayAccessUsingVarIndexTest = PassTest<::testing::Test>;
+
+TEST_F(ReplaceDescArrayAccessUsingVarIndexTest,
+ ReplaceAccessChainToTextureArray) {
+ const std::string text = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %psmain "psmain" %gl_FragCoord %in_var_INSTANCEID %out_var_SV_TARGET
+ OpExecutionMode %psmain OriginUpperLeft
+ OpSource HLSL 600
+ OpName %type_sampler "type.sampler"
+ OpName %Sampler0 "Sampler0"
+ OpName %type_2d_image "type.2d.image"
+ OpName %Tex0 "Tex0"
+ OpName %in_var_INSTANCEID "in.var.INSTANCEID"
+ OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+ OpName %psmain "psmain"
+ OpName %type_sampled_image "type.sampled.image"
+ OpDecorate %gl_FragCoord BuiltIn FragCoord
+ OpDecorate %in_var_INSTANCEID Flat
+ OpDecorate %in_var_INSTANCEID Location 0
+ OpDecorate %out_var_SV_TARGET Location 0
+ OpDecorate %Sampler0 DescriptorSet 0
+ OpDecorate %Sampler0 Binding 1
+ OpDecorate %Tex0 DescriptorSet 0
+ OpDecorate %Tex0 Binding 2
+ %bool = OpTypeBool
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+ %uint = OpTypeInt 32 0
+ %uint_3 = OpConstant %uint 3
+ %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 0 Unknown
+%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3
+%_ptr_UniformConstant__arr_type_2d_image_uint_3 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_3
+ %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Input_uint = OpTypePointer Input %uint
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %21 = OpTypeFunction %void
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+ %v2float = OpTypeVector %float 2
+ %v2uint = OpTypeVector %uint 2
+ %uint_0 = OpConstant %uint 0
+ %uint_1 = OpConstant %uint 1
+ %27 = OpConstantComposite %v2uint %uint_0 %uint_1
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+ %Sampler0 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+ %Tex0 = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_3 UniformConstant
+%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+%in_var_INSTANCEID = OpVariable %_ptr_Input_uint Input
+%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+ %uint_2 = OpConstant %uint 2
+ %66 = OpConstantNull %v4float
+
+; CHECK: [[null_value:%\w+]] = OpConstantNull %v4float
+
+ %psmain = OpFunction %void None %21
+ %39 = OpLabel
+ %29 = OpLoad %v4float %gl_FragCoord
+ %30 = OpLoad %uint %in_var_INSTANCEID
+ %37 = OpIEqual %bool %30 %uint_2
+ OpSelectionMerge %38 None
+ OpBranchConditional %37 %28 %40
+
+; CHECK: [[var_index:%\w+]] = OpLoad %uint %in_var_INSTANCEID
+; CHECK: OpSelectionMerge [[cond_branch_merge:%\w+]] None
+; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} [[bb_cond_br:%\w+]]
+
+ %28 = OpLabel
+ %31 = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %30
+ %32 = OpLoad %type_2d_image %31
+ OpImageWrite %32 %27 %29
+
+; CHECK: OpSelectionMerge [[merge:%\w+]] None
+; CHECK: OpSwitch [[var_index]] [[default:%\w+]] 0 [[case0:%\w+]] 1 [[case1:%\w+]] 2 [[case2:%\w+]]
+; CHECK: [[case0]] = OpLabel
+; CHECK: OpAccessChain
+; CHECK: OpLoad
+; CHECK: OpImageWrite
+; CHECK: OpBranch [[merge]]
+; CHECK: [[case1]] = OpLabel
+; CHECK: OpAccessChain
+; CHECK: OpLoad
+; CHECK: OpImageWrite
+; CHECK: OpBranch [[merge]]
+; CHECK: [[case2]] = OpLabel
+; CHECK: OpAccessChain
+; CHECK: OpLoad
+; CHECK: OpImageWrite
+; CHECK: OpBranch [[merge]]
+; CHECK: [[default]] = OpLabel
+; CHECK: OpBranch [[merge]]
+; CHECK: [[merge]] = OpLabel
+
+ %33 = OpLoad %type_sampler %Sampler0
+ %34 = OpVectorShuffle %v2float %29 %29 0 1
+ %35 = OpSampledImage %type_sampled_image %32 %33
+ %36 = OpImageSampleImplicitLod %v4float %35 %34 None
+
+; CHECK: OpSelectionMerge [[merge:%\w+]] None
+; CHECK: OpSwitch [[var_index]] [[default:%\w+]] 0 [[case0:%\w+]] 1 [[case1:%\w+]] 2 [[case2:%\w+]]
+; CHECK: [[case0]] = OpLabel
+; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_0
+; CHECK: [[sam:%\w+]] = OpLoad %type_sampler %Sampler0
+; CHECK: [[img:%\w+]] = OpLoad %type_2d_image [[ac]]
+; CHECK: [[sampledImg:%\w+]] = OpSampledImage %type_sampled_image [[img]] [[sam]]
+; CHECK: [[value0:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg]]
+; CHECK: OpBranch [[merge]]
+; CHECK: [[case1]] = OpLabel
+; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_1
+; CHECK: [[sam:%\w+]] = OpLoad %type_sampler %Sampler0
+; CHECK: [[img:%\w+]] = OpLoad %type_2d_image [[ac]]
+; CHECK: [[sampledImg:%\w+]] = OpSampledImage %type_sampled_image [[img]] [[sam]]
+; CHECK: [[value1:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg]]
+; CHECK: OpBranch [[merge]]
+; CHECK: [[case2]] = OpLabel
+; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_2
+; CHECK: [[sam:%\w+]] = OpLoad %type_sampler %Sampler0
+; CHECK: [[img:%\w+]] = OpLoad %type_2d_image [[ac]]
+; CHECK: [[sampledImg:%\w+]] = OpSampledImage %type_sampled_image [[img]] [[sam]]
+; CHECK: [[value2:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg]]
+; CHECK: OpBranch [[merge]]
+; CHECK: [[default]] = OpLabel
+; CHECK: OpBranch [[merge]]
+; CHECK: [[merge]] = OpLabel
+; CHECK: [[phi0:%\w+]] = OpPhi %v4float [[value0]] [[case0]] [[value1]] [[case1]] [[value2]] [[case2]] [[null_value]] [[default]]
+
+ OpBranch %38
+ %40 = OpLabel
+ OpBranch %38
+ %38 = OpLabel
+ %41 = OpPhi %v4float %36 %28 %29 %40
+
+; CHECK: OpBranch [[cond_branch_merge]]
+; CHECK: [[bb_cond_br]] = OpLabel
+; CHECK: OpBranch [[cond_branch_merge]]
+; CHECK: [[cond_branch_merge]] = OpLabel
+; CHECK: [[phi1:%\w+]] = OpPhi %v4float [[phi0]] [[merge]] {{%\w+}} [[bb_cond_br]]
+; CHECK: OpStore {{%\w+}} [[phi1]]
+
+ OpStore %out_var_SV_TARGET %41
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<ReplaceDescArrayAccessUsingVarIndex>(text, true);
+}
+
+TEST_F(ReplaceDescArrayAccessUsingVarIndexTest,
+ ReplaceAccessChainToTextureArrayAndSamplerArray) {
+ const std::string text = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %psmain "psmain" %gl_FragCoord %in_var_INSTANCEID %out_var_SV_TARGET
+ OpExecutionMode %psmain OriginUpperLeft
+ OpSource HLSL 600
+ OpName %type_sampler "type.sampler"
+ OpName %Sampler0 "Sampler0"
+ OpName %type_2d_image "type.2d.image"
+ OpName %Tex0 "Tex0"
+ OpName %in_var_INSTANCEID "in.var.INSTANCEID"
+ OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+ OpName %psmain "psmain"
+ OpName %type_sampled_image "type.sampled.image"
+ OpDecorate %gl_FragCoord BuiltIn FragCoord
+ OpDecorate %in_var_INSTANCEID Flat
+ OpDecorate %in_var_INSTANCEID Location 0
+ OpDecorate %out_var_SV_TARGET Location 0
+ OpDecorate %Sampler0 DescriptorSet 0
+ OpDecorate %Sampler0 Binding 1
+ OpDecorate %Tex0 DescriptorSet 0
+ OpDecorate %Tex0 Binding 2
+%type_sampler = OpTypeSampler
+ %uint = OpTypeInt 32 0
+ %uint_2 = OpConstant %uint 2
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+%_arr_type_sampler_uint_2 = OpTypeArray %type_sampler %uint_2
+%_ptr_UniformConstant__arr_type_sampler_uint_2 = OpTypePointer UniformConstant %_arr_type_sampler_uint_2
+ %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 0 Unknown
+%_arr_type_2d_image_uint_2 = OpTypeArray %type_2d_image %uint_2
+%_ptr_UniformConstant__arr_type_2d_image_uint_2 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_2
+ %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Input_uint = OpTypePointer Input %uint
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %21 = OpTypeFunction %void
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+ %v2float = OpTypeVector %float 2
+ %v2uint = OpTypeVector %uint 2
+ %uint_0 = OpConstant %uint 0
+ %uint_1 = OpConstant %uint 1
+ %27 = OpConstantComposite %v2uint %uint_0 %uint_1
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+ %Sampler0 = OpVariable %_ptr_UniformConstant__arr_type_sampler_uint_2 UniformConstant
+ %Tex0 = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_2 UniformConstant
+%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+%in_var_INSTANCEID = OpVariable %_ptr_Input_uint Input
+%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+ %66 = OpConstantNull %v4float
+ %psmain = OpFunction %void None %21
+ %28 = OpLabel
+ %29 = OpLoad %v4float %gl_FragCoord
+ %30 = OpLoad %uint %in_var_INSTANCEID
+ %31 = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %30
+ %32 = OpLoad %type_2d_image %31
+ OpImageWrite %32 %27 %29
+
+; CHECK: [[null_value:%\w+]] = OpConstantNull %v4float
+
+; CHECK: [[var_index:%\w+]] = OpLoad %uint %in_var_INSTANCEID
+; CHECK: OpSelectionMerge [[merge:%\w+]] None
+; CHECK: OpSwitch [[var_index]] [[default:%\w+]] 0 [[case0:%\w+]] 1 [[case1:%\w+]]
+; CHECK: [[case0]] = OpLabel
+; CHECK: OpAccessChain
+; CHECK: OpLoad
+; CHECK: OpImageWrite
+; CHECK: OpBranch [[merge]]
+; CHECK: [[case1]] = OpLabel
+; CHECK: OpAccessChain
+; CHECK: OpLoad
+; CHECK: OpImageWrite
+; CHECK: OpBranch [[merge]]
+; CHECK: [[default]] = OpLabel
+; CHECK: OpBranch [[merge]]
+; CHECK: [[merge]] = OpLabel
+
+ %33 = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %30
+ %37 = OpLoad %type_sampler %33
+ %34 = OpVectorShuffle %v2float %29 %29 0 1
+ %35 = OpSampledImage %type_sampled_image %32 %37
+ %36 = OpImageSampleImplicitLod %v4float %35 %34 None
+
+; SPIR-V instructions to be replaced (will be killed by ADCE)
+; CHECK: OpSelectionMerge
+; CHECK: OpSwitch
+
+; CHECK: OpSelectionMerge [[merge_sampler:%\w+]] None
+; CHECK: OpSwitch [[var_index]] [[default_sampler:%\w+]] 0 [[case_sampler0:%\w+]] 1 [[case_sampler1:%\w+]]
+
+; CHECK: [[case_sampler0]] = OpLabel
+; CHECK: OpSelectionMerge [[merge_texture0:%\w+]] None
+; CHECK: OpSwitch [[var_index]] [[default_texture:%\w+]] 0 [[case_texture0:%\w+]] 1 [[case_texture1:%\w+]]
+; CHECK: [[case_texture0]] = OpLabel
+; CHECK: [[pt0:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_0
+; CHECK: [[ps0:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %uint_0
+; CHECK: [[s0:%\w+]] = OpLoad %type_sampler [[ps0]]
+; CHECK: [[t0:%\w+]] = OpLoad %type_2d_image [[pt0]]
+; CHECK: [[sampledImg0:%\w+]] = OpSampledImage %type_sampled_image [[t0]] [[s0]]
+; CHECK: [[value0:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg0]]
+; CHECK: OpBranch [[merge_texture0]]
+; CHECK: [[case_texture1]] = OpLabel
+; CHECK: [[pt1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_1
+; CHECK: [[ps0:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %uint_0
+; CHECK: [[s0:%\w+]] = OpLoad %type_sampler [[ps0]]
+; CHECK: [[t1:%\w+]] = OpLoad %type_2d_image [[pt1]]
+; CHECK: [[sampledImg1:%\w+]] = OpSampledImage %type_sampled_image [[t1]] [[s0]]
+; CHECK: [[value1:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg1]]
+; CHECK: OpBranch [[merge_texture0]]
+; CHECK: [[default_texture]] = OpLabel
+; CHECK: OpBranch [[merge_texture0]]
+; CHECK: [[merge_texture0]] = OpLabel
+; CHECK: [[phi0:%\w+]] = OpPhi %v4float [[value0]] [[case_texture0]] [[value1]] [[case_texture1]] [[null_value]] [[default_texture]]
+; CHECK: OpBranch [[merge_sampler]]
+
+; CHECK: [[case_sampler1]] = OpLabel
+; CHECK: OpSelectionMerge [[merge_texture1:%\w+]] None
+; CHECK: OpSwitch [[var_index]] [[default_texture:%\w+]] 0 [[case_texture0:%\w+]] 1 [[case_texture1:%\w+]]
+; CHECK: [[case_texture0]] = OpLabel
+; CHECK: [[pt0:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_0
+; CHECK: [[ps1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %uint_1
+; CHECK: [[s1:%\w+]] = OpLoad %type_sampler [[ps1]]
+; CHECK: [[t0:%\w+]] = OpLoad %type_2d_image [[pt0]]
+; CHECK: [[sampledImg0:%\w+]] = OpSampledImage %type_sampled_image [[t0]] [[s1]]
+; CHECK: [[value0:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg0]]
+; CHECK: OpBranch [[merge_texture1]]
+; CHECK: [[case_texture1]] = OpLabel
+; CHECK: [[pt1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_1
+; CHECK: [[ps1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %Sampler0 %uint_1
+; CHECK: [[s1:%\w+]] = OpLoad %type_sampler [[ps1]]
+; CHECK: [[t1:%\w+]] = OpLoad %type_2d_image [[pt1]]
+; CHECK: [[sampledImg1:%\w+]] = OpSampledImage %type_sampled_image [[t1]] [[s1]]
+; CHECK: [[value1:%\w+]] = OpImageSampleImplicitLod %v4float [[sampledImg1]]
+; CHECK: OpBranch [[merge_texture1]]
+; CHECK: [[default_texture]] = OpLabel
+; CHECK: OpBranch [[merge_texture1]]
+; CHECK: [[merge_texture1]] = OpLabel
+; CHECK: [[phi1:%\w+]] = OpPhi %v4float [[value0]] [[case_texture0]] [[value1]] [[case_texture1]] [[null_value]] [[default_texture]]
+
+; CHECK: [[default_sampler]] = OpLabel
+; CHECK: OpBranch [[merge_sampler]]
+; CHECK: [[merge_sampler]] = OpLabel
+; CHECK: OpPhi %v4float [[phi0]] [[merge_texture0]] [[phi1]] [[merge_texture1]] [[null_value]] [[default_sampler]]
+; CHECK: OpStore
+
+ OpStore %out_var_SV_TARGET %36
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<ReplaceDescArrayAccessUsingVarIndex>(text, true);
+}
+
+TEST_F(ReplaceDescArrayAccessUsingVarIndexTest,
+ ReplaceAccessChainToTextureArrayWithSingleElement) {
+ const std::string text = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %psmain "psmain" %gl_FragCoord %in_var_INSTANCEID %out_var_SV_TARGET
+ OpExecutionMode %psmain OriginUpperLeft
+ OpSource HLSL 600
+ OpName %type_sampler "type.sampler"
+ OpName %Sampler0 "Sampler0"
+ OpName %type_2d_image "type.2d.image"
+ OpName %Tex0 "Tex0"
+ OpName %in_var_INSTANCEID "in.var.INSTANCEID"
+ OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+ OpName %psmain "psmain"
+ OpName %type_sampled_image "type.sampled.image"
+ OpDecorate %gl_FragCoord BuiltIn FragCoord
+ OpDecorate %in_var_INSTANCEID Flat
+ OpDecorate %in_var_INSTANCEID Location 0
+ OpDecorate %out_var_SV_TARGET Location 0
+ OpDecorate %Sampler0 DescriptorSet 0
+ OpDecorate %Sampler0 Binding 1
+ OpDecorate %Tex0 DescriptorSet 0
+ OpDecorate %Tex0 Binding 2
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+ %uint = OpTypeInt 32 0
+ %uint_1 = OpConstant %uint 1
+ %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 0 Unknown
+%_arr_type_2d_image_uint_1 = OpTypeArray %type_2d_image %uint_1
+%_ptr_UniformConstant__arr_type_2d_image_uint_1 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_1
+ %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Input_uint = OpTypePointer Input %uint
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %21 = OpTypeFunction %void
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+ %v2float = OpTypeVector %float 2
+ %v2uint = OpTypeVector %uint 2
+ %uint_0 = OpConstant %uint 0
+ %27 = OpConstantComposite %v2uint %uint_0 %uint_1
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+ %Sampler0 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+ %Tex0 = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_1 UniformConstant
+%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+%in_var_INSTANCEID = OpVariable %_ptr_Input_uint Input
+%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+ %uint_2 = OpConstant %uint 2
+ %66 = OpConstantNull %v4float
+ %psmain = OpFunction %void None %21
+ %28 = OpLabel
+ %29 = OpLoad %v4float %gl_FragCoord
+ %30 = OpLoad %uint %in_var_INSTANCEID
+ %31 = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %30
+ %32 = OpLoad %type_2d_image %31
+ OpImageWrite %32 %27 %29
+
+; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %Tex0 %uint_0
+; CHECK-NOT: OpAccessChain
+; CHECK-NOT: OpSwitch
+; CHECK-NOT: OpPhi
+
+ %33 = OpLoad %type_sampler %Sampler0
+ %34 = OpVectorShuffle %v2float %29 %29 0 1
+ %35 = OpSampledImage %type_sampled_image %32 %33
+ %36 = OpImageSampleImplicitLod %v4float %35 %34 None
+
+ OpStore %out_var_SV_TARGET %36
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<ReplaceDescArrayAccessUsingVarIndex>(text, true);
+}
+
+} // namespace
+} // namespace opt
+} // namespace spvtools
diff --git a/test/opt/scalar_replacement_test.cpp b/test/opt/scalar_replacement_test.cpp
index 8115f5fb..8cb888c9 100644
--- a/test/opt/scalar_replacement_test.cpp
+++ b/test/opt/scalar_replacement_test.cpp
@@ -1935,12 +1935,12 @@ OpName %6 "simple_struct"
; CHECK: [[dbg_local_var:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable
; CHECK: [[deref_expr:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref]]
; CHECK: [[repl3:%\w+]] = OpVariable %_ptr_Function_uint Function
-; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr]] %int_3
; CHECK: [[repl2:%\w+]] = OpVariable %_ptr_Function_uint Function
-; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2
; CHECK: [[repl1:%\w+]] = OpVariable %_ptr_Function_uint Function
-; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1
; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function
+; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr]] %int_3
+; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2
+; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1
; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl0]] [[deref_expr]] %int_0
; CHECK-NOT: DebugDeclare
%decl = OpExtInst %1 %ext DebugDeclare %dbg_foo %14 %null_expr
@@ -2058,10 +2058,10 @@ OpName %6 "simple_struct"
; CHECK: [[repl2:%\w+]] = OpVariable %_ptr_Function_float Function %float_1
; CHECK: [[repl1:%\w+]] = OpVariable %_ptr_Function_uint Function %uint_32
; CHECK: [[repl3:%\w+]] = OpVariable %_ptr_Function_float Function %float_1
+; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function %uint_32
; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr]] %int_2
; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 %int_0
; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_1 %int_1
-; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function %uint_32
; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl0]] [[deref_expr]] %int_0
; CHECK-NOT: DebugDeclare
%decl = OpExtInst %1 %ext DebugDeclare %dbg_foo %14 %null_expr
@@ -2174,12 +2174,12 @@ OpName %6 "simple_struct"
; CHECK: [[dbg_local_var:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugLocalVariable
; CHECK: [[repl3:%\w+]] = OpVariable %_ptr_Function_uint Function
-; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr:%\w+]] %int_3
; CHECK: [[repl2:%\w+]] = OpVariable %_ptr_Function_uint Function
-; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2
; CHECK: [[repl1:%\w+]] = OpVariable %_ptr_Function_uint Function
-; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1
; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function
+; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr:%\w+]] %int_3
+; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2
+; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1
; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl0]] [[deref_expr]] %int_0
OpBranch %20
@@ -2237,6 +2237,32 @@ OpFunctionEnd
SinglePassRunAndMatch<ScalarReplacementPass>(text, false);
}
+TEST_F(ScalarReplacementTest, FunctionDeclaration) {
+ // Make sure the pass works with a function declaration that is called.
+ const std::string text = R"(OpCapability Addresses
+OpCapability Linkage
+OpCapability Kernel
+OpCapability Int8
+%1 = OpExtInstImport "OpenCL.std"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool"
+OpExecutionMode %2 ContractionOff
+OpSource Unknown 0
+OpDecorate %3 LinkageAttributes "julia_error_7712" Import
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%3 = OpFunction %void None %5
+OpFunctionEnd
+%2 = OpFunction %void None %5
+%6 = OpLabel
+%7 = OpFunctionCall %void %3
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<ScalarReplacementPass>(text, text, false);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/set_spec_const_default_value_test.cpp b/test/opt/set_spec_const_default_value_test.cpp
index 5e63862e..10f805b6 100644
--- a/test/opt/set_spec_const_default_value_test.cpp
+++ b/test/opt/set_spec_const_default_value_test.cpp
@@ -618,7 +618,7 @@ INSTANTIATE_TEST_SUITE_P(
{"", SpecIdToValueBitPatternMap{}, ""},
// 1. Empty with non-empty values to set.
{"", SpecIdToValueBitPatternMap{{1, {100}}, {2, {200}}}, ""},
- // 2. Baisc bool type.
+ // 2. Basic bool type.
{
// code
"OpDecorate %1 SpecId 100\n"
@@ -935,6 +935,98 @@ INSTANTIATE_TEST_SUITE_P(
"%2 = OpSpecConstantTrue %bool\n"
"%3 = OpSpecConstantTrue %bool\n",
},
+ // 19. 16-bit signed int type.
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "OpDecorate %3 SpecId 102\n"
+ "%short = OpTypeInt 16 1\n"
+ "%1 = OpSpecConstant %short 10\n"
+ "%2 = OpSpecConstant %short 11\n"
+ "%3 = OpSpecConstant %short 11\n",
+ // default values
+ SpecIdToValueBitPatternMap{
+ {100, {32767}}, {101, {0xffff}}, {102, {0xffffffd6}}},
+ // expected. These are sign-extended
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "OpDecorate %3 SpecId 102\n"
+ "%short = OpTypeInt 16 1\n"
+ "%1 = OpSpecConstant %short 32767\n"
+ "%2 = OpSpecConstant %short -1\n"
+ "%3 = OpSpecConstant %short -42\n",
+ },
+ // 20. 16-bit unsigned int type.
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "OpDecorate %3 SpecId 102\n"
+ "%ushort = OpTypeInt 16 0\n"
+ "%1 = OpSpecConstant %ushort 10\n"
+ "%2 = OpSpecConstant %ushort 11\n"
+ "%3 = OpSpecConstant %ushort 11\n",
+ // default values
+ SpecIdToValueBitPatternMap{
+ {100, {32767}}, {101, {0xffff}}, {102, {0xffffffd6}}},
+ // expected. Upper bits are always zero.
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "OpDecorate %3 SpecId 102\n"
+ "%ushort = OpTypeInt 16 0\n"
+ "%1 = OpSpecConstant %ushort 32767\n"
+ "%2 = OpSpecConstant %ushort 65535\n"
+ "%3 = OpSpecConstant %ushort 65494\n",
+ },
+ // 21. 8-bit signed int type.
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "OpDecorate %3 SpecId 102\n"
+ "%char = OpTypeInt 8 1\n"
+ "%1 = OpSpecConstant %char 10\n"
+ "%2 = OpSpecConstant %char 11\n"
+ "%3 = OpSpecConstant %char 11\n",
+ // default values
+ SpecIdToValueBitPatternMap{
+ {100, {127}}, {101, {128}}, {102, {0xd6}}},
+ // expected. These are sign extended
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "OpDecorate %3 SpecId 102\n"
+ "%char = OpTypeInt 8 1\n"
+ "%1 = OpSpecConstant %char 127\n"
+ "%2 = OpSpecConstant %char -128\n"
+ "%3 = OpSpecConstant %char -42\n",
+ },
+ // 22. 8-bit unsigned int type.
+ {
+ // code
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "OpDecorate %3 SpecId 102\n"
+ "OpDecorate %4 SpecId 103\n"
+ "%uchar = OpTypeInt 8 0\n"
+ "%1 = OpSpecConstant %uchar 10\n"
+ "%2 = OpSpecConstant %uchar 11\n"
+ "%3 = OpSpecConstant %uchar 11\n"
+ "%4 = OpSpecConstant %uchar 11\n",
+ // default values
+ SpecIdToValueBitPatternMap{
+ {100, {127}}, {101, {128}}, {102, {256}}, {103, {0xffffffd6}}},
+ // expected. Upper bits are always zero.
+ "OpDecorate %1 SpecId 100\n"
+ "OpDecorate %2 SpecId 101\n"
+ "OpDecorate %3 SpecId 102\n"
+ "OpDecorate %4 SpecId 103\n"
+ "%uchar = OpTypeInt 8 0\n"
+ "%1 = OpSpecConstant %uchar 127\n"
+ "%2 = OpSpecConstant %uchar 128\n"
+ "%3 = OpSpecConstant %uchar 0\n"
+ "%4 = OpSpecConstant %uchar 214\n",
+ },
}));
INSTANTIATE_TEST_SUITE_P(
diff --git a/test/opt/simplification_test.cpp b/test/opt/simplification_test.cpp
index 7a9696ea..7727f567 100644
--- a/test/opt/simplification_test.cpp
+++ b/test/opt/simplification_test.cpp
@@ -360,6 +360,31 @@ OpFunctionEnd
SinglePassRunAndMatch<SimplificationPass>(spirv, true);
}
+TEST_F(SimplificationTest, FunctionDeclaration) {
+ // Make sure the pass works with a function declaration that is called.
+ const std::string text = R"(OpCapability Addresses
+OpCapability Linkage
+OpCapability Kernel
+OpCapability Int8
+%1 = OpExtInstImport "OpenCL.std"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool"
+OpExecutionMode %2 ContractionOff
+OpSource Unknown 0
+OpDecorate %3 LinkageAttributes "julia_error_7712" Import
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%3 = OpFunction %void None %5
+OpFunctionEnd
+%2 = OpFunction %void None %5
+%6 = OpLabel
+%7 = OpFunctionCall %void %3
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<SimplificationPass>(text, text, false);
+}
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/opt/spread_volatile_semantics_test.cpp b/test/opt/spread_volatile_semantics_test.cpp
new file mode 100644
index 00000000..83b2dcfa
--- /dev/null
+++ b/test/opt/spread_volatile_semantics_test.cpp
@@ -0,0 +1,1118 @@
+// 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 <string>
+
+#include "gmock/gmock.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+struct ExecutionModelAndBuiltIn {
+ const char* execution_model;
+ const char* built_in;
+ const bool use_v4uint;
+};
+
+using AddVolatileDecorationTest =
+ PassTest<::testing::TestWithParam<ExecutionModelAndBuiltIn>>;
+
+TEST_P(AddVolatileDecorationTest, InMain) {
+ const auto& tc = GetParam();
+ const std::string execution_model(tc.execution_model);
+ const std::string built_in(tc.built_in);
+ const std::string var_type =
+ tc.use_v4uint ? "%_ptr_Input_v4uint" : "%_ptr_Input_uint";
+ const std::string var_load_type = tc.use_v4uint ? "%v4uint" : "%uint";
+
+ const std::string text =
+ std::string(R"(OpCapability RuntimeDescriptorArray
+OpCapability RayTracingKHR
+OpCapability SubgroupBallotKHR
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_KHR_ray_tracing"
+OpExtension "SPV_KHR_shader_ballot"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint )") +
+ execution_model + std::string(R"( %main "main" %var
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_KHR_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
+)") + std::string(R"(
+; CHECK: OpDecorate [[var:%\w+]] BuiltIn )") +
+ built_in + std::string(R"(
+; CHECK: OpDecorate [[var]] Volatile
+OpDecorate %var BuiltIn )") + built_in + std::string(R"(
+%void = OpTypeVoid
+%3 = 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
+%_ptr_Input_uint = OpTypePointer Input %uint
+%v4uint = OpTypeVector %uint 4
+%_ptr_Input_v4uint = OpTypePointer Input %v4uint
+%var = OpVariable )") +
+ var_type + std::string(R"( Input
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%25 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%main = OpFunction %void None %3
+%5 = OpLabel
+%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%20 = OpLoad %uint %19
+%load = OpLoad )") + var_load_type + std::string(R"( %var
+%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+%23 = OpLoad %13 %22
+%27 = OpImageRead %v4float %23 %25
+%29 = OpCompositeExtract %float %27 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+OpStore %31 %29
+OpReturn
+OpFunctionEnd
+)");
+
+ SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AddVolatileDecoration, AddVolatileDecorationTest,
+ ::testing::ValuesIn(std::vector<ExecutionModelAndBuiltIn>{
+ {"RayGenerationKHR", "SubgroupSize", false},
+ {"RayGenerationKHR", "SubgroupLocalInvocationId", false},
+ {"RayGenerationKHR", "SubgroupEqMask", true},
+ {"ClosestHitKHR", "SubgroupLocalInvocationId", true},
+ {"IntersectionKHR", "SubgroupEqMask", true},
+ {"MissKHR", "SubgroupGeMask", true},
+ {"CallableKHR", "SubgroupGtMask", true},
+ {"RayGenerationKHR", "SubgroupLeMask", true},
+ }));
+
+using SetLoadVolatileTest =
+ PassTest<::testing::TestWithParam<ExecutionModelAndBuiltIn>>;
+
+TEST_P(SetLoadVolatileTest, InMain) {
+ const auto& tc = GetParam();
+ const std::string execution_model(tc.execution_model);
+ const std::string built_in(tc.built_in);
+
+ const std::string var_type =
+ tc.use_v4uint ? "%_ptr_Input_v4uint" : "%_ptr_Input_uint";
+ const std::string var_value = tc.use_v4uint ? std::string(R"(
+; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Input_uint [[var]] %int_0
+; CHECK: OpLoad {{%\w+}} [[ptr]] Volatile
+%ptr = OpAccessChain %_ptr_Input_uint %var %int_0
+%var_value = OpLoad %uint %ptr)")
+ : std::string(R"(
+; CHECK: OpLoad {{%\w+}} [[var]] Volatile
+%var_value = OpLoad %uint %var)");
+
+ const std::string text = std::string(R"(OpCapability RuntimeDescriptorArray
+OpCapability RayTracingKHR
+OpCapability SubgroupBallotKHR
+OpCapability VulkanMemoryModel
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_KHR_ray_tracing"
+OpExtension "SPV_KHR_shader_ballot"
+OpMemoryModel Logical Vulkan
+OpEntryPoint )") + execution_model +
+ std::string(R"( %main "main" %var
+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
+)") + std::string(R"(
+; CHECK: OpDecorate [[var:%\w+]] BuiltIn )") +
+ built_in + std::string(R"(
+OpDecorate %var BuiltIn )") + built_in +
+ std::string(R"(
+%void = OpTypeVoid
+%3 = 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
+%_ptr_Input_uint = OpTypePointer Input %uint
+%v4uint = OpTypeVector %uint 4
+%_ptr_Input_v4uint = OpTypePointer Input %v4uint
+%var = OpVariable )") + var_type +
+ std::string(R"( Input
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%25 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%main = OpFunction %void None %3
+%5 = OpLabel
+%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%20 = OpLoad %uint %19
+)") + var_value + std::string(R"(
+%test = OpIAdd %uint %var_value %20
+%22 = OpAccessChain %_ptr_UniformConstant_13 %images %test
+%23 = OpLoad %13 %22
+%27 = OpImageRead %v4float %23 %25
+%29 = OpCompositeExtract %float %27 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+OpStore %31 %29
+OpReturn
+OpFunctionEnd
+)");
+
+ SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ SetLoadVolatile, SetLoadVolatileTest,
+ ::testing::ValuesIn(std::vector<ExecutionModelAndBuiltIn>{
+ {"RayGenerationKHR", "SubgroupSize", false},
+ {"RayGenerationKHR", "SubgroupLocalInvocationId", false},
+ {"RayGenerationKHR", "SubgroupEqMask", true},
+ {"ClosestHitKHR", "SubgroupLocalInvocationId", true},
+ {"IntersectionKHR", "SubgroupEqMask", true},
+ {"MissKHR", "SubgroupGeMask", true},
+ {"CallableKHR", "SubgroupGtMask", true},
+ {"RayGenerationKHR", "SubgroupLeMask", true},
+ }));
+
+using VolatileSpreadTest = PassTest<::testing::Test>;
+
+TEST_F(VolatileSpreadTest, SpreadVolatileForHelperInvocation) {
+ const std::string text =
+ R"(
+OpCapability Shader
+OpCapability DemoteToHelperInvocation
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+
+; CHECK: OpDecorate [[var:%\w+]] BuiltIn HelperInvocation
+; CHECK: OpDecorate [[var]] Volatile
+OpDecorate %var BuiltIn HelperInvocation
+
+%bool = OpTypeBool
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%_ptr_Input_bool = OpTypePointer Input %bool
+%var = OpVariable %_ptr_Input_bool Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%load = OpLoad %bool %var
+OpDemoteToHelperInvocation
+OpReturn
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_UNIVERSAL_1_6);
+ SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true);
+}
+
+TEST_F(VolatileSpreadTest, MultipleExecutionModel) {
+ const std::string text =
+ R"(
+OpCapability RuntimeDescriptorArray
+OpCapability RayTracingKHR
+OpCapability SubgroupBallotKHR
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_KHR_ray_tracing"
+OpExtension "SPV_KHR_shader_ballot"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var
+OpEntryPoint GLCompute %compute "Compute" %gl_LocalInvocationIndex
+OpExecutionMode %compute LocalSize 16 16 1
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_KHR_ray_tracing"
+OpName %RayGeneration "RayGeneration"
+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 %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+
+; CHECK: OpEntryPoint RayGenerationNV {{%\w+}} "RayGeneration" [[var:%\w+]]
+; CHECK: OpDecorate [[var]] BuiltIn SubgroupSize
+; CHECK: OpDecorate [[var]] Volatile
+; CHECK-NOT: OpDecorate {{%\w+}} Volatile
+OpDecorate %var BuiltIn SubgroupSize
+
+%void = OpTypeVoid
+%3 = 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
+%_ptr_Input_uint = OpTypePointer Input %uint
+%var = OpVariable %_ptr_Input_uint Input
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%25 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+%shared = OpVariable %_ptr_Workgroup_uint Workgroup
+
+%RayGeneration = OpFunction %void None %3
+%5 = OpLabel
+%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%20 = OpLoad %uint %var
+%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+%23 = OpLoad %13 %22
+%27 = OpImageRead %v4float %23 %25
+%29 = OpCompositeExtract %float %27 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+OpStore %31 %29
+OpReturn
+OpFunctionEnd
+
+%compute = OpFunction %void None %3
+%66 = OpLabel
+%62 = OpLoad %uint %gl_LocalInvocationIndex
+%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %62
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true);
+}
+
+TEST_F(VolatileSpreadTest, VarUsedInMultipleEntryPoints) {
+ const std::string text =
+ R"(
+OpCapability RuntimeDescriptorArray
+OpCapability RayTracingKHR
+OpCapability SubgroupBallotKHR
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_KHR_ray_tracing"
+OpExtension "SPV_KHR_shader_ballot"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var
+OpEntryPoint ClosestHitKHR %ClosestHit "ClosestHit" %var
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_KHR_ray_tracing"
+OpName %RayGeneration "RayGeneration"
+OpName %ClosestHit "ClosestHit"
+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 %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+
+; CHECK: OpEntryPoint RayGenerationNV {{%\w+}} "RayGeneration" [[var:%\w+]]
+; CHECK: OpEntryPoint ClosestHitNV {{%\w+}} "ClosestHit" [[var]]
+; CHECK: OpDecorate [[var]] BuiltIn SubgroupSize
+; CHECK: OpDecorate [[var]] Volatile
+; CHECK-NOT: OpDecorate {{%\w+}} Volatile
+OpDecorate %var BuiltIn SubgroupSize
+
+%void = OpTypeVoid
+%3 = 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
+%_ptr_Input_uint = OpTypePointer Input %uint
+%var = OpVariable %_ptr_Input_uint Input
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%25 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+%shared = OpVariable %_ptr_Workgroup_uint Workgroup
+
+%RayGeneration = OpFunction %void None %3
+%5 = OpLabel
+%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%20 = OpLoad %uint %var
+%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+%23 = OpLoad %13 %22
+%27 = OpImageRead %v4float %23 %25
+%29 = OpCompositeExtract %float %27 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+OpStore %31 %29
+OpReturn
+OpFunctionEnd
+
+%ClosestHit = OpFunction %void None %3
+%45 = OpLabel
+%49 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%40 = OpLoad %uint %var
+%42 = OpAccessChain %_ptr_UniformConstant_13 %images %40
+%43 = OpLoad %13 %42
+%47 = OpImageRead %v4float %43 %25
+%59 = OpCompositeExtract %float %47 0
+%51 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+OpStore %51 %59
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true);
+}
+
+class VolatileSpreadErrorTest : public PassTest<::testing::Test> {
+ public:
+ VolatileSpreadErrorTest()
+ : 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<SpreadVolatileSemantics>();
+
+ 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(VolatileSpreadErrorTest, VarUsedInMultipleExecutionModelError) {
+ const std::string text =
+ R"(
+OpCapability RuntimeDescriptorArray
+OpCapability RayTracingKHR
+OpCapability SubgroupBallotKHR
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_KHR_ray_tracing"
+OpExtension "SPV_KHR_shader_ballot"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var
+OpEntryPoint GLCompute %compute "Compute" %gl_LocalInvocationIndex %var
+OpExecutionMode %compute LocalSize 16 16 1
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_KHR_ray_tracing"
+OpName %RayGeneration "RayGeneration"
+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 %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+OpDecorate %var BuiltIn SubgroupSize
+%void = OpTypeVoid
+%3 = 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
+%_ptr_Input_uint = OpTypePointer Input %uint
+%var = OpVariable %_ptr_Input_uint Input
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%25 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+%shared = OpVariable %_ptr_Workgroup_uint Workgroup
+
+%RayGeneration = OpFunction %void None %3
+%5 = OpLabel
+%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%20 = OpLoad %uint %var
+%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+%23 = OpLoad %13 %22
+%27 = OpImageRead %v4float %23 %25
+%29 = OpCompositeExtract %float %27 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+OpStore %31 %29
+OpReturn
+OpFunctionEnd
+
+%compute = OpFunction %void None %3
+%66 = OpLabel
+%62 = OpLoad %uint %gl_LocalInvocationIndex
+%63 = OpLoad %uint %var
+%64 = OpIAdd %uint %62 %63
+%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %64
+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);
+}
+
+TEST_F(VolatileSpreadErrorTest,
+ VarUsedInMultipleReverseOrderExecutionModelError) {
+ const std::string text =
+ R"(
+OpCapability RuntimeDescriptorArray
+OpCapability RayTracingKHR
+OpCapability SubgroupBallotKHR
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_KHR_ray_tracing"
+OpExtension "SPV_KHR_shader_ballot"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %compute "Compute" %gl_LocalInvocationIndex %var
+OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var
+OpExecutionMode %compute LocalSize 16 16 1
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_KHR_ray_tracing"
+OpName %RayGeneration "RayGeneration"
+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 %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+OpDecorate %var BuiltIn SubgroupSize
+%void = OpTypeVoid
+%3 = 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
+%_ptr_Input_uint = OpTypePointer Input %uint
+%var = OpVariable %_ptr_Input_uint Input
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%25 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+%shared = OpVariable %_ptr_Workgroup_uint Workgroup
+
+%RayGeneration = OpFunction %void None %3
+%5 = OpLabel
+%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%20 = OpLoad %uint %var
+%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+%23 = OpLoad %13 %22
+%27 = OpImageRead %v4float %23 %25
+%29 = OpCompositeExtract %float %27 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+OpStore %31 %29
+OpReturn
+OpFunctionEnd
+
+%compute = OpFunction %void None %3
+%66 = OpLabel
+%62 = OpLoad %uint %gl_LocalInvocationIndex
+%63 = OpLoad %uint %var
+%64 = OpIAdd %uint %62 %63
+%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %64
+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);
+}
+
+TEST_F(VolatileSpreadErrorTest, FunctionNotInlined) {
+ const std::string text =
+ R"(
+OpCapability RuntimeDescriptorArray
+OpCapability RayTracingKHR
+OpCapability SubgroupBallotKHR
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_KHR_ray_tracing"
+OpExtension "SPV_KHR_shader_ballot"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var
+OpEntryPoint ClosestHitKHR %ClosestHit "ClosestHit" %var
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_KHR_ray_tracing"
+OpName %RayGeneration "RayGeneration"
+OpName %ClosestHit "ClosestHit"
+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 %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+OpDecorate %var BuiltIn SubgroupSize
+%void = OpTypeVoid
+%3 = 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
+%_ptr_Input_uint = OpTypePointer Input %uint
+%var = OpVariable %_ptr_Input_uint Input
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%25 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+%shared = OpVariable %_ptr_Workgroup_uint Workgroup
+
+%RayGeneration = OpFunction %void None %3
+%5 = OpLabel
+%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%20 = OpLoad %uint %19
+%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+%23 = OpLoad %13 %22
+%27 = OpImageRead %v4float %23 %25
+%29 = OpCompositeExtract %float %27 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+OpStore %31 %29
+OpReturn
+OpFunctionEnd
+
+%NotInlined = OpFunction %void None %3
+%32 = OpLabel
+OpReturn
+OpFunctionEnd
+
+%ClosestHit = OpFunction %void None %3
+%45 = OpLabel
+%49 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%40 = OpLoad %uint %49
+%42 = OpAccessChain %_ptr_UniformConstant_13 %images %40
+%43 = OpLoad %13 %42
+%47 = OpImageRead %v4float %43 %25
+%59 = OpCompositeExtract %float %47 0
+%51 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+OpStore %51 %59
+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);
+}
+
+TEST_F(VolatileSpreadErrorTest, VarNotUsedInEntryPointForVolatile) {
+ const std::string text =
+ R"(
+OpCapability RuntimeDescriptorArray
+OpCapability RayTracingKHR
+OpCapability SubgroupBallotKHR
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_KHR_ray_tracing"
+OpExtension "SPV_KHR_shader_ballot"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var
+OpEntryPoint GLCompute %compute "Compute" %gl_LocalInvocationIndex %var
+OpExecutionMode %compute LocalSize 16 16 1
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_KHR_ray_tracing"
+OpName %RayGeneration "RayGeneration"
+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 %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex
+OpDecorate %StorageBuffer BufferBlock
+OpDecorate %sbo DescriptorSet 0
+OpDecorate %sbo Binding 0
+OpDecorate %images DescriptorSet 0
+OpDecorate %images Binding 1
+OpDecorate %images NonWritable
+
+; CHECK-NOT: OpDecorate {{%\w+}} Volatile
+
+OpDecorate %var BuiltIn SubgroupSize
+%void = OpTypeVoid
+%3 = 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
+%_ptr_Input_uint = OpTypePointer Input %uint
+%var = OpVariable %_ptr_Input_uint Input
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%25 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+%shared = OpVariable %_ptr_Workgroup_uint Workgroup
+
+%RayGeneration = OpFunction %void None %3
+%5 = OpLabel
+%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
+%20 = OpLoad %uint %19
+%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+%23 = OpLoad %13 %22
+%27 = OpImageRead %v4float %23 %25
+%29 = OpCompositeExtract %float %27 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
+OpStore %31 %29
+OpReturn
+OpFunctionEnd
+
+%compute = OpFunction %void None %3
+%66 = OpLabel
+%62 = OpLoad %uint %gl_LocalInvocationIndex
+%63 = OpLoad %uint %var
+%64 = OpIAdd %uint %62 %63
+%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %64
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true);
+}
+
+TEST_F(VolatileSpreadTest, RecursivelySpreadVolatile) {
+ const std::string text =
+ R"(
+OpCapability RuntimeDescriptorArray
+OpCapability RayTracingKHR
+OpCapability SubgroupBallotKHR
+OpCapability VulkanMemoryModel
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_KHR_ray_tracing"
+OpExtension "SPV_KHR_shader_ballot"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical Vulkan
+OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var0 %var1
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_KHR_ray_tracing"
+OpName %RayGeneration "RayGeneration"
+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
+
+; CHECK: OpDecorate [[var0:%\w+]] BuiltIn SubgroupEqMask
+; CHECK: OpDecorate [[var1:%\w+]] BuiltIn SubgroupGeMask
+OpDecorate %var0 BuiltIn SubgroupEqMask
+OpDecorate %var1 BuiltIn SubgroupGeMask
+
+%void = OpTypeVoid
+%3 = 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
+%v4uint = OpTypeVector %uint 4
+%_ptr_Input_v4uint = OpTypePointer Input %v4uint
+%_ptr_Input_uint = OpTypePointer Input %uint
+%var0 = OpVariable %_ptr_Input_v4uint Input
+%var1 = OpVariable %_ptr_Input_v4uint Input
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%25 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+
+%RayGeneration = OpFunction %void None %3
+%5 = OpLabel
+
+; CHECK: [[ptr0:%\w+]] = OpAccessChain %_ptr_Input_uint [[var0]] %int_0
+; CHECK: OpLoad {{%\w+}} [[ptr0]] Volatile
+%19 = OpAccessChain %_ptr_Input_uint %var0 %int_0
+%20 = OpLoad %uint %19
+
+%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+%23 = OpLoad %13 %22
+%27 = OpImageRead %v4float %23 %25
+%29 = OpCompositeExtract %float %27 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %uint_1
+
+; CHECK: OpLoad {{%\w+}} [[ptr0]] Volatile
+%24 = OpLoad %uint %19
+
+; CHECK: [[var2:%\w+]] = OpCopyObject %_ptr_Input_v4uint [[var0]]
+; CHECK: [[ptr2:%\w+]] = OpAccessChain %_ptr_Input_uint [[var2]] %int_1
+; CHECK: OpLoad {{%\w+}} [[ptr2]] Volatile
+%18 = OpCopyObject %_ptr_Input_v4uint %var0
+%21 = OpAccessChain %_ptr_Input_uint %18 %int_1
+%26 = OpLoad %uint %21
+
+%28 = OpIAdd %uint %24 %26
+%30 = OpConvertUToF %float %28
+
+; CHECK: [[ptr1:%\w+]] = OpAccessChain %_ptr_Input_uint [[var1]] %int_1
+; CHECK: OpLoad {{%\w+}} [[ptr1]] Volatile
+%32 = OpAccessChain %_ptr_Input_uint %var1 %int_1
+%33 = OpLoad %uint %32
+
+%34 = OpConvertUToF %float %33
+%35 = OpFAdd %float %34 %30
+%36 = OpFAdd %float %35 %29
+OpStore %31 %36
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true);
+}
+
+TEST_F(VolatileSpreadTest, SpreadVolatileOnlyForTargetEntryPoints) {
+ const std::string text =
+ R"(
+OpCapability RuntimeDescriptorArray
+OpCapability RayTracingKHR
+OpCapability SubgroupBallotKHR
+OpCapability VulkanMemoryModel
+OpCapability VulkanMemoryModelDeviceScopeKHR
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_EXT_descriptor_indexing"
+OpExtension "SPV_KHR_ray_tracing"
+OpExtension "SPV_KHR_shader_ballot"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical Vulkan
+OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var0 %var1
+OpEntryPoint GLCompute %compute "Compute" %var0 %var1
+OpExecutionMode %compute LocalSize 16 16 1
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_nonuniform_qualifier"
+OpSourceExtension "GL_KHR_ray_tracing"
+OpName %RayGeneration "RayGeneration"
+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
+
+; CHECK: OpDecorate [[var0:%\w+]] BuiltIn SubgroupEqMask
+; CHECK: OpDecorate [[var1:%\w+]] BuiltIn SubgroupGeMask
+OpDecorate %var0 BuiltIn SubgroupEqMask
+OpDecorate %var1 BuiltIn SubgroupGeMask
+
+%void = OpTypeVoid
+%3 = 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
+%v4uint = OpTypeVector %uint 4
+%_ptr_Input_v4uint = OpTypePointer Input %v4uint
+%_ptr_Input_uint = OpTypePointer Input %uint
+%var0 = OpVariable %_ptr_Input_v4uint Input
+%var1 = OpVariable %_ptr_Input_v4uint Input
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%v2int = OpTypeVector %int 2
+%25 = OpConstantComposite %v2int %int_0 %int_0
+%v4float = OpTypeVector %float 4
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+%shared = OpVariable %_ptr_Workgroup_uint Workgroup
+
+%RayGeneration = OpFunction %void None %3
+%5 = OpLabel
+
+; CHECK: [[ptr0:%\w+]] = OpAccessChain %_ptr_Input_uint [[var0]] %int_0
+; CHECK: OpLoad {{%\w+}} [[ptr0]] Volatile
+%19 = OpAccessChain %_ptr_Input_uint %var0 %int_0
+%20 = OpLoad %uint %19
+
+%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
+%23 = OpLoad %13 %22
+%27 = OpImageRead %v4float %23 %25
+%29 = OpCompositeExtract %float %27 0
+%31 = OpAccessChain %_ptr_Uniform_float %sbo %uint_1
+
+; CHECK: OpLoad {{%\w+}} [[ptr0]] Volatile
+%24 = OpLoad %uint %19
+
+; CHECK: [[var2:%\w+]] = OpCopyObject %_ptr_Input_v4uint [[var0]]
+; CHECK: [[ptr2:%\w+]] = OpAccessChain %_ptr_Input_uint [[var2]] %int_1
+; CHECK: OpLoad {{%\w+}} [[ptr2]] Volatile
+%18 = OpCopyObject %_ptr_Input_v4uint %var0
+%21 = OpAccessChain %_ptr_Input_uint %18 %int_1
+%26 = OpLoad %uint %21
+
+%28 = OpIAdd %uint %24 %26
+%30 = OpConvertUToF %float %28
+
+; CHECK: [[ptr1:%\w+]] = OpAccessChain %_ptr_Input_uint [[var1]] %int_1
+; CHECK: OpLoad {{%\w+}} [[ptr1]] Volatile
+%32 = OpAccessChain %_ptr_Input_uint %var1 %int_1
+%33 = OpLoad %uint %32
+
+%34 = OpConvertUToF %float %33
+%35 = OpFAdd %float %34 %30
+%36 = OpFAdd %float %35 %29
+OpStore %31 %36
+OpReturn
+OpFunctionEnd
+
+%compute = OpFunction %void None %3
+%66 = OpLabel
+
+; CHECK-NOT: OpLoad {{%\w+}} {{%\w+}} Volatile
+%62 = OpLoad %v4uint %var0
+%63 = OpLoad %v4uint %var1
+%64 = OpIAdd %v4uint %62 %63
+%65 = OpCompositeExtract %uint %64 0
+%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %65
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true);
+}
+
+} // namespace
+} // namespace opt
+} // namespace spvtools
diff --git a/test/opt/strip_reflect_info_test.cpp b/test/opt/strip_nonsemantic_info_test.cpp
index f3fc115a..3aacffa3 100644
--- a/test/opt/strip_reflect_info_test.cpp
+++ b/test/opt/strip_nonsemantic_info_test.cpp
@@ -13,10 +13,9 @@
// limitations under the License.
#include <string>
-#include "gmock/gmock.h"
+#include "gmock/gmock.h"
#include "spirv-tools/optimizer.hpp"
-
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
@@ -24,7 +23,6 @@ namespace spvtools {
namespace opt {
namespace {
-using StripLineReflectInfoTest = PassTest<::testing::Test>;
using StripNonSemanticInfoTest = PassTest<::testing::Test>;
// This test acts as an end-to-end code example on how to strip
@@ -33,7 +31,7 @@ using StripNonSemanticInfoTest = PassTest<::testing::Test>;
// option -fhlsl_functionality1 to insert reflection information,
// but then want to filter out the extra instructions before sending
// it to a driver that does not implement VK_GOOGLE_hlsl_functionality1.
-TEST_F(StripLineReflectInfoTest, StripReflectEnd2EndExample) {
+TEST_F(StripNonSemanticInfoTest, StripReflectEnd2EndExample) {
// This is a non-sensical example, but exercises the instructions.
std::string before = R"(OpCapability Shader
OpCapability Linkage
@@ -49,11 +47,11 @@ OpDecorateStringGOOGLE %void HlslSemanticGOOGLE "my goodness"
std::vector<uint32_t> binary_in;
tools.Assemble(before, &binary_in);
- // Instantiate the optimizer, and run the strip-reflection-info
+ // Instantiate the optimizer, and run the strip-nonsemantic-info
// pass over the |binary_in| module, and place the modified module
// into |binary_out|.
spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_1);
- optimizer.RegisterPass(spvtools::CreateStripReflectInfoPass());
+ optimizer.RegisterPass(spvtools::CreateStripNonSemanticInfoPass());
std::vector<uint32_t> binary_out;
optimizer.Run(binary_in.data(), binary_in.size(), &binary_out);
@@ -71,7 +69,7 @@ OpMemoryModel Logical Simple
// This test is functionally the same as the end-to-end test above,
// but uses the test SinglePassRunAndCheck test fixture instead.
-TEST_F(StripLineReflectInfoTest, StripHlslSemantic) {
+TEST_F(StripNonSemanticInfoTest, StripHlslSemantic) {
// This is a non-sensical example, but exercises the instructions.
std::string before = R"(OpCapability Shader
OpCapability Linkage
@@ -90,10 +88,10 @@ OpMemoryModel Logical Simple
%float = OpTypeFloat 32
)";
- SinglePassRunAndCheck<StripReflectInfoPass>(before, after, false);
+ SinglePassRunAndCheck<StripNonSemanticInfoPass>(before, after, false);
}
-TEST_F(StripLineReflectInfoTest, StripHlslCounterBuffer) {
+TEST_F(StripNonSemanticInfoTest, StripHlslCounterBuffer) {
std::string before = R"(OpCapability Shader
OpCapability Linkage
OpExtension "SPV_GOOGLE_hlsl_functionality1"
@@ -109,10 +107,10 @@ OpMemoryModel Logical Simple
%float = OpTypeFloat 32
)";
- SinglePassRunAndCheck<StripReflectInfoPass>(before, after, false);
+ SinglePassRunAndCheck<StripNonSemanticInfoPass>(before, after, false);
}
-TEST_F(StripLineReflectInfoTest, StripHlslSemanticOnMember) {
+TEST_F(StripNonSemanticInfoTest, StripHlslSemanticOnMember) {
// This is a non-sensical example, but exercises the instructions.
std::string before = R"(OpCapability Shader
OpCapability Linkage
@@ -130,7 +128,7 @@ OpMemoryModel Logical Simple
%_struct_3 = OpTypeStruct %float
)";
- SinglePassRunAndCheck<StripReflectInfoPass>(before, after, false);
+ SinglePassRunAndCheck<StripNonSemanticInfoPass>(before, after, false);
}
TEST_F(StripNonSemanticInfoTest, StripNonSemanticImport) {
@@ -144,7 +142,7 @@ OpExtension "SPV_KHR_non_semantic_info"
OpMemoryModel Logical GLSL450
)";
- SinglePassRunAndMatch<StripReflectInfoPass>(text, true);
+ SinglePassRunAndMatch<StripNonSemanticInfoPass>(text, true);
}
TEST_F(StripNonSemanticInfoTest, StripNonSemanticGlobal) {
@@ -159,7 +157,7 @@ OpMemoryModel Logical GLSL450
%1 = OpExtInst %void %ext 1
)";
- SinglePassRunAndMatch<StripReflectInfoPass>(text, true);
+ SinglePassRunAndMatch<StripNonSemanticInfoPass>(text, true);
}
TEST_F(StripNonSemanticInfoTest, StripNonSemanticInFunction) {
@@ -179,7 +177,7 @@ OpReturn
OpFunctionEnd
)";
- SinglePassRunAndMatch<StripReflectInfoPass>(text, true);
+ SinglePassRunAndMatch<StripNonSemanticInfoPass>(text, true);
}
TEST_F(StripNonSemanticInfoTest, StripNonSemanticAfterFunction) {
@@ -199,7 +197,7 @@ OpFunctionEnd
%1 = OpExtInst %void %ext 1 %foo
)";
- SinglePassRunAndMatch<StripReflectInfoPass>(text, true);
+ SinglePassRunAndMatch<StripNonSemanticInfoPass>(text, true);
}
TEST_F(StripNonSemanticInfoTest, StripNonSemanticBetweenFunctions) {
@@ -223,7 +221,74 @@ OpReturn
OpFunctionEnd
)";
- SinglePassRunAndMatch<StripReflectInfoPass>(text, true);
+ SinglePassRunAndMatch<StripNonSemanticInfoPass>(text, true);
+}
+
+// Make sure that strip reflect does not remove the debug info (OpString and
+// OpLine).
+TEST_F(StripNonSemanticInfoTest, DontStripDebug) {
+ std::string text = R"(OpCapability Shader
+OpMemoryModel Logical Simple
+OpEntryPoint Fragment %1 "main"
+OpExecutionMode %1 OriginUpperLeft
+%2 = OpString "file"
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%1 = OpFunction %void None %4
+%5 = OpLabel
+OpLine %2 1 1
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<StripNonSemanticInfoPass>(text, text, false);
+}
+
+TEST_F(StripNonSemanticInfoTest, RemovedNonSemanticDebugInfo) {
+ const std::string text = R"(
+;CHECK-NOT: OpExtension "SPV_KHR_non_semantic_info
+;CHECK-NOT: OpExtInstImport "NonSemantic.Shader.DebugInfo.100
+;CHECK-NOT: OpExtInst %void {{%\w+}} DebugSource
+;CHECK-NOT: OpExtInst %void {{%\w+}} DebugLine
+ OpCapability Shader
+ OpExtension "SPV_KHR_non_semantic_info"
+ %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %PSMain "PSMain" %in_var_COLOR %out_var_SV_TARGET
+ OpExecutionMode %PSMain OriginUpperLeft
+ %5 = OpString "t.hlsl"
+ %6 = OpString "float"
+ %7 = OpString "color"
+ %8 = OpString "PSInput"
+ %9 = OpString "PSMain"
+ %10 = OpString ""
+ %11 = OpString "input"
+ OpName %in_var_COLOR "in.var.COLOR"
+ OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+ OpName %PSMain "PSMain"
+ OpDecorate %in_var_COLOR Location 0
+ OpDecorate %out_var_SV_TARGET Location 0
+ %uint = OpTypeInt 32 0
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %uint_1 = OpConstant %uint 1
+ %uint_9 = OpConstant %uint 9
+ %21 = OpTypeFunction %void
+%in_var_COLOR = OpVariable %_ptr_Input_v4float Input
+%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+ %13 = OpExtInst %void %1 DebugSource %5
+ %PSMain = OpFunction %void None %21
+ %22 = OpLabel
+ %23 = OpLoad %v4float %in_var_COLOR
+ OpStore %out_var_SV_TARGET %23
+ %24 = OpExtInst %void %1 DebugLine %13 %uint_9 %uint_9 %uint_1 %uint_1
+ OpReturn
+ OpFunctionEnd
+)";
+ SinglePassRunAndMatch<StripNonSemanticInfoPass>(text, true);
}
} // namespace
diff --git a/test/opt/unify_const_test.cpp b/test/opt/unify_const_test.cpp
index 6ed21734..0d7c30b0 100644
--- a/test/opt/unify_const_test.cpp
+++ b/test/opt/unify_const_test.cpp
@@ -263,7 +263,7 @@ TEST_F(UnifyFrontEndConstantSingleTest, UnifyWithDecorationOnTypes) {
// decorated flat struct
"%flat_d = OpTypeStruct %int %float",
"%_pf_flat_d = OpTypePointer Function %flat_d",
- // perserved contants. %flat_1 and %flat_d has same members, but
+ // preserved constants. %flat_1 and %flat_d has same members, but
// their type are different in decorations, so they should not be
// used to replace each other.
"%int_1 = OpConstant %int 1",
@@ -682,7 +682,7 @@ INSTANTIATE_TEST_SUITE_P(
// zero-valued composite constant built from zero-valued constant
// component. inner_zero should not be replace by null_inner.
"%inner_zero = OpConstantComposite %inner_struct %bool_zero %float_zero",
- // zero-valued composite contant built from zero-valued constants
+ // zero-valued composite constant built from zero-valued constants
// and null constants.
"%outer_zero = OpConstantComposite %outer_struct %inner_zero %int_null %double_null",
// outer_struct type null constant, it should not be replaced by
@@ -820,7 +820,7 @@ INSTANTIATE_TEST_SUITE_P(
{
"%spec_signed_add_duplicate = OpSpecConstantOp %int IAdd %spec_signed_1 %spec_signed_2",
},
- // use duplicated contants in main
+ // use duplicated constants in main
{
"%int_var = OpVariable %_pf_int Function",
"OpStore %int_var %spec_signed_add_duplicate",
diff --git a/test/opt/upgrade_memory_model_test.cpp b/test/opt/upgrade_memory_model_test.cpp
index 7f64ffd7..2cd3c7df 100644
--- a/test/opt/upgrade_memory_model_test.cpp
+++ b/test/opt/upgrade_memory_model_test.cpp
@@ -404,7 +404,7 @@ OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
OpDecorate %param Coherent
-OpDecorate %param ArrayStride 4
+OpDecorate %ptr_int_StorageBuffer ArrayStride 4
%void = OpTypeVoid
%bool = OpTypeBool
%int = OpTypeInt 32 0
diff --git a/test/opt/vector_dce_test.cpp b/test/opt/vector_dce_test.cpp
index 9bdad375..b14e2256 100644
--- a/test/opt/vector_dce_test.cpp
+++ b/test/opt/vector_dce_test.cpp
@@ -1351,6 +1351,72 @@ OpFunctionEnd
SinglePassRunAndMatch<VectorDCE>(text, true);
}
+TEST_F(VectorDCETest, OutOfBoundsExtract) {
+ // It tests that the vector DCE pass is able to handle an extract with an
+ // index that is out of bounds.
+ const std::string text = R"(
+; CHECK: [[undef:%\w+]] = OpUndef %v4float
+; CHECK: OpCompositeExtract %float [[undef]] 8
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %OutColor
+ OpExecutionMode %main OriginUpperLeft
+ OpDecorate %OutColor Location 0
+ %void = OpTypeVoid
+ %10 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_float = OpTypePointer Output %float
+ %OutColor = OpVariable %_ptr_Output_float Output
+ %null = OpConstantNull %v4float
+ %float_1 = OpConstant %float 1
+ %main = OpFunction %void None %10
+ %28 = OpLabel
+ %33 = OpCompositeInsert %v4float %float_1 %null 1
+ %extract = OpCompositeExtract %float %33 8
+ OpStore %OutColor %extract
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ SinglePassRunAndMatch<VectorDCE>(text, false);
+}
+
+TEST_F(VectorDCETest, OutOfBoundsShuffle) {
+ // It tests that the vector DCE pass is able to handle a shuffle with an
+ // index that is out of bounds.
+ const std::string text = R"(
+; CHECK: [[undef:%\w+]] = OpUndef %v4float
+; CHECK: OpVectorShuffle %v4float [[undef]] [[undef]] 9 10 11 12
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %OutColor
+ OpExecutionMode %main OriginUpperLeft
+ OpDecorate %OutColor Location 0
+ %void = OpTypeVoid
+ %10 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %OutColor = OpVariable %_ptr_Output_v4float Output
+ %null = OpConstantNull %v4float
+ %float_1 = OpConstant %float 1
+ %main = OpFunction %void None %10
+ %28 = OpLabel
+ %33 = OpCompositeInsert %v4float %float_1 %null 1
+ %shuffle = OpVectorShuffle %v4float %33 %33 9 10 11 12
+ OpStore %OutColor %shuffle
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ SinglePassRunAndMatch<VectorDCE>(text, false);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/test/reduce/CMakeLists.txt b/test/reduce/CMakeLists.txt
index 652f0ab5..121cd4f0 100644
--- a/test/reduce/CMakeLists.txt
+++ b/test/reduce/CMakeLists.txt
@@ -14,6 +14,7 @@
add_spvtools_unittest(TARGET reduce
SRCS
+ conditional_branch_to_simple_conditional_branch_test.cpp
merge_blocks_test.cpp
operand_to_constant_test.cpp
operand_to_undef_test.cpp
@@ -26,10 +27,10 @@ add_spvtools_unittest(TARGET reduce
remove_selection_test.cpp
remove_unused_instruction_test.cpp
remove_unused_struct_member_test.cpp
+ simple_conditional_branch_to_branch_test.cpp
+ structured_construct_to_block_test.cpp
structured_loop_to_selection_test.cpp
validation_during_reduction_test.cpp
- conditional_branch_to_simple_conditional_branch_test.cpp
- simple_conditional_branch_to_branch_test.cpp
LIBS SPIRV-Tools-reduce
)
diff --git a/test/reduce/merge_blocks_test.cpp b/test/reduce/merge_blocks_test.cpp
index 8506ee08..c472301f 100644
--- a/test/reduce/merge_blocks_test.cpp
+++ b/test/reduce/merge_blocks_test.cpp
@@ -647,6 +647,64 @@ TEST(MergeBlocksReductionPassTest, LoopReturnReverse) {
MergeBlocksReductionPassTest_LoopReturn_Helper(true);
}
+TEST(MergeBlocksReductionPassTest, MergeUnreachable) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %11 = OpTypeBool
+ %12 = OpConstantFalse %11
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ %9 = OpLabel
+ OpBranch %100
+ %100 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, shader, kReduceAssembleOption);
+ const auto ops =
+ MergeBlocksReductionOpportunityFinder().GetAvailableOpportunities(
+ context.get(), 0);
+ ASSERT_EQ(1, ops.size());
+
+ ASSERT_TRUE(ops[0]->PreconditionHolds());
+ ops[0]->TryToApply();
+
+ std::string after = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %11 = OpTypeBool
+ %12 = OpConstantFalse %11
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ %9 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CheckEqual(env, after, context.get());
+}
+
} // namespace
} // namespace reduce
} // namespace spvtools
diff --git a/test/reduce/reduce_test_util.cpp b/test/reduce/reduce_test_util.cpp
index 0c234111..42716600 100644
--- a/test/reduce/reduce_test_util.cpp
+++ b/test/reduce/reduce_test_util.cpp
@@ -21,6 +21,29 @@
namespace spvtools {
namespace reduce {
+const spvtools::MessageConsumer kConsoleMessageConsumer =
+ [](spv_message_level_t level, const char*, const spv_position_t& position,
+ const char* message) -> void {
+ switch (level) {
+ case SPV_MSG_FATAL:
+ case SPV_MSG_INTERNAL_ERROR:
+ case SPV_MSG_ERROR:
+ std::cerr << "error: line " << position.index << ": " << message
+ << std::endl;
+ break;
+ case SPV_MSG_WARNING:
+ std::cout << "warning: line " << position.index << ": " << message
+ << std::endl;
+ break;
+ case SPV_MSG_INFO:
+ std::cout << "info: line " << position.index << ": " << message
+ << std::endl;
+ break;
+ default:
+ break;
+ }
+};
+
void CheckEqual(const spv_target_env env,
const std::vector<uint32_t>& expected_binary,
const std::vector<uint32_t>& actual_binary) {
@@ -55,8 +78,9 @@ void CheckEqual(const spv_target_env env, const std::string& expected_text,
void CheckValid(spv_target_env env, const opt::IRContext* ir) {
std::vector<uint32_t> binary;
ir->module()->ToBinary(&binary, false);
- SpirvTools t(env);
- ASSERT_TRUE(t.Validate(binary));
+ SpirvTools tools(env);
+ tools.SetMessageConsumer(kConsoleMessageConsumer);
+ ASSERT_TRUE(tools.Validate(binary));
}
std::string ToString(spv_target_env env, const opt::IRContext* ir) {
diff --git a/test/reduce/structured_construct_to_block_test.cpp b/test/reduce/structured_construct_to_block_test.cpp
new file mode 100644
index 00000000..95009660
--- /dev/null
+++ b/test/reduce/structured_construct_to_block_test.cpp
@@ -0,0 +1,245 @@
+// Copyright (c) 2021 Alastair F. Donaldson
+//
+// 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/reduce/structured_construct_to_block_reduction_opportunity_finder.h"
+
+#include "source/opt/build_module.h"
+#include "source/reduce/reduction_opportunity.h"
+#include "test/reduce/reduce_test_util.h"
+
+namespace spvtools {
+namespace reduce {
+namespace {
+
+TEST(StructuredConstructToBlockReductionPassTest, SimpleTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %10 = OpTypeBool
+ %11 = OpConstantTrue %10
+ %19 = OpConstant %6 3
+ %29 = OpConstant %6 1
+ %31 = OpConstant %6 2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ OpStore %8 %9
+ OpSelectionMerge %13 None
+ OpBranchConditional %11 %12 %13
+ %12 = OpLabel
+ OpBranch %13
+ %13 = OpLabel
+ OpBranch %14
+ %14 = OpLabel
+ OpLoopMerge %16 %17 None
+ OpBranch %15
+ %15 = OpLabel
+ %18 = OpLoad %6 %8
+ %20 = OpSGreaterThan %10 %18 %19
+ OpSelectionMerge %22 None
+ OpBranchConditional %20 %21 %22
+ %21 = OpLabel
+ OpBranch %16
+ %22 = OpLabel
+ OpBranch %17
+ %17 = OpLabel
+ OpBranch %14
+ %16 = OpLabel
+ %24 = OpLoad %6 %8
+ OpSelectionMerge %28 None
+ OpSwitch %24 %27 1 %25 2 %26
+ %27 = OpLabel
+ OpStore %8 %19
+ OpBranch %28
+ %25 = OpLabel
+ OpStore %8 %29
+ OpBranch %28
+ %26 = OpLabel
+ OpStore %8 %31
+ OpBranch %28
+ %28 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
+ const auto ops = StructuredConstructToBlockReductionOpportunityFinder()
+ .GetAvailableOpportunities(context.get(), 0);
+ ASSERT_EQ(3, ops.size());
+
+ ASSERT_TRUE(ops[0]->PreconditionHolds());
+ ops[0]->TryToApply();
+ CheckValid(env, context.get());
+
+ ASSERT_TRUE(ops[1]->PreconditionHolds());
+ ops[1]->TryToApply();
+ CheckValid(env, context.get());
+
+ ASSERT_TRUE(ops[2]->PreconditionHolds());
+ ops[2]->TryToApply();
+ CheckValid(env, context.get());
+
+ std::string expected = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %10 = OpTypeBool
+ %11 = OpConstantTrue %10
+ %19 = OpConstant %6 3
+ %29 = OpConstant %6 1
+ %31 = OpConstant %6 2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ OpStore %8 %9
+ OpBranch %13
+ %13 = OpLabel
+ OpBranch %14
+ %14 = OpLabel
+ OpBranch %16
+ %16 = OpLabel
+ %24 = OpLoad %6 %8
+ OpBranch %28
+ %28 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ CheckEqual(env, expected, context.get());
+}
+
+TEST(StructuredConstructToBlockReductionPassTest, CannotBeRemovedDueToUses) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ OpName %100 "temp"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %10 = OpTypeBool
+ %11 = OpConstantTrue %10
+ %19 = OpConstant %6 3
+ %29 = OpConstant %6 1
+ %31 = OpConstant %6 2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ OpStore %8 %9
+ OpSelectionMerge %13 None
+ OpBranchConditional %11 %12 %13
+ %12 = OpLabel
+ %100 = OpCopyObject %10 %11
+ OpBranch %13
+ %13 = OpLabel
+ OpBranch %14
+ %14 = OpLabel
+ OpLoopMerge %16 %17 None
+ OpBranch %15
+ %15 = OpLabel
+ %18 = OpLoad %6 %8
+ %20 = OpSGreaterThan %10 %18 %19
+ OpSelectionMerge %22 None
+ OpBranchConditional %20 %21 %22
+ %21 = OpLabel
+ OpBranch %16
+ %22 = OpLabel
+ OpBranch %17
+ %17 = OpLabel
+ OpBranch %14
+ %16 = OpLabel
+ %101 = OpCopyObject %6 %18
+ %24 = OpLoad %6 %8
+ OpSelectionMerge %28 None
+ OpSwitch %24 %27 1 %25 2 %26
+ %27 = OpLabel
+ OpStore %8 %19
+ %102 = OpCopyObject %10 %11
+ OpBranch %28
+ %25 = OpLabel
+ OpStore %8 %29
+ OpBranch %28
+ %26 = OpLabel
+ OpStore %8 %31
+ OpBranch %28
+ %28 = OpLabel
+ %103 = OpPhi %10 %102 %27 %11 %25 %11 %26
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
+ const auto ops = StructuredConstructToBlockReductionOpportunityFinder()
+ .GetAvailableOpportunities(context.get(), 0);
+ ASSERT_TRUE(ops.empty());
+}
+
+TEST(StructuredConstructToBlockReductionPassTest,
+ CannotBeRemovedDueToOpPhiAtMerge) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeBool
+ %11 = OpConstantTrue %10
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpSelectionMerge %13 None
+ OpBranchConditional %11 %12 %13
+ %12 = OpLabel
+ OpBranch %13
+ %13 = OpLabel
+ %101 = OpPhi %10 %11 %5 %11 %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
+ const auto ops = StructuredConstructToBlockReductionOpportunityFinder()
+ .GetAvailableOpportunities(context.get(), 0);
+ ASSERT_TRUE(ops.empty());
+}
+
+} // namespace
+} // namespace reduce
+} // namespace spvtools
diff --git a/test/target_env_test.cpp b/test/target_env_test.cpp
index 4acb8ff2..7917cbfb 100644
--- a/test/target_env_test.cpp
+++ b/test/target_env_test.cpp
@@ -135,7 +135,8 @@ INSTANTIATE_TEST_SUITE_P(
{VK(1, 0), SPV(1, 3), true, SPV_ENV_VULKAN_1_1},
{VK(1, 0), SPV(1, 4), true, SPV_ENV_VULKAN_1_1_SPIRV_1_4},
{VK(1, 0), SPV(1, 5), true, SPV_ENV_VULKAN_1_2},
- {VK(1, 0), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0},
+ {VK(1, 0), SPV(1, 6), true, SPV_ENV_VULKAN_1_3},
+ {VK(1, 0), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0},
// Vulkan 1.1 cases
{VK(1, 1), SPV(1, 0), true, SPV_ENV_VULKAN_1_1},
{VK(1, 1), SPV(1, 1), true, SPV_ENV_VULKAN_1_1},
@@ -143,7 +144,8 @@ INSTANTIATE_TEST_SUITE_P(
{VK(1, 1), SPV(1, 3), true, SPV_ENV_VULKAN_1_1},
{VK(1, 1), SPV(1, 4), true, SPV_ENV_VULKAN_1_1_SPIRV_1_4},
{VK(1, 1), SPV(1, 5), true, SPV_ENV_VULKAN_1_2},
- {VK(1, 1), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0},
+ {VK(1, 1), SPV(1, 6), true, SPV_ENV_VULKAN_1_3},
+ {VK(1, 1), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0},
// Vulkan 1.2 cases
{VK(1, 2), SPV(1, 0), true, SPV_ENV_VULKAN_1_2},
{VK(1, 2), SPV(1, 1), true, SPV_ENV_VULKAN_1_2},
@@ -151,9 +153,17 @@ INSTANTIATE_TEST_SUITE_P(
{VK(1, 2), SPV(1, 3), true, SPV_ENV_VULKAN_1_2},
{VK(1, 2), SPV(1, 4), true, SPV_ENV_VULKAN_1_2},
{VK(1, 2), SPV(1, 5), true, SPV_ENV_VULKAN_1_2},
- {VK(1, 2), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0},
+ {VK(1, 2), SPV(1, 6), true, SPV_ENV_VULKAN_1_3},
+ {VK(1, 2), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0},
// Vulkan 1.3 cases
- {VK(1, 3), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0},
+ {VK(1, 3), SPV(1, 0), true, SPV_ENV_VULKAN_1_3},
+ {VK(1, 3), SPV(1, 1), true, SPV_ENV_VULKAN_1_3},
+ {VK(1, 3), SPV(1, 2), true, SPV_ENV_VULKAN_1_3},
+ {VK(1, 3), SPV(1, 3), true, SPV_ENV_VULKAN_1_3},
+ {VK(1, 3), SPV(1, 4), true, SPV_ENV_VULKAN_1_3},
+ {VK(1, 3), SPV(1, 5), true, SPV_ENV_VULKAN_1_3},
+ {VK(1, 3), SPV(1, 6), true, SPV_ENV_VULKAN_1_3},
+ {VK(1, 3), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0},
// Vulkan 2.0 cases
{VK(2, 0), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0},
// Vulkan 99.0 cases
diff --git a/test/test_fixture.h b/test/test_fixture.h
index 0c5bfc9c..029fc854 100644
--- a/test/test_fixture.h
+++ b/test/test_fixture.h
@@ -15,6 +15,7 @@
#ifndef TEST_TEST_FIXTURE_H_
#define TEST_TEST_FIXTURE_H_
+#include <algorithm>
#include <string>
#include <vector>
@@ -91,12 +92,26 @@ class TextToBinaryTestBase : public T {
return diagnostic->error;
}
+ // Potentially flip the words in the binary representation to the other
+ // endianness
+ template <class It>
+ void MaybeFlipWords(bool flip_words, It begin, It end) {
+ SCOPED_TRACE(flip_words ? "Flipped Endianness" : "Normal Endianness");
+ if (flip_words) {
+ std::transform(begin, end, begin, [](const uint32_t raw_word) {
+ return spvFixWord(raw_word, I32_ENDIAN_HOST == I32_ENDIAN_BIG
+ ? SPV_ENDIANNESS_LITTLE
+ : SPV_ENDIANNESS_BIG);
+ });
+ }
+ }
+
// Encodes SPIR-V text into binary and then decodes the binary using
// given options. Returns the decoded text.
std::string EncodeAndDecodeSuccessfully(
const std::string& txt,
uint32_t disassemble_options = SPV_BINARY_TO_TEXT_OPTION_NONE,
- spv_target_env env = SPV_ENV_UNIVERSAL_1_0) {
+ spv_target_env env = SPV_ENV_UNIVERSAL_1_0, bool flip_words = false) {
DestroyBinary();
DestroyDiagnostic();
ScopedContext context(env);
@@ -110,6 +125,8 @@ class TextToBinaryTestBase : public T {
EXPECT_EQ(SPV_SUCCESS, error);
if (!binary) return "";
+ MaybeFlipWords(flip_words, binary->code, binary->code + binary->wordCount);
+
spv_text decoded_text;
error = spvBinaryToText(context.context, binary->code, binary->wordCount,
disassemble_options, &decoded_text, &diagnostic);
diff --git a/test/text_to_binary.control_flow_test.cpp b/test/text_to_binary.control_flow_test.cpp
index 3e117b8f..abae6a22 100644
--- a/test/text_to_binary.control_flow_test.cpp
+++ b/test/text_to_binary.control_flow_test.cpp
@@ -379,7 +379,7 @@ INSTANTIATE_TEST_SUITE_P(
"OpTypeQueue",
"OpTypePipe ReadOnly",
- // Skip OpTypeForwardPointer becasuse it doesn't even produce a result
+ // Skip OpTypeForwardPointer because it doesn't even produce a result
// ID.
// At least one thing that isn't a type at all
diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp
index 08579843..e5f152e4 100644
--- a/test/text_to_binary.extension_test.cpp
+++ b/test/text_to_binary.extension_test.cpp
@@ -197,7 +197,7 @@ INSTANTIATE_TEST_SUITE_P(
SpvBuiltInSubgroupLtMask})},
})));
-// The old builtin names (with KHR suffix) still work in the assmebler, and
+// The old builtin names (with KHR suffix) still work in the assembler, and
// map to the enums without the KHR.
INSTANTIATE_TEST_SUITE_P(
SPV_KHR_shader_ballot_vulkan_1_1_alias_check, ExtensionAssemblyTest,
@@ -957,66 +957,82 @@ INSTANTIATE_TEST_SUITE_P(
INSTANTIATE_TEST_SUITE_P(
SPV_KHR_integer_dot_product, ExtensionRoundTripTest,
Combine(
- Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_0,
- SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2),
+ 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_integer_dot_product\"\n",
MakeInstruction(SpvOpExtension,
MakeVector("SPV_KHR_integer_dot_product"))},
- {"OpCapability DotProductInputAllKHR\n",
+ {"OpCapability DotProductInputAll\n",
MakeInstruction(SpvOpCapability,
{SpvCapabilityDotProductInputAllKHR})},
- {"OpCapability DotProductInput4x8BitKHR\n",
+ {"OpCapability DotProductInput4x8Bit\n",
MakeInstruction(SpvOpCapability,
{SpvCapabilityDotProductInput4x8BitKHR})},
- {"OpCapability DotProductInput4x8BitPackedKHR\n",
+ {"OpCapability DotProductInput4x8BitPacked\n",
MakeInstruction(SpvOpCapability,
{SpvCapabilityDotProductInput4x8BitPackedKHR})},
- {"OpCapability DotProductKHR\n",
+ {"OpCapability DotProduct\n",
MakeInstruction(SpvOpCapability, {SpvCapabilityDotProductKHR})},
- {"%2 = OpSDotKHR %1 %3 %4\n",
+ {"%2 = OpSDot %1 %3 %4\n",
MakeInstruction(SpvOpSDotKHR, {1, 2, 3, 4})},
- {"%2 = OpSDotKHR %1 %3 %4 PackedVectorFormat4x8BitKHR\n",
+ {"%2 = OpSDot %1 %3 %4 PackedVectorFormat4x8Bit\n",
MakeInstruction(
SpvOpSDotKHR,
{1, 2, 3, 4,
SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
- {"%2 = OpUDotKHR %1 %3 %4\n",
+ {"%2 = OpUDot %1 %3 %4\n",
MakeInstruction(SpvOpUDotKHR, {1, 2, 3, 4})},
- {"%2 = OpUDotKHR %1 %3 %4 PackedVectorFormat4x8BitKHR\n",
+ {"%2 = OpUDot %1 %3 %4 PackedVectorFormat4x8Bit\n",
MakeInstruction(
SpvOpUDotKHR,
{1, 2, 3, 4,
SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
- {"%2 = OpSUDotKHR %1 %3 %4\n",
+ {"%2 = OpSUDot %1 %3 %4\n",
MakeInstruction(SpvOpSUDotKHR, {1, 2, 3, 4})},
- {"%2 = OpSUDotKHR %1 %3 %4 PackedVectorFormat4x8BitKHR\n",
+ {"%2 = OpSUDot %1 %3 %4 PackedVectorFormat4x8Bit\n",
MakeInstruction(
SpvOpSUDotKHR,
{1, 2, 3, 4,
SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
- {"%2 = OpSDotAccSatKHR %1 %3 %4 %5\n",
+ {"%2 = OpSDotAccSat %1 %3 %4 %5\n",
MakeInstruction(SpvOpSDotAccSatKHR, {1, 2, 3, 4, 5})},
- {"%2 = OpSDotAccSatKHR %1 %3 %4 %5 PackedVectorFormat4x8BitKHR\n",
+ {"%2 = OpSDotAccSat %1 %3 %4 %5 PackedVectorFormat4x8Bit\n",
MakeInstruction(
SpvOpSDotAccSatKHR,
{1, 2, 3, 4, 5,
SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
- {"%2 = OpUDotAccSatKHR %1 %3 %4 %5\n",
+ {"%2 = OpUDotAccSat %1 %3 %4 %5\n",
MakeInstruction(SpvOpUDotAccSatKHR, {1, 2, 3, 4, 5})},
- {"%2 = OpUDotAccSatKHR %1 %3 %4 %5 PackedVectorFormat4x8BitKHR\n",
+ {"%2 = OpUDotAccSat %1 %3 %4 %5 PackedVectorFormat4x8Bit\n",
MakeInstruction(
SpvOpUDotAccSatKHR,
{1, 2, 3, 4, 5,
SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
- {"%2 = OpSUDotAccSatKHR %1 %3 %4 %5\n",
+ {"%2 = OpSUDotAccSat %1 %3 %4 %5\n",
MakeInstruction(SpvOpSUDotAccSatKHR, {1, 2, 3, 4, 5})},
- {"%2 = OpSUDotAccSatKHR %1 %3 %4 %5 PackedVectorFormat4x8BitKHR\n",
+ {"%2 = OpSUDotAccSat %1 %3 %4 %5 PackedVectorFormat4x8Bit\n",
MakeInstruction(
SpvOpSUDotAccSatKHR,
{1, 2, 3, 4, 5,
SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})},
})));
+// SPV_KHR_bit_instructions
+
+INSTANTIATE_TEST_SUITE_P(
+ SPV_KHR_bit_instructions, ExtensionRoundTripTest,
+ Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5,
+ SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2),
+ ValuesIn(std::vector<AssemblyCase>{
+ {"OpExtension \"SPV_KHR_bit_instructions\"\n",
+ MakeInstruction(SpvOpExtension,
+ MakeVector("SPV_KHR_bit_instructions"))},
+ {"OpCapability BitInstructions\n",
+ MakeInstruction(SpvOpCapability,
+ {SpvCapabilityBitInstructions})},
+ })));
+
} // namespace
} // namespace spvtools
diff --git a/test/text_to_binary_test.cpp b/test/text_to_binary_test.cpp
index 57f0a6ce..0b348e87 100644
--- a/test/text_to_binary_test.cpp
+++ b/test/text_to_binary_test.cpp
@@ -65,7 +65,7 @@ INSTANTIATE_TEST_SUITE_P(
{SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 1, "NotNaN"},
{SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 2, "NotInf"},
{SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotNaN|NotInf"},
- // Mask experssions are symmetric.
+ // Mask expressions are symmetric.
{SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotInf|NotNaN"},
// Repeating a value has no effect.
{SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotInf|NotNaN|NotInf"},
@@ -247,12 +247,6 @@ INSTANTIATE_TEST_SUITE_P(
{"0x1.804p4", 0x00004e01},
}));
-TEST(CreateContext, InvalidEnvironment) {
- spv_target_env env;
- std::memset(&env, 99, sizeof(env));
- EXPECT_THAT(spvContextCreate(env), IsNull());
-}
-
TEST(CreateContext, UniversalEnvironment) {
auto c = spvContextCreate(SPV_ENV_UNIVERSAL_1_0);
EXPECT_THAT(c, NotNull());
diff --git a/test/tools/expect.py b/test/tools/expect.py
index 0b51adc9..7351c029 100755
--- a/test/tools/expect.py
+++ b/test/tools/expect.py
@@ -285,6 +285,21 @@ class ValidObjectFile1_5(ReturnCodeIsZero, CorrectObjectFilePreamble):
return True, ''
+class ValidObjectFile1_6(ReturnCodeIsZero, CorrectObjectFilePreamble):
+ """Mixin class for checking that every input file generates a valid SPIR-V 1.6
+ object file following the object file naming rule, and there is no output on
+ stdout/stderr."""
+
+ def check_object_file_preamble(self, status):
+ for input_filename in status.input_filenames:
+ object_filename = get_object_filename(input_filename)
+ success, message = self.verify_object_file_preamble(
+ os.path.join(status.directory, object_filename), 0x10600)
+ if not success:
+ return False, message
+ return True, ''
+
+
class ValidObjectFileWithAssemblySubstr(SuccessfulReturn,
CorrectObjectFilePreamble):
"""Mixin class for checking that every input file generates a valid object
diff --git a/test/tools/opt/flags.py b/test/tools/opt/flags.py
index c79f6807..52a43c51 100644
--- a/test/tools/opt/flags.py
+++ b/test/tools/opt/flags.py
@@ -34,7 +34,7 @@ def empty_main_assembly():
@inside_spirv_testsuite('SpirvOptBase')
-class TestAssemblyFileAsOnlyParameter(expect.ValidObjectFile1_5):
+class TestAssemblyFileAsOnlyParameter(expect.ValidObjectFile1_6):
"""Tests that spirv-opt accepts a SPIR-V object file."""
shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm')
@@ -52,7 +52,7 @@ class TestHelpFlag(expect.ReturnCodeIsZero, expect.StdoutMatch):
@inside_spirv_testsuite('SpirvOptFlags')
-class TestValidPassFlags(expect.ValidObjectFile1_5,
+class TestValidPassFlags(expect.ValidObjectFile1_6,
expect.ExecutedListOfPasses):
"""Tests that spirv-opt accepts all valid optimization flags."""
@@ -72,7 +72,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_5,
'--private-to-local', '--reduce-load-size', '--redundancy-elimination',
'--remove-duplicates', '--replace-invalid-opcode', '--ssa-rewrite',
'--scalar-replacement', '--scalar-replacement=42', '--strength-reduction',
- '--strip-debug', '--strip-reflect', '--vector-dce', '--workaround-1209',
+ '--strip-debug', '--strip-nonsemantic', '--vector-dce', '--workaround-1209',
'--unify-const', '--graphics-robust-access', '--wrap-opkill', '--amd-ext-to-khr'
]
expected_passes = [
@@ -117,7 +117,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_5,
'scalar-replacement=42',
'strength-reduction',
'strip-debug',
- 'strip-reflect',
+ 'strip-nonsemantic',
'vector-dce',
'workaround-1209',
'unify-const',
@@ -132,7 +132,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_5,
@inside_spirv_testsuite('SpirvOptFlags')
-class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_5,
+class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_6,
expect.ExecutedListOfPasses):
"""Tests that spirv-opt schedules all the passes triggered by -O."""
@@ -190,7 +190,7 @@ class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_5,
@inside_spirv_testsuite('SpirvOptFlags')
-class TestSizeOptimizationPasses(expect.ValidObjectFile1_5,
+class TestSizeOptimizationPasses(expect.ValidObjectFile1_6,
expect.ExecutedListOfPasses):
"""Tests that spirv-opt schedules all the passes triggered by -Os."""
@@ -237,7 +237,7 @@ class TestSizeOptimizationPasses(expect.ValidObjectFile1_5,
@inside_spirv_testsuite('SpirvOptFlags')
-class TestLegalizationPasses(expect.ValidObjectFile1_5,
+class TestLegalizationPasses(expect.ValidObjectFile1_6,
expect.ExecutedListOfPasses):
"""Tests that spirv-opt schedules all the passes triggered by --legalize-hlsl.
"""
diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt
index 83249641..64eba446 100644
--- a/test/val/CMakeLists.txt
+++ b/test/val/CMakeLists.txt
@@ -23,6 +23,7 @@ set(VAL_TEST_COMMON_SRCS
add_spvtools_unittest(TARGET val_abcde
SRCS
val_adjacency_test.cpp
+ val_annotation_test.cpp
val_arithmetics_test.cpp
val_atomics_test.cpp
val_barriers_test.cpp
@@ -42,8 +43,10 @@ add_spvtools_unittest(TARGET val_abcde
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_ext_inst_test.cpp
+ val_ext_inst_debug_test.cpp
${VAL_TEST_COMMON_SRCS}
LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}
PCH_FILE pch_test_val
diff --git a/test/val/val_annotation_test.cpp b/test/val/val_annotation_test.cpp
new file mode 100644
index 00000000..889c76ca
--- /dev/null
+++ b/test/val/val_annotation_test.cpp
@@ -0,0 +1,951 @@
+// Copyright (c) 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Validation tests for decorations
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "test/test_fixture.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_code_generator.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::Combine;
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::Values;
+
+using DecorationTest = spvtest::ValidateBase<bool>;
+
+TEST_F(DecorationTest, WorkgroupSizeShader) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %ones BuiltIn WorkgroupSize
+%int = OpTypeInt 32 0
+%int3 = OpTypeVector %int 3
+%int_1 = OpConstant %int 1
+%ones = OpConstantComposite %int3 %int_1 %int_1 %int_1
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(DecorationTest, WorkgroupSizeKernel) {
+ const std::string text = R"(
+OpCapability Kernel
+OpCapability Linkage
+OpMemoryModel Logical OpenCL
+OpDecorate %var BuiltIn WorkgroupSize
+%int = OpTypeInt 32 0
+%int3 = OpTypeVector %int 3
+%ptr = OpTypePointer Input %int3
+%var = OpVariable %ptr Input
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+using MemberOnlyDecorations = spvtest::ValidateBase<std::string>;
+
+TEST_P(MemberOnlyDecorations, MemberDecoration) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpMemberDecorate %struct 0 )" +
+ deco + R"(
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%float2x2 = OpTypeMatrix %float2 2
+%struct = OpTypeStruct %float2x2
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(MemberOnlyDecorations, Decoration) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %struct )" + deco +
+ R"(
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%float2x2 = OpTypeMatrix %float2 2
+%struct = OpTypeStruct %float2x2
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("can only be applied to structure members"));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidateMemberOnlyDecorations, MemberOnlyDecorations,
+ Values("RowMajor", "ColMajor", "MatrixStride 16"
+ // SPIR-V spec bug?
+ /*,"Offset 0"*/));
+
+using NonMemberOnlyDecorations = spvtest::ValidateBase<std::string>;
+
+TEST_P(NonMemberOnlyDecorations, MemberDecoration) {
+ const auto deco = GetParam();
+ const auto text = R"(
+OpCapability Shader
+OpCapability Kernel
+OpCapability Linkage
+OpCapability InputAttachment
+OpCapability Addresses
+OpCapability PhysicalStorageBufferAddresses
+OpCapability ShaderNonUniform
+OpExtension "SPV_KHR_no_integer_wrap_decoration"
+OpExtension "SPV_KHR_physical_storage_buffer"
+OpExtension "SPV_GOOGLE_hlsl_functionality1"
+OpExtension "SPV_EXT_descriptor_indexing"
+OpMemoryModel Logical GLSL450
+OpMemberDecorate %struct 0 )" +
+ deco + R"(
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%float2x2 = OpTypeMatrix %float2 2
+%struct = OpTypeStruct %float2x2
+)";
+
+ CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("cannot be applied to structure members"));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ValidateNonMemberOnlyDecorations, NonMemberOnlyDecorations,
+ Values("SpecId 1", "Block", "BufferBlock", "ArrayStride 4", "GLSLShared",
+ "GLSLPacked", "CPacked",
+ // TODO: https://github.com/KhronosGroup/glslang/issues/703:
+ // glslang applies Restrict to structure members.
+ //"Restrict",
+ "Aliased", "Constant", "Uniform", "SaturatedConversion", "Index 0",
+ "Binding 0", "DescriptorSet 0", "FuncParamAttr Zext",
+ "FPRoundingMode RTE", "FPFastMathMode None",
+ "LinkageAttributes \"ext\" Import", "NoContraction",
+ "InputAttachmentIndex 0", "Alignment 4", "MaxByteOffset 4",
+ "AlignmentId %float", "MaxByteOffsetId %float", "NoSignedWrap",
+ "NoUnsignedWrap", "NonUniform", "RestrictPointer", "AliasedPointer",
+ "CounterBuffer %float"));
+
+using StructDecorations = spvtest::ValidateBase<std::string>;
+
+TEST_P(StructDecorations, Struct) {
+ const std::string deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Kernel
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %struct )" + deco +
+ R"(
+%struct = OpTypeStruct
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(StructDecorations, OtherType) {
+ const std::string deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Kernel
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %int )" + deco + R"(
+%int = OpTypeInt 32 0
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type"));
+}
+
+TEST_P(StructDecorations, Variable) {
+ const std::string deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Kernel
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %var )" + deco + R"(
+%int = OpTypeInt 32 0
+%ptr = OpTypePointer Private %int
+%var = OpVariable %ptr Private
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type"));
+}
+
+TEST_P(StructDecorations, FunctionParameter) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Kernel
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %func LinkageAttributes "import" Import
+OpDecorate %param )" + deco +
+ R"(
+%int = OpTypeInt 32 0
+%void = OpTypeVoid
+%fn = OpTypeFunction %void %int
+%func = OpFunction %void None %fn
+%param = OpFunctionParameter %int
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type"));
+}
+
+TEST_P(StructDecorations, Constant) {
+ const std::string deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Kernel
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %int_0 )" + deco +
+ R"(
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type"));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidateStructDecorations, StructDecorations,
+ Values("Block", "BufferBlock", "GLSLShared",
+ "GLSLPacked", "CPacked"));
+
+using ArrayDecorations = spvtest::ValidateBase<std::string>;
+
+TEST_P(ArrayDecorations, Array) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %array )" + deco +
+ R"(
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %int %int_4
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ArrayDecorations, RuntimeArray) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %array )" + deco +
+ R"(
+%int = OpTypeInt 32 0
+%array = OpTypeRuntimeArray %int
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ArrayDecorations, Pointer) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %ptr )" + deco + R"(
+%int = OpTypeInt 32 0
+%ptr = OpTypePointer Workgroup %int
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ArrayDecorations, Struct) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %struct )" + deco +
+ R"(
+%int = OpTypeInt 32 0
+%struct = OpTypeStruct %int
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("must be an array or pointer type"));
+}
+
+TEST_P(ArrayDecorations, Variable) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %var )" + deco + R"(
+%int = OpTypeInt 32 0
+%ptr = OpTypePointer Private %int
+%var = OpVariable %ptr Private
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("must be an array or pointer type"));
+}
+
+TEST_P(ArrayDecorations, FunctionParameter) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %func LinkageAttributes "import" Import
+OpDecorate %param )" + deco +
+ R"(
+%int = OpTypeInt 32 0
+%void = OpTypeVoid
+%fn = OpTypeFunction %void %int
+%func = OpFunction %void None %fn
+%param = OpFunctionParameter %int
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("must be an array or pointer type"));
+}
+
+TEST_P(ArrayDecorations, Constant) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %null )" + deco +
+ R"(
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %int %int_4
+%null = OpConstantNull %array
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("must be an array or pointer type"));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidateArrayDecorations, ArrayDecorations,
+ Values("ArrayStride 4"));
+
+using BuiltInDecorations = spvtest::ValidateBase<std::string>;
+
+TEST_P(BuiltInDecorations, Variable) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %var BuiltIn )" +
+ deco + R"(
+%int = OpTypeInt 32 0
+%ptr = OpTypePointer Input %int
+%var = OpVariable %ptr Input
+)";
+
+ CompileSuccessfully(text);
+ if (deco != "WorkgroupSize") {
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+ } else {
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("must be a constant for WorkgroupSize"));
+ }
+}
+
+TEST_P(BuiltInDecorations, IntegerType) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %int BuiltIn )" +
+ deco + R"(
+%int = OpTypeInt 32 0
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("BuiltIns can only target variables, structure members "
+ "or constants"));
+}
+
+TEST_P(BuiltInDecorations, FunctionParameter) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %func LinkageAttributes "import" Import
+OpDecorate %param BuiltIn )" +
+ deco + R"(
+%int = OpTypeInt 32 0
+%void = OpTypeVoid
+%fn = OpTypeFunction %void %int
+%func = OpFunction %void None %fn
+%param = OpFunctionParameter %int
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("BuiltIns can only target variables, structure members "
+ "or constants"));
+}
+
+TEST_P(BuiltInDecorations, Constant) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %const BuiltIn )" +
+ deco + R"(
+%int = OpTypeInt 32 0
+%int3 = OpTypeVector %int 3
+%int_1 = OpConstant %int 1
+%const = OpConstantComposite %int3 %int_1 %int_1 %int_1
+)";
+
+ CompileSuccessfully(text);
+ if (deco == "WorkgroupSize") {
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+ } else {
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable"));
+ }
+}
+
+TEST_P(BuiltInDecorations, SpecConstant) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %const BuiltIn )" +
+ deco + R"(
+%int = OpTypeInt 32 0
+%int3 = OpTypeVector %int 3
+%int_1 = OpConstant %int 1
+%const = OpSpecConstantComposite %int3 %int_1 %int_1 %int_1
+)";
+
+ CompileSuccessfully(text);
+ if (deco == "WorkgroupSize") {
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+ } else {
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable"));
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidateBuiltInDecorations, BuiltInDecorations,
+ Values("Position", "PointSize", "VertexId",
+ "InstanceId", "FragCoord", "FrontFacing",
+ "NumWorkgroups", "WorkgroupSize",
+ "LocalInvocationId", "GlobalInvocationId"));
+
+using MemoryObjectDecorations = spvtest::ValidateBase<std::string>;
+
+TEST_P(MemoryObjectDecorations, Variable) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability SampleRateShading
+OpCapability TransformFeedback
+OpCapability GeometryStreams
+OpCapability Tessellation
+OpCapability PhysicalStorageBufferAddresses
+OpExtension "SPV_KHR_physical_storage_buffer"
+OpMemoryModel Logical GLSL450
+OpDecorate %var )" + deco + R"(
+%float = OpTypeFloat 32
+%ptr = OpTypePointer Input %float
+%var = OpVariable %ptr Input
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(MemoryObjectDecorations, FunctionParameterGood) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability SampleRateShading
+OpCapability TransformFeedback
+OpCapability GeometryStreams
+OpCapability Tessellation
+OpCapability PhysicalStorageBufferAddresses
+OpExtension "SPV_KHR_physical_storage_buffer"
+OpMemoryModel Logical GLSL450
+OpDecorate %func LinkageAttributes "import" Import
+OpDecorate %param )" + deco +
+ R"(
+%float = OpTypeFloat 32
+%ptr = OpTypePointer Input %float
+%void = OpTypeVoid
+%fn = OpTypeFunction %void %ptr
+%func = OpFunction %void None %fn
+%param = OpFunctionParameter %ptr
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(MemoryObjectDecorations, FunctionParameterNotAPointer) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability SampleRateShading
+OpCapability TransformFeedback
+OpCapability GeometryStreams
+OpCapability Tessellation
+OpCapability PhysicalStorageBufferAddresses
+OpExtension "SPV_KHR_physical_storage_buffer"
+OpMemoryModel Logical GLSL450
+OpDecorate %func LinkageAttributes "import" Import
+OpDecorate %param )" + deco +
+ R"(
+%float = OpTypeFloat 32
+%void = OpTypeVoid
+%fn = OpTypeFunction %void %float
+%func = OpFunction %void None %fn
+%param = OpFunctionParameter %float
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a pointer type"));
+}
+
+TEST_P(MemoryObjectDecorations, FloatType) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability SampleRateShading
+OpCapability TransformFeedback
+OpCapability GeometryStreams
+OpCapability Tessellation
+OpCapability PhysicalStorageBufferAddresses
+OpExtension "SPV_KHR_physical_storage_buffer"
+OpMemoryModel Logical GLSL450
+OpDecorate %float )" + deco +
+ R"(
+%float = OpTypeFloat 32
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("must be a memory object declaration"));
+}
+
+TEST_P(MemoryObjectDecorations, Constant) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability SampleRateShading
+OpCapability TransformFeedback
+OpCapability GeometryStreams
+OpCapability Tessellation
+OpCapability PhysicalStorageBufferAddresses
+OpExtension "SPV_KHR_physical_storage_buffer"
+OpMemoryModel Logical GLSL450
+OpDecorate %const )" + deco +
+ R"(
+%float = OpTypeFloat 32
+%const = OpConstant %float 0
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("must be a memory object declaration"));
+}
+
+// NonWritable and NonReadable are covered by other tests.
+INSTANTIATE_TEST_SUITE_P(
+ ValidateMemoryObjectDecorations, MemoryObjectDecorations,
+ Values("NoPerspective", "Flat", "Patch", "Centroid", "Component 0",
+ "Sample", "Restrict", "Aliased", "Volatile", "Coherent", "Stream 0",
+ "XfbBuffer 1", "XfbStride 1", "AliasedPointer", "RestrictPointer"));
+
+using VariableDecorations = spvtest::ValidateBase<std::string>;
+
+TEST_P(VariableDecorations, Variable) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Kernel
+OpCapability Linkage
+OpCapability InputAttachment
+OpMemoryModel Logical GLSL450
+OpDecorate %var )" + deco + R"(
+%float = OpTypeFloat 32
+%ptr = OpTypePointer Input %float
+%var = OpVariable %ptr Input
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(VariableDecorations, FunctionParameter) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Kernel
+OpCapability Linkage
+OpCapability InputAttachment
+OpMemoryModel Logical GLSL450
+OpDecorate %func LinkageAttributes "import" Import
+OpDecorate %param )" + deco +
+ R"(
+%float = OpTypeFloat 32
+%void = OpTypeVoid
+%fn = OpTypeFunction %void %float
+%func = OpFunction %void None %fn
+%param = OpFunctionParameter %float
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable"));
+}
+
+TEST_P(VariableDecorations, FloatType) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Kernel
+OpCapability Linkage
+OpCapability InputAttachment
+OpMemoryModel Logical GLSL450
+OpDecorate %float )" + deco +
+ R"(
+%float = OpTypeFloat 32
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable"));
+}
+
+TEST_P(VariableDecorations, Constant) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Kernel
+OpCapability Linkage
+OpCapability InputAttachment
+OpMemoryModel Logical GLSL450
+OpDecorate %const )" + deco +
+ R"(
+%float = OpTypeFloat 32
+%const = OpConstant %float 0
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable"));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidateVariableDecorations, VariableDecorations,
+ Values("Invariant", "Constant", "Location 0",
+ "Index 0", "Binding 0", "DescriptorSet 0"));
+
+using VulkanIOStorageClass =
+ spvtest::ValidateBase<std::tuple<std::string, std::string>>;
+
+TEST_P(VulkanIOStorageClass, Invalid) {
+ const auto deco = std::get<0>(GetParam());
+ const auto sc = std::get<1>(GetParam());
+ const std::string text = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var )" + deco + R"( 0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%ptr = OpTypePointer )" +
+ sc +
+ R"( %float
+%var = OpVariable %ptr )" + sc +
+ R"(
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("decoration must not be applied to this storage class"));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidateVulkanIOStorageClass, VulkanIOStorageClass,
+ Combine(Values("Location", "Component"),
+ Values("StorageBuffer", "Uniform",
+ "UniformConstant", "Workgroup",
+ "Private")));
+
+using VulkanResourceStorageClass =
+ spvtest::ValidateBase<std::tuple<std::string, std::string>>;
+
+TEST_P(VulkanResourceStorageClass, Invalid) {
+ const auto deco = std::get<0>(GetParam());
+ const auto sc = std::get<1>(GetParam());
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var )" + deco + R"( 0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%ptr = OpTypePointer )" +
+ sc +
+ R"( %float
+%var = OpVariable %ptr )" + sc +
+ R"(
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("must be in the StorageBuffer, Uniform, or "
+ "UniformConstant storage class"));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidateVulkanResourceStorageClass,
+ VulkanResourceStorageClass,
+ Combine(Values("DescriptorSet", "Binding"),
+ Values("Private", "Input", "Output",
+ "Workgroup")));
+
+using VulkanInterpolationStorageClass = spvtest::ValidateBase<std::string>;
+
+TEST_P(VulkanInterpolationStorageClass, Input) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability SampleRateShading
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var )" + deco + R"(
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%void_fn = OpTypeFunction %void
+%ptr = OpTypePointer Input %float
+%var = OpVariable %ptr Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_P(VulkanInterpolationStorageClass, Output) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability SampleRateShading
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main"
+OpDecorate %var )" + deco + R"(
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%void_fn = OpTypeFunction %void
+%ptr = OpTypePointer Output %float
+%var = OpVariable %ptr Output
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_P(VulkanInterpolationStorageClass, Private) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability SampleRateShading
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var )" + deco + R"(
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%void_fn = OpTypeFunction %void
+%ptr = OpTypePointer Private %float
+%var = OpVariable %ptr Private
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("storage class must be Input or Output"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("[VUID-StandaloneSpirv-Flat-04670"));
+}
+
+TEST_P(VulkanInterpolationStorageClass, Uniform) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability SampleRateShading
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var )" + deco + R"(
+OpDecorate %var Binding 0
+OpDecorate %var DescriptorSet 0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%void_fn = OpTypeFunction %void
+%ptr = OpTypePointer Uniform %float
+%var = OpVariable %ptr Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("storage class must be Input or Output"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("[VUID-StandaloneSpirv-Flat-04670"));
+}
+
+TEST_P(VulkanInterpolationStorageClass, StorageBuffer) {
+ const auto deco = GetParam();
+ const std::string text = R"(
+OpCapability Shader
+OpCapability SampleRateShading
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var )" + deco + R"(
+OpDecorate %var Binding 0
+OpDecorate %var DescriptorSet 0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%void_fn = OpTypeFunction %void
+%ptr = OpTypePointer StorageBuffer %float
+%var = OpVariable %ptr StorageBuffer
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("storage class must be Input or Output"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("[VUID-StandaloneSpirv-Flat-04670"));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidateVulkanInterpolationStorageClass,
+ VulkanInterpolationStorageClass,
+ Values("Flat", "NoPerspective", "Centroid", "Sample"));
+
+} // namespace
+} // namespace val
+} // namespace spvtools
diff --git a/test/val/val_arithmetics_test.cpp b/test/val/val_arithmetics_test.cpp
index b82fc97e..4c093e9f 100644
--- a/test/val/val_arithmetics_test.cpp
+++ b/test/val/val_arithmetics_test.cpp
@@ -656,7 +656,7 @@ TEST_F(ValidateArithmetics, DotDifferentVectorSize) {
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
- "Expected operands to have the same number of componenets: Dot"));
+ "Expected operands to have the same number of components: Dot"));
}
TEST_F(ValidateArithmetics, VectorTimesScalarSuccess) {
@@ -1309,6 +1309,57 @@ TEST_F(ValidateArithmetics, CoopMatDimFail) {
HasSubstr("Cooperative matrix 'M' mismatch: CooperativeMatrixMulAddNV"));
}
+TEST_F(ValidateArithmetics, CoopMatComponentTypeNotScalarNumeric) {
+ const std::string types = R"(
+%bad = OpTypeCooperativeMatrixNV %bool %subgroup %u32_8 %u32_8
+)";
+
+ CompileSuccessfully(GenerateCoopMatCode(types, "").c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("OpTypeCooperativeMatrixNV Component Type <id> "
+ "'4[%bool]' is not a scalar numerical type."));
+}
+
+TEST_F(ValidateArithmetics, CoopMatScopeNotConstantInt) {
+ const std::string types = R"(
+%bad = OpTypeCooperativeMatrixNV %f16 %f32_1 %u32_8 %u32_8
+)";
+
+ CompileSuccessfully(GenerateCoopMatCode(types, "").c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("OpTypeCooperativeMatrixNV Scope <id> '17[%float_1]' is not a "
+ "constant instruction with scalar integer type."));
+}
+
+TEST_F(ValidateArithmetics, CoopMatRowsNotConstantInt) {
+ const std::string types = R"(
+%bad = OpTypeCooperativeMatrixNV %f16 %subgroup %f32_1 %u32_8
+)";
+
+ CompileSuccessfully(GenerateCoopMatCode(types, "").c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("OpTypeCooperativeMatrixNV Rows <id> '17[%float_1]' is not a "
+ "constant instruction with scalar integer type."));
+}
+
+TEST_F(ValidateArithmetics, CoopMatColumnsNotConstantInt) {
+ const std::string types = R"(
+%bad = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_8 %f32_1
+)";
+
+ CompileSuccessfully(GenerateCoopMatCode(types, "").c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("OpTypeCooperativeMatrixNV Cols <id> '17[%float_1]' is not a "
+ "constant instruction with scalar integer type."));
+}
+
TEST_F(ValidateArithmetics, IAddCarrySuccess) {
const std::string body = R"(
%val1 = OpIAddCarry %struct_u32_u32 %u32_0 %u32_1
diff --git a/test/val/val_atomics_test.cpp b/test/val/val_atomics_test.cpp
index fc3aedbe..a0308d59 100644
--- a/test/val/val_atomics_test.cpp
+++ b/test/val/val_atomics_test.cpp
@@ -280,7 +280,7 @@ TEST_F(ValidateAtomics, AtomicLoadVulkanWrongStorageClass) {
AnyVUID("VUID-StandaloneSpirv-None-04645"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("in Vulkan evironment, Workgroup Storage Class is limited to "
+ HasSubstr("in Vulkan environment, Workgroup Storage Class is limited to "
"MeshNV, TaskNV, and GLCompute execution model"));
}
@@ -318,7 +318,7 @@ TEST_F(ValidateAtomics, AtomicAddFloatVulkan) {
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Opcode AtomicFAddEXT requires one of these capabilities: "
- "AtomicFloat32AddEXT AtomicFloat64AddEXT"));
+ "AtomicFloat32AddEXT AtomicFloat64AddEXT AtomicFloat16AddEXT"));
}
TEST_F(ValidateAtomics, AtomicMinFloatVulkan) {
@@ -539,6 +539,27 @@ OpExtension "SPV_EXT_shader_atomic_float_min_max"
"require the AtomicFloat32MinMaxEXT capability"));
}
+TEST_F(ValidateAtomics, AtomicAddFloat16VulkanSuccess) {
+ const std::string defs = R"(
+%f16 = OpTypeFloat 16
+%f16_1 = OpConstant %f16 1
+%f16_ptr = OpTypePointer Workgroup %f16
+%f16_var = OpVariable %f16_ptr Workgroup
+)";
+ const std::string body = R"(
+%val1 = OpAtomicFAddEXT %f16 %f16_var %device %relaxed %f16_1
+)";
+ const std::string extra = R"(
+OpCapability Float16
+OpCapability AtomicFloat16AddEXT
+OpExtension "SPV_EXT_shader_atomic_float16_add"
+)";
+
+ CompileSuccessfully(GenerateShaderComputeCode(body, extra, defs),
+ SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
TEST_F(ValidateAtomics, AtomicAddFloatVulkanSuccess) {
const std::string body = R"(
%val1 = OpAtomicFAddEXT %f32 %f32_var %device %relaxed %f32_1
@@ -687,7 +708,7 @@ OpAtomicStore %f32_var %device %relaxed %f32_1
AnyVUID("VUID-StandaloneSpirv-None-04645"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("in Vulkan evironment, Workgroup Storage Class is limited to "
+ HasSubstr("in Vulkan environment, Workgroup Storage Class is limited to "
"MeshNV, TaskNV, and GLCompute execution model"));
}
@@ -2658,6 +2679,39 @@ OpFunctionEnd
"CooperativeMatrixNV capability is present"));
}
+TEST_F(ValidateAtomics, IIncrementBadPointerDataType) {
+ const std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+ %v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+ %void = OpTypeVoid
+ %16 = OpTypeFunction %void
+%uint_538976288 = OpConstant %uint 538976288
+ %int = OpTypeInt 32 1
+%_runtimearr_int = OpTypeRuntimeArray %int
+ %_struct_5 = OpTypeStruct %_runtimearr_int
+%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
+ %3 = OpVariable %_ptr_Input_v3uint Input
+ %7 = OpVariable %_ptr_Uniform__struct_5 Uniform
+ %8224 = OpFunction %void None %16
+ %65312 = OpLabel
+ %25 = OpAccessChain %_ptr_Input_uint %3 %uint_538976288
+ %26 = OpLoad %uint %25
+ %2097184 = OpAtomicIIncrement %int %7 %uint_538976288 %26
+ OpUnreachable
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("AtomicIIncrement: expected Pointer to point to a "
+ "value of type Result Type"));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp
index 2116fff2..d749c5a8 100644
--- a/test/val/val_builtins_test.cpp
+++ b/test/val/val_builtins_test.cpp
@@ -93,7 +93,8 @@ CodeGenerator GetInMainCodeGenerator(const char* const built_in,
generator.extensions_ += extensions;
}
- generator.before_types_ = "OpMemberDecorate %built_in_type 0 BuiltIn ";
+ generator.before_types_ = R"(OpDecorate %built_in_type Block
+ OpMemberDecorate %built_in_type 0 BuiltIn )";
generator.before_types_ += built_in;
generator.before_types_ += "\n";
@@ -251,7 +252,8 @@ CodeGenerator GetInFunctionCodeGenerator(const char* const built_in,
generator.extensions_ += extensions;
}
- generator.before_types_ = "OpMemberDecorate %built_in_type 0 BuiltIn ";
+ generator.before_types_ = R"(OpDecorate %built_in_type Block
+ OpMemberDecorate %built_in_type 0 BuiltIn )";
generator.before_types_ += built_in;
generator.before_types_ += "\n";
@@ -767,18 +769,18 @@ INSTANTIATE_TEST_SUITE_P(
INSTANTIATE_TEST_SUITE_P(
ComputeShaderInputInt32Vec3NotGLCompute,
ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
- Combine(
- Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups",
- "WorkgroupId"),
- Values("Vertex", "Fragment", "Geometry", "TessellationControl",
- "TessellationEvaluation"),
- Values("Input"), Values("%u32vec3"),
- Values("VUID-GlobalInvocationId-GlobalInvocationId-04236 "
- "VUID-LocalInvocationId-LocalInvocationId-04281 "
- "VUID-NumWorkgroups-NumWorkgroups-04296 "
- "VUID-WorkgroupId-WorkgroupId-04422"),
- Values(TestResult(SPV_ERROR_INVALID_DATA,
- "to be used only with GLCompute execution model"))));
+ Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups",
+ "WorkgroupId"),
+ Values("Vertex", "Fragment", "Geometry", "TessellationControl",
+ "TessellationEvaluation"),
+ Values("Input"), Values("%u32vec3"),
+ Values("VUID-GlobalInvocationId-GlobalInvocationId-04236 "
+ "VUID-LocalInvocationId-LocalInvocationId-04281 "
+ "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"))));
INSTANTIATE_TEST_SUITE_P(
ComputeShaderInputInt32Vec3NotInput,
@@ -2828,9 +2830,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 execution model"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Vulkan spec allows BuiltIn WorkgroupSize to be used "
+ "only with GLCompute, MeshNV, or TaskNV execution model"));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("is referencing ID <2> (OpConstantComposite) which is "
"decorated with BuiltIn WorkgroupSize in function <1> "
@@ -2860,9 +2863,9 @@ OpDecorate %copy BuiltIn WorkgroupSize
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("BuiltIns can only target variables, structs or constants"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("BuiltIns can only target variables, structure "
+ "members or constants"));
}
CodeGenerator GetWorkgroupSizeNotVectorGenerator() {
@@ -3097,6 +3100,8 @@ TEST_F(ValidateBuiltIns, TwoBuiltInsFirstFails) {
CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
generator.before_types_ = R"(
+OpDecorate %input_type Block
+OpDecorate %output_type Block
OpMemberDecorate %input_type 0 BuiltIn FragCoord
OpMemberDecorate %output_type 0 BuiltIn Position
)";
@@ -3137,6 +3142,8 @@ TEST_F(ValidateBuiltIns, TwoBuiltInsSecondFails) {
CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
generator.before_types_ = R"(
+OpDecorate %input_type Block
+OpDecorate %output_type Block
OpMemberDecorate %input_type 0 BuiltIn Position
OpMemberDecorate %output_type 0 BuiltIn FragCoord
)";
@@ -3200,6 +3207,7 @@ OpStore %position %f32vec4_0123
TEST_F(ValidateBuiltIns, FragmentPositionTwoEntryPoints) {
CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
generator.before_types_ = R"(
+OpDecorate %output_type Block
OpMemberDecorate %output_type 0 BuiltIn Position
)";
@@ -3251,6 +3259,7 @@ CodeGenerator GetNoDepthReplacingGenerator() {
CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
generator.before_types_ = R"(
+OpDecorate %output_type Block
OpMemberDecorate %output_type 0 BuiltIn FragDepth
)";
@@ -3302,6 +3311,7 @@ CodeGenerator GetOneMainHasDepthReplacingOtherHasntGenerator() {
CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
generator.before_types_ = R"(
+OpDecorate %output_type Block
OpMemberDecorate %output_type 0 BuiltIn FragDepth
)";
@@ -3373,6 +3383,7 @@ OpExtension "SPV_NV_ray_tracing"
)";
generator.before_types_ = R"(
+OpDecorate %input_type Block
OpMemberDecorate %input_type 0 BuiltIn InstanceId
)";
@@ -3481,35 +3492,6 @@ OpDecorate %gl_ViewportIndex PerPrimitiveNV
EXPECT_THAT(getDiagnosticString(), HasSubstr("is not an int scalar"));
}
-TEST_F(ValidateBuiltIns, GetUnderlyingTypeNoAssert) {
- std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %4 "PSMa" %12 %17
- OpExecutionMode %4 OriginUpperLeft
- OpDecorate %gl_PointCoord BuiltIn PointCoord
- OpDecorate %12 Location 0
- OpDecorate %17 Location 0
- %void = OpTypeVoid
- %3 = OpTypeFunction %void
- %float = OpTypeFloat 32
- %v4float = OpTypeVector %float 4
- %gl_PointCoord = OpTypeStruct %v4float
- %_ptr_Input_v4float = OpTypePointer Input %v4float
- %_ptr_Output_v4float = OpTypePointer Output %v4float
- %12 = OpVariable %_ptr_Input_v4float Input
- %17 = OpVariable %_ptr_Output_v4float Output
- %4 = OpFunction %void None %3
- %15 = OpLabel
- OpReturn
- OpFunctionEnd)";
- CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("did not find an member index to get underlying data "
- "type"));
-}
-
TEST_P(ValidateVulkanSubgroupBuiltIns, InMain) {
const char* const built_in = std::get<0>(GetParam());
const char* const execution_model = std::get<1>(GetParam());
@@ -3637,6 +3619,7 @@ OpCapability GroupNonUniformBallot
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %foo "foo"
OpExecutionMode %foo LocalSize 1 1 1
+OpDecorate %struct Block
OpMemberDecorate %struct 0 BuiltIn SubgroupEqMask
%void = OpTypeVoid
%int = OpTypeInt 32 0
@@ -3691,6 +3674,7 @@ OpCapability GroupNonUniform
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %foo "foo"
OpExecutionMode %foo LocalSize 1 1 1
+OpDecorate %struct Block
OpMemberDecorate %struct 0 BuiltIn SubgroupSize
%void = OpTypeVoid
%int = OpTypeInt 32 0
@@ -3711,13 +3695,13 @@ OpFunctionEnd
INSTANTIATE_TEST_SUITE_P(
SubgroupNumAndIdNotCompute, ValidateVulkanSubgroupBuiltIns,
- Combine(
- Values("SubgroupId", "NumSubgroups"), Values("Vertex"), Values("Input"),
- Values("%u32"),
- Values("VUID-SubgroupId-SubgroupId-04367 "
- "VUID-NumSubgroups-NumSubgroups-04293"),
- Values(TestResult(SPV_ERROR_INVALID_DATA,
- "to be used only with GLCompute execution model"))));
+ Combine(Values("SubgroupId", "NumSubgroups"), Values("Vertex"),
+ Values("Input"), Values("%u32"),
+ 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"))));
INSTANTIATE_TEST_SUITE_P(
SubgroupNumAndIdNotU32, ValidateVulkanSubgroupBuiltIns,
@@ -3751,6 +3735,7 @@ OpCapability GroupNonUniform
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %foo "foo"
OpExecutionMode %foo LocalSize 1 1 1
+OpDecorate %struct Block
OpMemberDecorate %struct 0 BuiltIn SubgroupId
%void = OpTypeVoid
%int = OpTypeInt 32 0
@@ -3780,9 +3765,9 @@ OpDecorate %void BuiltIn Position
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("BuiltIns can only target variables, structs or constants"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("BuiltIns can only target variables, structure members "
+ "or constants"));
}
TEST_F(ValidateBuiltIns, TargetIsVariable) {
@@ -3800,47 +3785,6 @@ OpDecorate %wg_var BuiltIn Position
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
-TEST_F(ValidateBuiltIns, TargetIsStruct) {
- const std::string text = R"(
-OpCapability Shader
-OpCapability Linkage
-OpMemoryModel Logical GLSL450
-OpDecorate %struct BuiltIn Position
-%struct = OpTypeStruct
-)";
-
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateBuiltIns, TargetIsConstant) {
- const std::string text = R"(
-OpCapability Shader
-OpCapability Linkage
-OpMemoryModel Logical GLSL450
-OpDecorate %int0 BuiltIn Position
-%int = OpTypeInt 32 0
-%int0 = OpConstant %int 0
-)";
-
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateBuiltIns, TargetIsSpecConstant) {
- const std::string text = R"(
-OpCapability Shader
-OpCapability Linkage
-OpMemoryModel Logical GLSL450
-OpDecorate %int0 BuiltIn Position
-%int = OpTypeInt 32 0
-%int0 = OpSpecConstant %int 0
-)";
-
- CompileSuccessfully(text);
- EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
INSTANTIATE_TEST_SUITE_P(
PrimitiveShadingRateOutputSuccess,
ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
diff --git a/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp
index 82f8d381..c432c3cf 100644
--- a/test/val/val_capability_test.cpp
+++ b/test/val/val_capability_test.cpp
@@ -1205,8 +1205,10 @@ INSTANTIATE_TEST_SUITE_P(Decoration, ValidateCapability,
Values(
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt RelaxedPrecision\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var RelaxedPrecision\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Private %intt\n"
+ "%var = OpVariable %ptr Private\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
// Block applies to struct type.
@@ -1224,93 +1226,125 @@ std::make_pair(std::string(kOpenCLMemoryModel) +
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt RowMajor\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpMemberDecorate %structt 0 RowMajor\n"
+ "%floatt = OpTypeFloat 32\n"
+ "%float2 = OpTypeVector %floatt 2\n"
+ "%mat2x2 = OpTypeMatrix %float2 2\n"
+ "%structt = OpTypeStruct %mat2x2\n" + std::string(kVoidFVoid),
MatrixDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt ColMajor\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpMemberDecorate %structt 0 ColMajor\n"
+ "%floatt = OpTypeFloat 32\n"
+ "%float2 = OpTypeVector %floatt 2\n"
+ "%mat2x2 = OpTypeMatrix %float2 2\n"
+ "%structt = OpTypeStruct %mat2x2\n" + std::string(kVoidFVoid),
MatrixDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt ArrayStride 1\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %array ArrayStride 4\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%array = OpTypeRuntimeArray %intt\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt MatrixStride 1\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpMemberDecorate %structt 0 MatrixStride 8\n"
+ "%floatt = OpTypeFloat 32\n"
+ "%float2 = OpTypeVector %floatt 2\n"
+ "%mat2x2 = OpTypeMatrix %float2 2\n"
+ "%structt = OpTypeStruct %mat2x2\n" + std::string(kVoidFVoid),
MatrixDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt GLSLShared\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %struct GLSLShared\n"
+ "%struct = OpTypeStruct\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt GLSLPacked\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %struct GLSLPacked\n"
+ "%struct = OpTypeStruct\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n"
- "OpDecorate %intt CPacked\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %struct CPacked\n"
+ "%struct = OpTypeStruct\n" + std::string(kVoidFVoid),
KernelDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt NoPerspective\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var NoPerspective\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt Flat\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var Flat\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt Patch\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var Patch\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
TessellationDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt Centroid\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var Centroid\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt Sample\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var Sample\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
std::vector<std::string>{"SampleRateShading"}),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt Invariant\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var Invariant\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt Restrict\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var Restrict\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
AllCapabilities()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt Aliased\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var Aliased\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
AllCapabilities()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt Volatile\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var Volatile\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
AllCapabilities()),
std::make_pair(std::string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n"
- "OpDecorate %intt Constant\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var Constant\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
KernelDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt Coherent\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var Coherent\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
AllCapabilities()),
std::make_pair(std::string(kOpenCLMemoryModel) +
// NonWritable must target something valid, such as a storage image.
@@ -1324,8 +1358,12 @@ std::make_pair(std::string(kOpenCLMemoryModel) +
AllCapabilities()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt NonReadable\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var NonReadable "
+ "%float = OpTypeFloat 32 "
+ "%imstor = OpTypeImage %float 2D 0 0 0 2 Unknown "
+ "%ptr = OpTypePointer UniformConstant %imstor "
+ "%var = OpVariable %ptr UniformConstant "
+ + std::string(kVoidFVoid),
AllCapabilities()),
std::make_pair(std::string(kOpenCLMemoryModel) +
// Uniform must target a non-void value.
@@ -1342,8 +1380,10 @@ std::make_pair(std::string(kGLSL450MemoryModel) +
KernelDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt Stream 0\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var Stream 0\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Output %intt\n"
+ "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid),
std::vector<std::string>{"GeometryStreams"}),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
@@ -1360,33 +1400,44 @@ std::make_pair(std::string(kOpenCLMemoryModel) +
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt Index 0\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var Index 0\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt Binding 0\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var Binding 0\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Uniform %intt\n"
+ "%var = OpVariable %ptr Uniform\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt DescriptorSet 0\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var DescriptorSet 0\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Uniform %intt\n"
+ "%var = OpVariable %ptr Uniform\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt Offset 0\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpMemberDecorate %structt 0 Offset 0\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%structt = OpTypeStruct %intt\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt XfbBuffer 0\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var XfbBuffer 0\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Uniform %intt\n"
+ "%var = OpVariable %ptr Uniform\n" + std::string(kVoidFVoid),
std::vector<std::string>{"TransformFeedback"}),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt XfbStride 0\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var XfbStride 0\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer Uniform %intt\n"
+ "%var = OpVariable %ptr Uniform\n" + std::string(kVoidFVoid),
std::vector<std::string>{"TransformFeedback"}),
std::make_pair(std::string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n"
@@ -1410,8 +1461,10 @@ std::make_pair(std::string(kOpenCLMemoryModel) +
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt InputAttachmentIndex 0\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpDecorate %var InputAttachmentIndex 0\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%ptr = OpTypePointer UniformConstant %intt\n"
+ "%var = OpVariable %ptr UniformConstant\n" + std::string(kVoidFVoid),
std::vector<std::string>{"InputAttachment"}),
std::make_pair(std::string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n"
@@ -1471,264 +1524,300 @@ INSTANTIATE_TEST_SUITE_P(BuiltIn, ValidateCapability,
Values(
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn Position\n"
+ "OpDecorate %var BuiltIn Position\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
// Just mentioning PointSize, ClipDistance, or CullDistance as a BuiltIn does
// not trigger the requirement for the associated capability.
// See https://github.com/KhronosGroup/SPIRV-Tools/issues/365
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn PointSize\n"
+ "OpDecorate %var BuiltIn PointSize\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
AllCapabilities()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn ClipDistance\n"
+ "OpDecorate %var BuiltIn ClipDistance\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
AllCapabilities()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn CullDistance\n"
+ "OpDecorate %var BuiltIn CullDistance\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
AllCapabilities()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn VertexId\n"
+ "OpDecorate %var BuiltIn VertexId\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn InstanceId\n"
+ "OpDecorate %var BuiltIn InstanceId\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn PrimitiveId\n"
+ "OpDecorate %var BuiltIn PrimitiveId\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
GeometryTessellationDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn InvocationId\n"
+ "OpDecorate %var BuiltIn InvocationId\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
GeometryTessellationDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn Layer\n"
+ "OpDecorate %var BuiltIn Layer\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
GeometryDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn ViewportIndex\n"
+ "OpDecorate %var BuiltIn ViewportIndex\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
std::vector<std::string>{"MultiViewport"}),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn TessLevelOuter\n"
+ "OpDecorate %var BuiltIn TessLevelOuter\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
TessellationDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn TessLevelInner\n"
+ "OpDecorate %var BuiltIn TessLevelInner\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
TessellationDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn TessCoord\n"
+ "OpDecorate %var BuiltIn TessCoord\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
TessellationDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn PatchVertices\n"
+ "OpDecorate %var BuiltIn PatchVertices\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
TessellationDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn FragCoord\n"
+ "OpDecorate %var BuiltIn FragCoord\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn PointCoord\n"
+ "OpDecorate %var BuiltIn PointCoord\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn FrontFacing\n"
+ "OpDecorate %var BuiltIn FrontFacing\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn SampleId\n"
+ "OpDecorate %var BuiltIn SampleId\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
std::vector<std::string>{"SampleRateShading"}),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn SamplePosition\n"
+ "OpDecorate %var BuiltIn SamplePosition\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
std::vector<std::string>{"SampleRateShading"}),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn SampleMask\n"
+ "OpDecorate %var BuiltIn SampleMask\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn FragDepth\n"
+ "OpDecorate %var BuiltIn FragDepth\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn HelperInvocation\n"
+ "OpDecorate %var BuiltIn HelperInvocation\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn VertexIndex\n"
+ "OpDecorate %var BuiltIn VertexIndex\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn InstanceIndex\n"
+ "OpDecorate %var BuiltIn InstanceIndex\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn NumWorkgroups\n"
+ "OpDecorate %var BuiltIn NumWorkgroups\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
- AllCapabilities()),
-std::make_pair(std::string(kOpenCLMemoryModel) +
- "OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn WorkgroupSize\n"
- "%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
AllCapabilities()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn WorkgroupId\n"
+ "OpDecorate %var BuiltIn WorkgroupId\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
AllCapabilities()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn LocalInvocationId\n"
+ "OpDecorate %var BuiltIn LocalInvocationId\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
AllCapabilities()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn GlobalInvocationId\n"
+ "OpDecorate %var BuiltIn GlobalInvocationId\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
AllCapabilities()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn LocalInvocationIndex\n"
+ "OpDecorate %var BuiltIn LocalInvocationIndex\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
AllCapabilities()),
std::make_pair(std::string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn WorkDim\n"
+ "OpDecorate %var BuiltIn WorkDim\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
KernelDependencies()),
std::make_pair(std::string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn GlobalSize\n"
+ "OpDecorate %var BuiltIn GlobalSize\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
KernelDependencies()),
std::make_pair(std::string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn EnqueuedWorkgroupSize\n"
+ "OpDecorate %var BuiltIn EnqueuedWorkgroupSize\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
KernelDependencies()),
std::make_pair(std::string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn GlobalOffset\n"
+ "OpDecorate %var BuiltIn GlobalOffset\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
KernelDependencies()),
std::make_pair(std::string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn GlobalLinearId\n"
+ "OpDecorate %var BuiltIn GlobalLinearId\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
KernelDependencies()),
std::make_pair(std::string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn SubgroupSize\n"
+ "OpDecorate %var BuiltIn SubgroupSize\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
KernelAndGroupNonUniformDependencies()),
std::make_pair(std::string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn SubgroupMaxSize\n"
+ "OpDecorate %var BuiltIn SubgroupMaxSize\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
KernelDependencies()),
std::make_pair(std::string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn NumSubgroups\n"
+ "OpDecorate %var BuiltIn NumSubgroups\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
KernelAndGroupNonUniformDependencies()),
std::make_pair(std::string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn NumEnqueuedSubgroups\n"
+ "OpDecorate %var BuiltIn NumEnqueuedSubgroups\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
KernelDependencies()),
std::make_pair(std::string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn SubgroupId\n"
+ "OpDecorate %var BuiltIn SubgroupId\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
KernelAndGroupNonUniformDependencies()),
std::make_pair(std::string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn SubgroupLocalInvocationId\n"
+ "OpDecorate %var BuiltIn SubgroupLocalInvocationId\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
KernelAndGroupNonUniformDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn VertexIndex\n"
+ "OpDecorate %var BuiltIn VertexIndex\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n" +
- "OpDecorate %int0 BuiltIn InstanceIndex\n"
+ "OpDecorate %var BuiltIn InstanceIndex\n"
"%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "%ptr = OpTypePointer Input %intt\n"
+ "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies())
)));
@@ -1742,11 +1831,11 @@ INSTANTIATE_TEST_SUITE_P(BuiltIn, ValidateCapabilityVulkan10,
ValuesIn(AllSpirV10Capabilities()),
Values(
std::make_pair(std::string(kGLSL450MemoryModel) +
- "OpEntryPoint Vertex %func \"shader\" \n"
- "OpMemberDecorate %block 0 BuiltIn PointSize\n"
- "%f32 = OpTypeFloat 32\n"
- "%block = OpTypeStruct %f32\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpEntryPoint Vertex %func \"shader\" %var\n" +
+ "OpDecorate %var BuiltIn PointSize\n"
+ "%float = OpTypeFloat 32\n"
+ "%ptr_output_float = OpTypePointer Output %float\n"
+ "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid),
// Capabilities which should succeed.
AllVulkan10Capabilities()),
std::make_pair(std::string(kGLSL450MemoryModel) +
@@ -1775,22 +1864,31 @@ INSTANTIATE_TEST_SUITE_P(BuiltIn, ValidateCapabilityOpenGL40,
ValuesIn(AllSpirV10Capabilities()),
Values(
std::make_pair(std::string(kGLSL450MemoryModel) +
- "OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn PointSize\n"
- "%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "OpEntryPoint Vertex %func \"shader\" %var\n" +
+ "OpDecorate %var BuiltIn PointSize\n"
+ "%float = OpTypeFloat 32\n"
+ "%ptr_output_float = OpTypePointer Output %float\n"
+ "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid),
AllSpirV10Capabilities()),
std::make_pair(std::string(kGLSL450MemoryModel) +
- "OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn ClipDistance\n"
- "%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "OpEntryPoint Vertex %func \"shader\" %var\n" +
+ "OpDecorate %var BuiltIn ClipDistance\n"
+ "%float = OpTypeFloat 32\n"
+ "%int = OpTypeInt 32 0\n"
+ "%int_1 = OpConstant %int 1\n"
+ "%array = OpTypeArray %float %int_1\n"
+ "%ptr = OpTypePointer Output %array\n"
+ "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid),
AllSpirV10Capabilities()),
std::make_pair(std::string(kGLSL450MemoryModel) +
- "OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn CullDistance\n"
- "%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "OpEntryPoint Vertex %func \"shader\" %var\n" +
+ "OpDecorate %var BuiltIn CullDistance\n"
+ "%float = OpTypeFloat 32\n"
+ "%int = OpTypeInt 32 0\n"
+ "%int_1 = OpConstant %int 1\n"
+ "%array = OpTypeArray %float %int_1\n"
+ "%ptr = OpTypePointer Output %array\n"
+ "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid),
AllSpirV10Capabilities())
)));
@@ -1800,16 +1898,21 @@ INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan11,
ValuesIn(AllCapabilities()),
Values(
std::make_pair(std::string(kGLSL450MemoryModel) +
- "OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn PointSize\n"
- "%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "OpEntryPoint Vertex %func \"shader\" %var\n" +
+ "OpDecorate %var BuiltIn PointSize\n"
+ "%float = OpTypeFloat 32\n"
+ "%ptr_output_float = OpTypePointer Output %float\n"
+ "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid),
AllVulkan11Capabilities()),
std::make_pair(std::string(kGLSL450MemoryModel) +
- "OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn CullDistance\n"
- "%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "OpEntryPoint Vertex %func \"shader\" %var\n" +
+ "OpDecorate %var BuiltIn CullDistance\n"
+ "%float = OpTypeFloat 32\n"
+ "%int = OpTypeInt 32 0\n"
+ "%int_1 = OpConstant %int 1\n"
+ "%array = OpTypeArray %float %int_1\n"
+ "%ptr = OpTypePointer Output %array\n"
+ "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid),
AllVulkan11Capabilities())
)));
@@ -1819,16 +1922,21 @@ INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan12,
ValuesIn(AllSpirV15Capabilities()),
Values(
std::make_pair(std::string(kGLSL450MemoryModel) +
- "OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn PointSize\n"
- "%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "OpEntryPoint Vertex %func \"shader\" %var\n" +
+ "OpDecorate %var BuiltIn PointSize\n"
+ "%float = OpTypeFloat 32\n"
+ "%ptr_output_float = OpTypePointer Output %float\n"
+ "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid),
AllVulkan12Capabilities()),
std::make_pair(std::string(kGLSL450MemoryModel) +
- "OpEntryPoint Vertex %func \"shader\" \n" +
- "OpDecorate %int0 BuiltIn CullDistance\n"
- "%intt = OpTypeInt 32 0\n"
- "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid),
+ "OpEntryPoint Vertex %func \"shader\" %var\n" +
+ "OpDecorate %var BuiltIn CullDistance\n"
+ "%float = OpTypeFloat 32\n"
+ "%int = OpTypeInt 32 0\n"
+ "%int_1 = OpConstant %int 1\n"
+ "%array = OpTypeArray %float %int_1\n"
+ "%ptr = OpTypePointer Output %array\n"
+ "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid),
AllVulkan12Capabilities())
)));
diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp
index 6ae2ee6d..76477468 100644
--- a/test/val/val_cfg_test.cpp
+++ b/test/val/val_cfg_test.cpp
@@ -65,7 +65,7 @@ class Block {
/// Creates a Block with a given label
///
/// @param[in]: label the label id of the block
- /// @param[in]: type the branch instruciton that ends the block
+ /// @param[in]: type the branch instruction that ends the block
explicit Block(std::string label, SpvOp type = SpvOpBranch)
: label_(label), body_(), type_(type), successors_() {}
@@ -3523,7 +3523,7 @@ OpFunctionEnd
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
- "OpSwitch must be preceeded by an OpSelectionMerge instruction"));
+ "OpSwitch must be preceded by an OpSelectionMerge instruction"));
}
TEST_F(ValidateCFG, MissingMergeSwitchBad2) {
@@ -3550,7 +3550,7 @@ OpFunctionEnd
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
- "OpSwitch must be preceeded by an OpSelectionMerge instruction"));
+ "OpSwitch must be preceded by an OpSelectionMerge instruction"));
}
TEST_F(ValidateCFG, MissingMergeOneBranchToMergeGood) {
@@ -3622,7 +3622,7 @@ OpFunctionEnd
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
- "OpSwitch must be preceeded by an OpSelectionMerge instruction"));
+ "OpSwitch must be preceded by an OpSelectionMerge instruction"));
}
TEST_F(ValidateCFG, MissingMergeOneUnseenTargetSwitchBad) {
@@ -3654,7 +3654,7 @@ OpFunctionEnd
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
- "OpSwitch must be preceeded by an OpSelectionMerge instruction"));
+ "OpSwitch must be preceded by an OpSelectionMerge instruction"));
}
TEST_F(ValidateCFG, MissingMergeLoopBreakGood) {
@@ -4229,6 +4229,67 @@ OpFunctionEnd
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
+TEST_F(ValidateCFG, StructuredSelections_RegisterBothTrueAndFalse) {
+ // In this test, we try to make a case where the false branches
+ // to %20 and %60 from blocks %10 and %50 must be registered
+ // during the validity check for sturctured selections.
+ // However, an error is caught earlier in the flow, that the
+ // branches from %100 to %20 and %60 violate dominance.
+ const std::string text = R"(
+ OpCapability Shader
+ OpMemoryModel Logical Simple
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+
+ %void = OpTypeVoid
+ %void_fn = OpTypeFunction %void
+
+ %bool = OpTypeBool
+ %cond = OpUndef %bool
+
+ %main = OpFunction %void None %void_fn
+
+ %1 = OpLabel
+ OpSelectionMerge %999 None
+ OpBranchConditional %cond %10 %100
+
+ %10 = OpLabel
+ OpSelectionMerge %30 None ; force registration of %30
+ OpBranchConditional %cond %30 %20 ; %20 should be registered too
+
+ %20 = OpLabel
+ OpBranch %30
+
+ %30 = OpLabel ; merge for first if
+ OpBranch %50
+
+
+ %50 = OpLabel
+ OpSelectionMerge %70 None ; force registration of %70
+ OpBranchConditional %cond %70 %60 ; %60 should be registered
+
+ %60 = OpLabel
+ OpBranch %70
+
+ %70 = OpLabel ; merge for second if
+ OpBranch %999
+
+ %100 = OpLabel
+ OpBranchConditional %cond %20 %60 ; should require a merge
+
+ %999 = OpLabel
+ OpReturn
+
+ OpFunctionEnd
+)";
+
+ 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"));
+}
+
TEST_F(ValidateCFG, UnreachableIsStaticallyReachable) {
const std::string text = R"(
OpCapability Shader
@@ -4399,6 +4460,170 @@ OpFunctionEnd
"1[%BAD], but not via a structured exit"));
}
+TEST_F(ValidateCFG, SwitchSelectorNotAnInt) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%float_1 = OpConstant %float 1
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSelectionMerge %default None
+OpSwitch %float_1 %default
+%default = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Selector type must be OpTypeInt"));
+}
+
+TEST_F(ValidateCFG, SwitchDefaultNotALabel) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSelectionMerge %default None
+OpSwitch %int_1 %int_1
+%default = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Default must be an OpLabel instruction"));
+}
+
+TEST_F(ValidateCFG, BlockDepthRecursion) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%1 = OpLabel
+OpBranch %2
+%2 = OpLabel
+OpLoopMerge %3 %4 None
+OpBranchConditional %undef %3 %4
+%4 = OpLabel
+OpBranch %2
+%3 = OpLabel
+OpBranch %5
+%5 = OpLabel
+OpSelectionMerge %2 None
+OpBranchConditional %undef %6 %7
+%6 = OpLabel
+OpReturn
+%7 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, BadStructuredExitBackwardsMerge) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%1 = OpLabel
+OpBranch %2
+%2 = OpLabel
+OpLoopMerge %4 %5 None
+OpBranchConditional %undef %4 %6
+%6 = OpLabel
+OpSelectionMerge %7 None
+OpBranchConditional %undef %8 %9
+%7 = OpLabel
+OpReturn
+%8 = OpLabel
+OpBranch %5
+%9 = OpLabel
+OpSelectionMerge %6 None
+OpBranchConditional %undef %5 %5
+%5 = OpLabel
+OpBranch %2
+%4 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, BranchConditionalDifferentTargetsPre1p6) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranchConditional %undef %target %target
+%target = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_5);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+}
+
+TEST_F(ValidateCFG, BranchConditionalDifferentTargetsPost1p6) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranchConditional %undef %target %target
+%target = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("In SPIR-V 1.6 or later, True Label and False Label "
+ "must be different labels"));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_composites_test.cpp b/test/val/val_composites_test.cpp
index bf7caa9f..507ee889 100644
--- a/test/val/val_composites_test.cpp
+++ b/test/val/val_composites_test.cpp
@@ -86,10 +86,10 @@ OpCapability Float64
%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12
%f32vec2arr3 = OpTypeArray %f32vec2 %u32_3
-%f32vec2rarr = OpTypeRuntimeArray %f32vec2
+%f32vec2arr2 = OpTypeArray %f32vec2 %u32_2
%f32u32struct = OpTypeStruct %f32 %u32
-%big_struct = OpTypeStruct %f32 %f32vec4 %f32mat23 %f32vec2arr3 %f32vec2rarr %f32u32struct
+%big_struct = OpTypeStruct %f32 %f32vec4 %f32mat23 %f32vec2arr3 %f32vec2arr2 %f32u32struct
%ptr_big_struct = OpTypePointer Uniform %big_struct
%var_big_struct = OpVariable %ptr_big_struct Uniform
@@ -150,7 +150,6 @@ OpMemoryModel Logical GLSL450
; uniform blockName {
; S s;
; bool cond;
-; RunTimeArray arr;
; }
%f32arr = OpTypeRuntimeArray %float
@@ -161,7 +160,7 @@ OpMemoryModel Logical GLSL450
%_ptr_Function_vec4 = OpTypePointer Function %v4float
%_ptr_Uniform_vec4 = OpTypePointer Uniform %v4float
%struct_s = OpTypeStruct %int %array5_vec4 %int %array5_mat4x3
-%struct_blockName = OpTypeStruct %struct_s %int %f32arr
+%struct_blockName = OpTypeStruct %struct_s %int
%_ptr_Uniform_blockName = OpTypePointer Uniform %struct_blockName
%_ptr_Uniform_struct_s = OpTypePointer Uniform %struct_s
%_ptr_Uniform_array5_mat4x3 = OpTypePointer Uniform %array5_mat4x3
@@ -644,8 +643,8 @@ TEST_F(ValidateComposites, CompositeExtractSuccess) {
%val12 = OpCompositeExtract %f32 %struct 2 2 1
%val13 = OpCompositeExtract %f32vec2 %struct 3 2
%val14 = OpCompositeExtract %f32 %struct 3 2 1
-%val15 = OpCompositeExtract %f32vec2 %struct 4 100
-%val16 = OpCompositeExtract %f32 %struct 4 1000 1
+%val15 = OpCompositeExtract %f32vec2 %struct 4 1
+%val16 = OpCompositeExtract %f32 %struct 4 0 1
%val17 = OpCompositeExtract %f32 %struct 5 0
%val18 = OpCompositeExtract %u32 %struct 5 1
)";
@@ -868,8 +867,8 @@ TEST_F(ValidateComposites, CompositeInsertSuccess) {
%val12 = OpCompositeInsert %big_struct %f32_3 %struct 2 2 1
%val13 = OpCompositeInsert %big_struct %f32vec2_01 %struct 3 2
%val14 = OpCompositeInsert %big_struct %f32_3 %struct 3 2 1
-%val15 = OpCompositeInsert %big_struct %f32vec2_01 %struct 4 100
-%val16 = OpCompositeInsert %big_struct %f32_3 %struct 4 1000 1
+%val15 = OpCompositeInsert %big_struct %f32vec2_01 %struct 4 1
+%val16 = OpCompositeInsert %big_struct %f32_3 %struct 4 0 1
%val17 = OpCompositeInsert %big_struct %f32_3 %struct 5 0
%val18 = OpCompositeInsert %big_struct %u32_3 %struct 5 1
)";
@@ -1382,8 +1381,8 @@ TEST_F(ValidateComposites, CompositeExtractStructIndexOutOfBoundBad) {
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Index is out of bounds, can not find index 3 in the "
- "structure <id> '25'. This structure has 3 members. "
- "Largest valid index is 2."));
+ "structure <id> '25'. This structure has 2 members. "
+ "Largest valid index is 1."));
}
// Invalid. Index into a struct is larger than the number of struct members.
@@ -1403,8 +1402,8 @@ TEST_F(ValidateComposites, CompositeInsertStructIndexOutOfBoundBad) {
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("Index is out of bounds, can not find index 3 in the structure "
- "<id> '25'. This structure has 3 members. Largest valid index "
- "is 2."));
+ "<id> '25'. This structure has 2 members. Largest valid index "
+ "is 1."));
}
// #1403: Ensure that the default spec constant value is not used to check the
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index 096fd172..6f5a2461 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -226,6 +226,7 @@ TEST_F(ValidateDecorations, StructAllMembersHaveBuiltInDecorationsGood) {
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
+ OpDecorate %_struct_1 Block
OpMemberDecorate %_struct_1 0 BuiltIn Position
OpMemberDecorate %_struct_1 1 BuiltIn Position
OpMemberDecorate %_struct_1 2 BuiltIn Position
@@ -243,6 +244,7 @@ TEST_F(ValidateDecorations, MixedBuiltInDecorationsBad) {
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
+ OpDecorate %_struct_1 Block
OpMemberDecorate %_struct_1 0 BuiltIn Position
OpMemberDecorate %_struct_1 1 BuiltIn Position
%float = OpTypeFloat 32
@@ -265,6 +267,7 @@ TEST_F(ValidateDecorations, StructContainsBuiltInStructBad) {
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
+ OpDecorate %_struct_1 Block
OpMemberDecorate %_struct_1 0 BuiltIn Position
OpMemberDecorate %_struct_1 1 BuiltIn Position
OpMemberDecorate %_struct_1 2 BuiltIn Position
@@ -305,6 +308,8 @@ TEST_F(ValidateDecorations, MultipleBuiltInObjectsConsumedByOpEntryPointBad) {
OpEntryPoint Geometry %main "main" %in_1 %in_2
OpExecutionMode %main InputPoints
OpExecutionMode %main OutputPoints
+ OpDecorate %struct_1 Block
+ OpDecorate %struct_2 Block
OpMemberDecorate %struct_1 0 BuiltIn InvocationId
OpMemberDecorate %struct_2 0 BuiltIn Position
%int = OpTypeInt 32 1
@@ -339,6 +344,8 @@ TEST_F(ValidateDecorations,
OpEntryPoint Geometry %main "main" %in_1 %out_1
OpExecutionMode %main InputPoints
OpExecutionMode %main OutputPoints
+ OpDecorate %struct_1 Block
+ OpDecorate %struct_2 Block
OpMemberDecorate %struct_1 0 BuiltIn InvocationId
OpMemberDecorate %struct_2 0 BuiltIn Position
%int = OpTypeInt 32 1
@@ -560,6 +567,8 @@ OpFunctionEnd
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Location-04915"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("A BuiltIn variable (id 2) cannot have any Location or "
"Component decorations"));
}
@@ -594,10 +603,354 @@ OpFunctionEnd
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Location-04915"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("A BuiltIn variable (id 2) cannot have any Location or "
"Component decorations"));
}
+TEST_F(ValidateDecorations, LocationDecorationOnNumericTypeBad) {
+ const spv_target_env env = SPV_ENV_VULKAN_1_0;
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %fragCoord
+ OpExecutionMode %main OriginUpperLeft
+ OpDecorate %fragCoord Location 0
+ OpDecorate %v4float Location 1
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%ptr_v4float = OpTypePointer Output %v4float
+ %fragCoord = OpVariable %ptr_v4float Output
+%non_interface = OpVariable %ptr_v4float Output
+ %main = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, env);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Location decoration on target <id> '3[%v4float]' must "
+ "be a variable"));
+}
+
+TEST_F(ValidateDecorations, LocationDecorationOnStructBad) {
+ const spv_target_env env = SPV_ENV_VULKAN_1_0;
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %fragCoord
+ OpExecutionMode %main OriginUpperLeft
+ OpDecorate %fragCoord Location 0
+ OpDecorate %struct Location 1
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %struct = OpTypeStruct %float
+ %v4float = OpTypeVector %float 4
+%ptr_v4float = OpTypePointer Output %v4float
+ %fragCoord = OpVariable %ptr_v4float Output
+%non_interface = OpVariable %ptr_v4float Output
+ %main = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, env);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Location decoration on target <id> '3[%_struct_3]' "
+ "must be a variable"));
+}
+
+TEST_F(ValidateDecorations,
+ LocationDecorationUnusedNonInterfaceVariableVulkan_Ignored) {
+ const spv_target_env env = SPV_ENV_VULKAN_1_0;
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %fragCoord
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %fragCoord Location 0
+ OpDecorate %non_interface Location 1
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%ptr_v4float = OpTypePointer Output %v4float
+ %fragCoord = OpVariable %ptr_v4float Output
+%non_interface = OpVariable %ptr_v4float Output
+ %main = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, env);
+ EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
+ EXPECT_EQ(getDiagnosticString(), "");
+}
+
+TEST_F(ValidateDecorations,
+ LocationDecorationNonInterfaceStructVulkan_Ignored) {
+ const spv_target_env env = SPV_ENV_VULKAN_1_0;
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %fragCoord
+ OpExecutionMode %main OriginUpperLeft
+ OpDecorate %fragCoord Location 0
+ OpMemberDecorate %block 0 Location 2
+ OpMemberDecorate %block 0 Component 1
+ OpDecorate %block Block
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %vec3 = OpTypeVector %float 3
+%outvar_ptr = OpTypePointer Output %vec3
+ %fragCoord = OpVariable %outvar_ptr Output
+ %block = OpTypeStruct %vec3
+ %invar_ptr = OpTypePointer Input %block
+%non_interface = OpVariable %invar_ptr Input
+ %main = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, env);
+ EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
+ EXPECT_EQ(getDiagnosticString(), "");
+}
+
+TEST_F(ValidateDecorations, LocationDecorationNonInterfaceStructVulkanGood) {
+ const spv_target_env env = SPV_ENV_VULKAN_1_0;
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %fragCoord %interface
+ OpExecutionMode %main OriginUpperLeft
+ OpDecorate %fragCoord Location 0
+ OpMemberDecorate %block 0 Location 2
+ OpMemberDecorate %block 0 Component 1
+ OpDecorate %block Block
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %vec3 = OpTypeVector %float 3
+%outvar_ptr = OpTypePointer Output %vec3
+ %fragCoord = OpVariable %outvar_ptr Output
+ %block = OpTypeStruct %vec3
+ %invar_ptr = OpTypePointer Input %block
+ %interface = OpVariable %invar_ptr Input ;; this variable is unused. Ignore it
+ %main = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, env);
+ EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
+}
+
+TEST_F(ValidateDecorations, LocationDecorationVariableNonStructVulkanBad) {
+ const spv_target_env env = SPV_ENV_VULKAN_1_0;
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %fragCoord %nonblock_var
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %fragCoord Location 0
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%ptr_v4float = OpTypePointer Output %v4float
+ %fragCoord = OpVariable %ptr_v4float Output
+%nonblock_var = OpVariable %ptr_v4float Output
+ %main = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, env);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Location-04916"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Variable must be decorated with a location"));
+}
+
+TEST_F(ValidateDecorations, LocationDecorationVariableStructNoBlockVulkanBad) {
+ const spv_target_env env = SPV_ENV_VULKAN_1_0;
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %fragCoord %block_var
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %fragCoord Location 0
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%ptr_v4float = OpTypePointer Output %v4float
+ %fragCoord = OpVariable %ptr_v4float Output
+ %block = OpTypeStruct %v4float
+ %block_ptr = OpTypePointer Output %block
+ %block_var = OpVariable %block_ptr Output
+ %main = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, env);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Location-04917"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Variable must be decorated with a location"));
+}
+
+TEST_F(ValidateDecorations, LocationDecorationVariableNoBlockVulkanGood) {
+ const spv_target_env env = SPV_ENV_VULKAN_1_0;
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %fragCoord %block_var
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %fragCoord Location 0
+ OpDecorate %block_var Location 1
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%ptr_v4float = OpTypePointer Output %v4float
+ %fragCoord = OpVariable %ptr_v4float Output
+ %block = OpTypeStruct %v4float
+ %block_ptr = OpTypePointer Output %block
+ %block_var = OpVariable %block_ptr Output
+ %main = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, env);
+ EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+}
+
+TEST_F(ValidateDecorations, LocationDecorationVariableExtraMemeberVulkan) {
+ const spv_target_env env = SPV_ENV_VULKAN_1_0;
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %fragCoord %block_var
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %fragCoord Location 0
+ OpDecorate %block Block
+ OpDecorate %block_var Location 1
+ OpMemberDecorate %block 0 Location 1
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%ptr_v4float = OpTypePointer Output %v4float
+ %fragCoord = OpVariable %ptr_v4float Output
+ %block = OpTypeStruct %v4float
+ %block_ptr = OpTypePointer Output %block
+ %block_var = OpVariable %block_ptr Output
+ %main = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+ CompileSuccessfully(spirv, env);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Location-04918"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Members cannot be assigned a location"));
+}
+
+TEST_F(ValidateDecorations, LocationDecorationVariableMissingMemeberVulkan) {
+ const spv_target_env env = SPV_ENV_VULKAN_1_0;
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %fragCoord %block_var
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %fragCoord Location 0
+ OpDecorate %block Block
+ OpMemberDecorate %block 0 Location 1
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%ptr_v4float = OpTypePointer Output %v4float
+ %fragCoord = OpVariable %ptr_v4float Output
+ %block = OpTypeStruct %v4float %v4float
+ %block_ptr = OpTypePointer Output %block
+ %block_var = OpVariable %block_ptr Output
+ %main = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, env);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Location-04919"));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Member index 1 is missing a location assignment"));
+}
+
+TEST_F(ValidateDecorations, LocationDecorationVariableOnlyMemeberVulkanGood) {
+ const spv_target_env env = SPV_ENV_VULKAN_1_0;
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %fragCoord %block_var
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %fragCoord Location 0
+ OpDecorate %block Block
+ OpMemberDecorate %block 0 Location 1
+ OpMemberDecorate %block 1 Location 4
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%ptr_v4float = OpTypePointer Output %v4float
+ %fragCoord = OpVariable %ptr_v4float Output
+ %block = OpTypeStruct %v4float %v4float
+ %block_ptr = OpTypePointer Output %block
+ %block_var = OpVariable %block_ptr Output
+ %main = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, env);
+ EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+}
+
// #version 440
// #extension GL_EXT_nonuniform_qualifier : enable
// layout(binding = 1) uniform sampler2D s2d[];
@@ -687,8 +1040,7 @@ TEST_F(ValidateDecorations, BlockDecoratingArrayBad) {
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Block decoration on a non-struct type"));
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type"));
}
TEST_F(ValidateDecorations, BlockDecoratingIntBad) {
@@ -713,8 +1065,7 @@ TEST_F(ValidateDecorations, BlockDecoratingIntBad) {
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Block decoration on a non-struct type"));
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type"));
}
TEST_F(ValidateDecorations, BlockMissingOffsetBad) {
@@ -6129,9 +6480,7 @@ TEST_F(ValidateDecorations, NonWritableLabelTargetBad) {
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Target of NonWritable decoration must be a "
- "memory object declaration (a variable or a function "
- "parameter)\n %label = OpLabel"));
+ HasSubstr("must be a memory object declaration"));
}
TEST_F(ValidateDecorations, NonWritableTypeTargetBad) {
@@ -6140,9 +6489,7 @@ TEST_F(ValidateDecorations, NonWritableTypeTargetBad) {
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Target of NonWritable decoration must be a "
- "memory object declaration (a variable or a function "
- "parameter)\n %void = OpTypeVoid"));
+ HasSubstr("must be a memory object declaration"));
}
TEST_F(ValidateDecorations, NonWritableValueTargetBad) {
@@ -6151,9 +6498,7 @@ TEST_F(ValidateDecorations, NonWritableValueTargetBad) {
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Target of NonWritable decoration must be a "
- "memory object declaration (a variable or a function "
- "parameter)\n %float_0 = OpConstant %float 0"));
+ HasSubstr("must be a memory object declaration"));
}
TEST_F(ValidateDecorations, NonWritableValueParamBad) {
@@ -6161,10 +6506,7 @@ TEST_F(ValidateDecorations, NonWritableValueParamBad) {
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Target of NonWritable decoration is invalid: must "
- "point to a storage image, uniform block, or storage "
- "buffer\n %param_f = OpFunctionParameter %float"));
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a pointer type"));
}
TEST_F(ValidateDecorations, NonWritablePointerParamButWrongTypeBad) {
@@ -6467,8 +6809,7 @@ OpFunctionEnd
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Target of Component decoration must be "
- "a memory object declaration"));
+ HasSubstr("must be a memory object declaration"));
}
TEST_F(ValidateDecorations, ComponentDecorationBadStorageClass) {
@@ -6767,8 +7108,8 @@ TEST_F(ValidateDecorations, ComponentDecorationFunctionParameter) {
)";
CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
- EXPECT_THAT(getDiagnosticString(), Eq(""));
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a pointer type"));
}
TEST_F(ValidateDecorations, VulkanStorageBufferBlock) {
@@ -7164,9 +7505,7 @@ OpDecorate %struct Location 0
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Location decoration can only be applied to a variable "
- "or member of a structure type"));
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable"));
}
TEST_F(ValidateDecorations, LocationFloatBad) {
@@ -7180,9 +7519,7 @@ OpDecorate %float Location 0
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Location decoration can only be applied to a variable "
- "or member of a structure type"));
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable"));
}
TEST_F(ValidateDecorations, WorkgroupSingleBlockVariable) {
@@ -7571,9 +7908,7 @@ TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableNotAStruct) {
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Block decoration on a non-struct type"));
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type"));
}
TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableMissingLayout) {
@@ -7648,6 +7983,213 @@ TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableBadLayout) {
"member 0 at offset 1 is not aligned to 4"));
}
+TEST_F(ValidateDecorations, BadMatrixStrideUniform) {
+ 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 3
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%float4 = OpTypeVector %float 4
+%matrix4x4 = OpTypeMatrix %float4 4
+%block = OpTypeStruct %matrix4x4
+%block_ptr = OpTypePointer Uniform %block
+%var = OpVariable %block_ptr Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Structure id 2 decorated as Block for variable in Uniform storage "
+ "class must follow standard uniform buffer layout rules: member 0 is "
+ "a matrix with stride 3 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateDecorations, BadMatrixStrideStorageBuffer) {
+ 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 3
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%float4 = OpTypeVector %float 4
+%matrix4x4 = OpTypeMatrix %float4 4
+%block = OpTypeStruct %matrix4x4
+%block_ptr = OpTypePointer StorageBuffer %block
+%var = OpVariable %block_ptr StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Structure id 2 decorated as Block for variable in StorageBuffer "
+ "storage class must follow standard storage buffer layout rules: "
+ "member 0 is a matrix with stride 3 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateDecorations, BadMatrixStridePushConstant) {
+ 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 3
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%float4 = OpTypeVector %float 4
+%matrix4x4 = OpTypeMatrix %float4 4
+%block = OpTypeStruct %matrix4x4
+%block_ptr = OpTypePointer PushConstant %block
+%var = OpVariable %block_ptr PushConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Structure id 2 decorated as Block for variable in PushConstant "
+ "storage class must follow standard storage buffer layout rules: "
+ "member 0 is a matrix with stride 3 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateDecorations, BadMatrixStrideStorageBufferScalarLayout) {
+ 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 3
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%float4 = OpTypeVector %float 4
+%matrix4x4 = OpTypeMatrix %float4 4
+%block = OpTypeStruct %matrix4x4
+%block_ptr = OpTypePointer StorageBuffer %block
+%var = OpVariable %block_ptr StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ options_->scalar_block_layout = true;
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Structure id 2 decorated as Block for variable in StorageBuffer "
+ "storage class must follow scalar storage buffer layout rules: "
+ "member 0 is a matrix with stride 3 not satisfying alignment to 4"));
+}
+
+TEST_F(ValidateDecorations, MissingOffsetStructNestedInArray) {
+ 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 %array ArrayStride 4
+OpDecorate %outer Block
+OpMemberDecorate %outer 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%inner = OpTypeStruct %int
+%array = OpTypeArray %inner %int_4
+%outer = OpTypeStruct %array
+%ptr_ssbo_outer = OpTypePointer StorageBuffer %outer
+%var = OpVariable %ptr_ssbo_outer StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Structure id 3 decorated as Block must be explicitly "
+ "laid out with Offset decorations"));
+}
+
+TEST_F(ValidateDecorations, AllOnesOffset) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %outer Block
+OpMemberDecorate %outer 0 Offset 0
+OpMemberDecorate %struct 0 Offset 4294967295
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%struct = OpTypeStruct %int
+%outer = OpTypeStruct %struct
+%ptr = OpTypePointer Uniform %outer
+%var = OpVariable %ptr Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("decorated as Block must be explicitly laid out with "
+ "Offset decorations"));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_derivatives_test.cpp b/test/val/val_derivatives_test.cpp
index 606abb93..0a846610 100644
--- a/test/val/val_derivatives_test.cpp
+++ b/test/val/val_derivatives_test.cpp
@@ -160,6 +160,31 @@ TEST_F(ValidateDerivatives, OpDPdxWrongExecutionModel) {
"execution model: DPdx"));
}
+TEST_F(ValidateDerivatives, NoExecutionModeGLCompute) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%float4 = OpTypeVector %float 4
+%undef = OpUndef %float4
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%derivative = OpDPdy %float4 %undef
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Derivative instructions require "
+ "DerivativeGroupQuadsNV or DerivativeGroupLinearNV "
+ "execution mode for GLCompute execution model"));
+}
+
using ValidateHalfDerivatives = spvtest::ValidateBase<std::string>;
TEST_P(ValidateHalfDerivatives, ScalarFailure) {
diff --git a/test/val/val_ext_inst_debug_test.cpp b/test/val/val_ext_inst_debug_test.cpp
new file mode 100644
index 00000000..307a8009
--- /dev/null
+++ b/test/val/val_ext_inst_debug_test.cpp
@@ -0,0 +1,5313 @@
+// 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.
+
+// Tests validation rules of GLSL.450.std and OpenCL.std extended instructions.
+// Doesn't test OpenCL.std vector size 2, 3, 4, 8 or 16 rules (not supported
+// by standard SPIR-V).
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::Not;
+
+using ValidateOldDebugInfo = spvtest::ValidateBase<std::string>;
+using ValidateOpenCL100DebugInfo = spvtest::ValidateBase<std::string>;
+using ValidateXDebugInfo = spvtest::ValidateBase<std::string>;
+using ValidateLocalDebugInfoOutOfFunction = spvtest::ValidateBase<std::string>;
+using ValidateOpenCL100DebugInfoDebugTypedef =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateVulkan100DebugInfoDebugTypedef =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugTypeEnum =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateVulkan100DebugInfoDebugTypeEnum =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugTypeComposite =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateVulkan100DebugInfoDebugTypeComposite =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugTypeMember =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateVulkan100DebugInfoDebugTypeMember =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugTypeInheritance =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugFunction =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateVulkan100DebugInfoDebugFunction =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugFunctionDeclaration =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateVulkan100DebugInfoDebugFunctionDeclaration =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugLexicalBlock =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateVulkan100DebugInfoDebugLexicalBlock =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugLocalVariable =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateVulkan100DebugInfoDebugLocalVariable =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugGlobalVariable =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateVulkan100DebugInfoDebugGlobalVariable =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugDeclare =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateVulkan100DebugInfoDebugDeclare =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugValue =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateVulkan100DebugInfoDebugValue =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateVulkan100DebugInfo = spvtest::ValidateBase<std::string>;
+
+std::string GenerateShaderCodeForDebugInfo(
+ const std::string& op_string_instructions,
+ const std::string& op_const_instructions,
+ const std::string& debug_instructions_before_main, const std::string& body,
+ const std::string& capabilities_and_extensions = "",
+ const std::string& execution_model = "Fragment") {
+ std::ostringstream ss;
+ ss << R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Float64
+OpCapability Int16
+OpCapability Int64
+)";
+
+ ss << capabilities_and_extensions;
+ ss << "%extinst = OpExtInstImport \"GLSL.std.450\"\n";
+ ss << "OpMemoryModel Logical GLSL450\n";
+ ss << "OpEntryPoint " << execution_model << " %main \"main\""
+ << " %f32_output"
+ << " %f32vec2_output"
+ << " %u32_output"
+ << " %u32vec2_output"
+ << " %u64_output"
+ << " %f32_input"
+ << " %f32vec2_input"
+ << " %u32_input"
+ << " %u32vec2_input"
+ << " %u64_input"
+ << "\n";
+ if (execution_model == "Fragment") {
+ ss << "OpExecutionMode %main OriginUpperLeft\n";
+ }
+
+ ss << op_string_instructions;
+
+ ss << R"(
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bool = OpTypeBool
+%f16 = OpTypeFloat 16
+%f32 = OpTypeFloat 32
+%f64 = OpTypeFloat 64
+%u32 = OpTypeInt 32 0
+%s32 = OpTypeInt 32 1
+%u64 = OpTypeInt 64 0
+%s64 = OpTypeInt 64 1
+%u16 = OpTypeInt 16 0
+%s16 = OpTypeInt 16 1
+%f32vec2 = OpTypeVector %f32 2
+%f32vec3 = OpTypeVector %f32 3
+%f32vec4 = OpTypeVector %f32 4
+%f64vec2 = OpTypeVector %f64 2
+%f64vec3 = OpTypeVector %f64 3
+%f64vec4 = OpTypeVector %f64 4
+%u32vec2 = OpTypeVector %u32 2
+%u32vec3 = OpTypeVector %u32 3
+%s32vec2 = OpTypeVector %s32 2
+%u32vec4 = OpTypeVector %u32 4
+%s32vec4 = OpTypeVector %s32 4
+%u64vec2 = OpTypeVector %u64 2
+%s64vec2 = OpTypeVector %s64 2
+%f64mat22 = OpTypeMatrix %f64vec2 2
+%f32mat22 = OpTypeMatrix %f32vec2 2
+%f32mat23 = OpTypeMatrix %f32vec2 3
+%f32mat32 = OpTypeMatrix %f32vec3 2
+%f32mat33 = OpTypeMatrix %f32vec3 3
+
+%f32_0 = OpConstant %f32 0
+%f32_1 = OpConstant %f32 1
+%f32_2 = OpConstant %f32 2
+%f32_3 = OpConstant %f32 3
+%f32_4 = OpConstant %f32 4
+%f32_h = OpConstant %f32 0.5
+%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1
+%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2
+%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2
+%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3
+%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3
+%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4
+
+%f64_0 = OpConstant %f64 0
+%f64_1 = OpConstant %f64 1
+%f64_2 = OpConstant %f64 2
+%f64_3 = OpConstant %f64 3
+%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1
+%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2
+%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3
+
+%f16_0 = OpConstant %f16 0
+%f16_1 = OpConstant %f16 1
+%f16_h = OpConstant %f16 0.5
+
+%u32_0 = OpConstant %u32 0
+%u32_1 = OpConstant %u32 1
+%u32_2 = OpConstant %u32 2
+%u32_3 = OpConstant %u32 3
+
+%s32_0 = OpConstant %s32 0
+%s32_1 = OpConstant %s32 1
+%s32_2 = OpConstant %s32 2
+%s32_3 = OpConstant %s32 3
+
+%u64_0 = OpConstant %u64 0
+%u64_1 = OpConstant %u64 1
+%u64_2 = OpConstant %u64 2
+%u64_3 = OpConstant %u64 3
+
+%s64_0 = OpConstant %s64 0
+%s64_1 = OpConstant %s64 1
+%s64_2 = OpConstant %s64 2
+%s64_3 = OpConstant %s64 3
+)";
+
+ ss << op_const_instructions;
+
+ ss << R"(
+%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1
+%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1
+
+%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2
+%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2
+
+%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3
+%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3
+
+%s64vec2_01 = OpConstantComposite %s64vec2 %s64_0 %s64_1
+%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1
+
+%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12
+%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12
+
+%f32_ptr_output = OpTypePointer Output %f32
+%f32vec2_ptr_output = OpTypePointer Output %f32vec2
+
+%u32_ptr_output = OpTypePointer Output %u32
+%u32vec2_ptr_output = OpTypePointer Output %u32vec2
+
+%u64_ptr_output = OpTypePointer Output %u64
+
+%f32_output = OpVariable %f32_ptr_output Output
+%f32vec2_output = OpVariable %f32vec2_ptr_output Output
+
+%u32_output = OpVariable %u32_ptr_output Output
+%u32vec2_output = OpVariable %u32vec2_ptr_output Output
+
+%u64_output = OpVariable %u64_ptr_output Output
+
+%f32_ptr_input = OpTypePointer Input %f32
+%f32vec2_ptr_input = OpTypePointer Input %f32vec2
+
+%u32_ptr_input = OpTypePointer Input %u32
+%u32vec2_ptr_input = OpTypePointer Input %u32vec2
+
+%u64_ptr_input = OpTypePointer Input %u64
+
+%f32_ptr_function = OpTypePointer Function %f32
+
+%f32_input = OpVariable %f32_ptr_input Input
+%f32vec2_input = OpVariable %f32vec2_ptr_input Input
+
+%u32_input = OpVariable %u32_ptr_input Input
+%u32vec2_input = OpVariable %u32vec2_ptr_input Input
+
+%u64_input = OpVariable %u64_ptr_input Input
+
+%u32_ptr_function = OpTypePointer Function %u32
+
+%struct_f16_u16 = OpTypeStruct %f16 %u16
+%struct_f32_f32 = OpTypeStruct %f32 %f32
+%struct_f32_f32_f32 = OpTypeStruct %f32 %f32 %f32
+%struct_f32_u32 = OpTypeStruct %f32 %u32
+%struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32
+%struct_u32_f32 = OpTypeStruct %u32 %f32
+%struct_u32_u32 = OpTypeStruct %u32 %u32
+%struct_f32_f64 = OpTypeStruct %f32 %f64
+%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2
+%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2
+)";
+
+ ss << debug_instructions_before_main;
+
+ ss << R"(
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+)";
+
+ ss << body;
+
+ ss << R"(
+OpReturn
+OpFunctionEnd)";
+
+ return ss.str();
+}
+
+TEST_F(ValidateOldDebugInfo, UseDebugInstructionOutOfFunction) {
+ const std::string src = R"(
+%code = OpString "main() {}"
+)";
+
+ const std::string dbg_inst = R"(
+%cu = OpExtInst %void %DbgExt DebugCompilationUnit %code 1 1
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "DebugInfo"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, UseDebugInstructionOutOfFunction) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+ const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugSourceInFunction) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+ const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst,
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Debug info extension instructions other than DebugScope, "
+ "DebugNoScope, DebugDeclare, DebugValue must appear between "
+ "section 9 (types, constants, global variables) and section 10 "
+ "(function declarations)"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugSourceInFunction) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+ const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst,
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Debug info extension instructions other than DebugScope, "
+ "DebugNoScope, DebugDeclare, DebugValue must appear between "
+ "section 9 (types, constants, global variables) and section 10 "
+ "(function declarations)"));
+}
+
+TEST_P(ValidateLocalDebugInfoOutOfFunction, OpenCLDebugInfo100DebugScope) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%void_name = OpString "void"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+%int_name = OpString "int"
+%foo_name = OpString "foo"
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_0 Signed
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %int_info %dbg_src 1 1 %main_info FlagIsLocal
+%expr = OpExtInst %void %DbgExt DebugExpression
+)";
+
+ const std::string body = R"(
+%foo = OpVariable %u32_ptr_function Function
+%foo_val = OpLoad %u32 %foo
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, "", dbg_inst_header + GetParam(), body, extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue "
+ "of debug info extension must appear in a function "
+ "body"));
+}
+
+TEST_P(ValidateLocalDebugInfoOutOfFunction, VulkanDebugInfo100DebugScope) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%void_name = OpString "void"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+%int_name = OpString "int"
+%foo_name = OpString "foo"
+)";
+
+ 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
+%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_0 %u32_1 %u32_0
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_1
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %int_info %dbg_src %u32_1 %u32_1 %main_info %u32_4
+%expr = OpExtInst %void %DbgExt DebugExpression
+)";
+
+ const std::string body = R"(
+%foo = OpVariable %u32_ptr_function Function
+%main_def = OpExtInst %void %DbgExt DebugFunctionDefinition %main_info %main
+%foo_val = OpLoad %u32 %foo
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, constants, dbg_inst_header + GetParam(), body, extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue "
+ "of debug info extension must appear in a function "
+ "body"));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllLocalDebugInfo, ValidateLocalDebugInfoOutOfFunction,
+ ::testing::ValuesIn(std::vector<std::string>{
+ "%main_scope = OpExtInst %void %DbgExt DebugScope %main_info",
+ "%no_scope = OpExtInst %void %DbgExt DebugNoScope",
+ }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionForwardReference) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%void_name = OpString "void"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main
+)";
+
+ const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %main_info
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, "", dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionMissingOpFunction) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%void_name = OpString "void"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbgNone = OpExtInst %void %DbgExt DebugInfoNone
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %dbgNone
+)";
+
+ const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %main_info
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, "", dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugScopeBeforeOpVariableInFunction) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main
+)";
+
+ const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %main_info
+%foo = OpVariable %f32_ptr_function Function
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeSizeDebugInfoNone) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "OpaqueType foo;
+main() {}
+"
+%ty_name = OpString "struct VS_OUTPUT"
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header,
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeForwardReference) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+ float4 color : COLOR;
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_color_name = OpString "color : COLOR"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
+%VS_OUTPUT_color_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_color_name %v4float_info %dbg_src 3 3 %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeMissingReference) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+ float4 color : COLOR;
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_color_name = OpString "color : COLOR"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("forward referenced IDs have not been defined"));
+}
+
+TEST_P(ValidateXDebugInfo, DebugSourceWrongResultType) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+ const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %bool %DbgExt DebugSource %src %code
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+ GetParam(), "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected result type must be a result id of "
+ "OpTypeVoid"));
+}
+
+TEST_P(ValidateXDebugInfo, DebugSourceFailFile) {
+ const std::string src = R"(
+%code = OpString "main() {}"
+)";
+
+ const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %DbgExt %code
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+ GetParam(), "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand File must be a result id of "
+ "OpString"));
+}
+
+TEST_P(ValidateXDebugInfo, DebugSourceFailSource) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+)";
+
+ const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %DbgExt
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+ GetParam(), "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Text must be a result id of "
+ "OpString"));
+}
+
+TEST_P(ValidateXDebugInfo, DebugSourceNoText) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+)";
+
+ const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+ GetParam(), "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+INSTANTIATE_TEST_SUITE_P(OpenCLAndVkDebugInfo100, ValidateXDebugInfo,
+ ::testing::ValuesIn(std::vector<std::string>{
+ R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)",
+ R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)",
+ }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnit) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+ const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnitFail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+ const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %src HLSL
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Source must be a result id of "
+ "DebugSource"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugCompilationUnitFail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+ const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %src %u32_5
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, dbg_inst,
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Source must be a result id of "
+ "DebugSource"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailName) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %int_32 %int_32 Float
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Name must be a result id of "
+ "OpString"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeBasicFailName) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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 %u32_32 %u32_32 %u32_3 %u32_0
+)";
+
+ 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 Name must be a result id of "
+ "OpString"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailSize) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %float_name Float
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Size must be a result id of "
+ "OpConstant"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeBasicFailSize) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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 %float_name %u32_3 %u32_0
+)";
+
+ 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 Size must be a result id of "
+ "OpConstant"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointer) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %float_info Function FlagIsLocal
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointerFail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %dbg_src Function FlagIsLocal
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Base Type must be a result id of "
+ "DebugTypeBasic"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifier) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %float_info ConstType
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifierFail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %comp_unit ConstType
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Base Type must be a result id of "
+ "DebugTypeBasic"));
+}
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeQualifier) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %float_info %u32_0
+)";
+
+ 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, DebugTypeQualifierFail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %comp_unit %u32_0
+)";
+
+ 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 Base Type must be a result id of "
+ "DebugTypeBasic"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArray) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %int_32
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayWithVariableSize) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%int_name = OpString "int"
+%main_name = OpString "main"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Unsigned
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %uint_info %dbg_src 1 1 %main_info FlagIsLocal
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailBaseType) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %comp_unit %int_32
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Base Type is not a valid debug "
+ "type"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCount) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %float_info
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, 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"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountFloat) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %f32_4
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, 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"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountZero) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_0
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, 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"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 1 %main_info FlagIsLocal
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, 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"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeArray) {
+ 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
+)";
+
+ 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
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_32
+)";
+
+ 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, DebugTypeArrayWithVariableSize) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%uint_name = OpString "uint"
+%main_name = OpString "main"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_6 = OpConstant %u32 6
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %uint_name %u32_32 %u32_6 %u32_0
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %uint_info %dbg_src %u32_1 %u32_1 %main_info %u32_4
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info
+)";
+
+ 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, DebugTypeArrayFailBaseType) {
+ 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
+)";
+
+ 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
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %comp_unit %u32_32
+)";
+
+ 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 Base Type is not a valid debug "
+ "type"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCount) {
+ 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
+)";
+
+ 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
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %float_info
+)";
+
+ 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("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"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCountFloat) {
+ 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
+)";
+
+ 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
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %f32_4
+)";
+
+ 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("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"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCountZero) {
+ 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
+)";
+
+ 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
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_0
+)";
+
+ 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("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"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_6 = OpConstant %u32 6
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src %u32_1 %u32_1 %main_info %u32_4
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info
+)";
+
+ 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("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"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVector) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 4
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Base Type must be a result id of "
+ "DebugTypeBasic"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentZero) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 0
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Base Type must be a result id of "
+ "DebugTypeBasic"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentFive) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 5
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Base Type must be a result id of "
+ "DebugTypeBasic"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeVector) {
+ 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
+)";
+
+ 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
+)";
+
+ 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, DebugTypeVectorFail) {
+ 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
+)";
+
+ 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 %dbg_src %u32_4
+)";
+
+ 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 Base Type must be a result id of "
+ "DebugTypeBasic"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorFailComponentZero) {
+ 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
+)";
+
+ 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_0
+)";
+
+ 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("Component Count must be positive "
+ "integer less than or equal to 4"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorFailComponentFive) {
+ 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
+)";
+
+ 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_5
+)";
+
+ 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("Component Count must be positive "
+ "integer less than or equal to 4"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypedef) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info = OpExtInst %void %DbgExt DebugTypedef %foo_name %float_info %dbg_src 1 1 %comp_unit
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugTypedef, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info = OpExtInst %void %DbgExt DebugTypedef )";
+ ss << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second +
+ " must be a result id of "));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypedef,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit)",
+ "Name"),
+ std::make_pair(R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit)",
+ "Base Type"),
+ std::make_pair(R"(%foo_name %float_info %comp_unit 1 1 %comp_unit)",
+ "Source"),
+ std::make_pair(R"(%foo_name %float_info %dbg_src 1 1 %dbg_src)",
+ "Parent"),
+ }));
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypedef) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%foo_info = OpExtInst %void %DbgExt DebugTypedef %foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit
+)";
+
+ 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_P(ValidateVulkan100DebugInfoDebugTypedef, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << 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
+%foo_info = OpExtInst %void %DbgExt DebugTypedef )";
+ ss << param.first;
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second +
+ " must be a result id of "));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugTypedef,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%dbg_src %float_info %dbg_src %u32_1 %u32_1 %comp_unit)",
+ "Name"),
+ std::make_pair(
+ R"(%foo_name %dbg_src %dbg_src %u32_1 %u32_1 %comp_unit)",
+ "Base Type"),
+ std::make_pair(
+ R"(%foo_name %float_info %comp_unit %u32_1 %u32_1 %comp_unit)",
+ "Source"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %dbg_src)",
+ "Parent"),
+ }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunction) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%main_type_info1 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_type_info2 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info
+%main_type_info3 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %float_info
+%main_type_info4 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void %float_info %float_info
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailReturn) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %dbg_src %float_info
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("expected operand Return Type is not a valid debug type"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailParam) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %void
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("expected operand Parameter Types is not a valid debug type"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeFunctionAndParams) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_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
+)";
+
+ 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
+%main_type_info1 = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void
+%main_type_info2 = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %float_info
+%main_type_info3 = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %float_info %float_info
+%main_type_info4 = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void %float_info %float_info
+)";
+
+ 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, DebugTypeFunctionFailReturn) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_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
+)";
+
+ 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
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %dbg_src %float_info
+)";
+
+ 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 Return Type is not a valid debug type"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeFunctionFailParam) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_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
+)";
+
+ 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
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %float_info %void
+)";
+
+ 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 Parameter Types is not a valid debug type"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeEnum) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%none = OpExtInst %void %DbgExt DebugInfoNone
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info1 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name
+%foo_info2 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name
+%foo_info3 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugTypeEnum, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info = OpExtInst %void %DbgExt DebugTypeEnum )";
+ ss << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeEnum,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)",
+ "Name"),
+ std::make_pair(
+ R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)",
+ "Underlying Types"),
+ std::make_pair(
+ R"(%foo_name %float_info %comp_unit 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)",
+ "Source"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src 1 1 %dbg_src %int_32 FlagIsPublic %u32_0 %foo_name)",
+ "Parent"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %void FlagIsPublic %u32_0 %foo_name)",
+ "Size"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %u32_0 FlagIsPublic %u32_0 %foo_name)",
+ "Size"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %foo_name %foo_name)",
+ "Value"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %u32_1)",
+ "Name"),
+ }));
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeEnum) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%none = OpExtInst %void %DbgExt DebugInfoNone
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%foo_info1 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name %u32_1 %foo_name
+%foo_info2 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name %u32_1 %foo_name
+%foo_info3 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3
+)";
+
+ 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_P(ValidateVulkan100DebugInfoDebugTypeEnum, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << 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
+%foo_info = OpExtInst %void %DbgExt DebugTypeEnum )";
+ ss << param.first;
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugTypeEnum,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%dbg_src %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name)",
+ "Name"),
+ std::make_pair(
+ R"(%foo_name %dbg_src %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name)",
+ "Underlying Types"),
+ std::make_pair(
+ R"(%foo_name %float_info %comp_unit %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name)",
+ "Source"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %dbg_src %u32_32 %u32_3 %u32_0 %foo_name)",
+ "Parent"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %void %u32_3 %u32_0 %foo_name)",
+ "Size"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_0 %u32_3 %u32_0 %foo_name)",
+ "Size"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %foo_name %foo_name)",
+ "Value"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src %u32_1 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %u32_1)",
+ "Name"),
+ }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeFunctionAndInheritance) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+};
+struct foo : VS_OUTPUT {
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main
+%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
+%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugTypeComposite, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+};
+struct foo : VS_OUTPUT {
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite )";
+ ss << param.first;
+ ss << R"(
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main
+%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
+%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second + " must be "));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeComposite,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%dbg_src Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
+ "Name"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name Structure %comp_unit 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
+ "Source"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %dbg_src %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
+ "Parent"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %int_128 %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
+ "Linkage Name"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %dbg_src FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
+ "Size"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %dbg_src %main_info %child)",
+ "Members"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %dbg_src %child)",
+ "Members"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %dbg_src)",
+ "Members"),
+ }));
+
+TEST_P(ValidateOpenCL100DebugInfoDebugTypeMember, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float pos : SV_POSITION;
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_32 FlagIsPublic %VS_OUTPUT_pos_info
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember )";
+ ss << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ if (!param.second.empty()) {
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second +
+ " must be a result id of "));
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeMember,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%dbg_src %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)",
+ "Name"),
+ std::make_pair(
+ R"(%VS_OUTPUT_pos_name %dbg_src %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)",
+ ""),
+ std::make_pair(
+ R"(%VS_OUTPUT_pos_name %float_info %float_info 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)",
+ "Source"),
+ std::make_pair(
+ R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %float_info %u32_0 %int_32 FlagIsPublic)",
+ "Parent"),
+ std::make_pair(
+ R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %void %int_32 FlagIsPublic)",
+ "Offset"),
+ std::make_pair(
+ R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %void FlagIsPublic)",
+ "Size"),
+ }));
+
+TEST_P(ValidateOpenCL100DebugInfoDebugTypeInheritance, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {};
+struct foo : VS_OUTPUT {};
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%foo_name = OpString "foo"
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_name %u32_0 FlagIsPublic %child
+%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
+%bar_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Union %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
+%child = OpExtInst %void %DbgExt DebugTypeInheritance )"
+ << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeInheritance,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(R"(%dbg_src %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)",
+ "Child must be a result id of"),
+ std::make_pair(R"(%foo_info %dbg_src %u32_0 %u32_0 FlagIsPublic)",
+ "Parent must be a result id of"),
+ std::make_pair(
+ R"(%bar_info %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)",
+ "Child must be class or struct debug type"),
+ std::make_pair(R"(%foo_info %bar_info %u32_0 %u32_0 FlagIsPublic)",
+ "Parent must be class or struct debug type"),
+ std::make_pair(R"(%foo_info %VS_OUTPUT_info %void %u32_0 FlagIsPublic)",
+ "Offset"),
+ std::make_pair(R"(%foo_info %VS_OUTPUT_info %u32_0 %void FlagIsPublic)",
+ "Size"),
+ }));
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeComposite) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+};
+struct foo : VS_OUTPUT {
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+%u32_128 = OpConstant %u32 128
+)";
+
+ 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
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src %u32_2 %u32_3 %u32_0 %u32_128 %u32_3
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %u32_3 %VS_OUTPUT_pos_info
+%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %foo_name %u32_0 %u32_3
+)";
+
+ 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_P(ValidateVulkan100DebugInfoDebugTypeComposite, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+};
+struct foo : VS_OUTPUT {
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+%u32_128 = OpConstant %u32 128
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << 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
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src %u32_2 %u32_3 %u32_0 %u32_128 %u32_3
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite )";
+ ss << param.first;
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second + " must be "));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugTypeComposite,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%dbg_src %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %u32_3 %VS_OUTPUT_pos_info)",
+ "Name"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name %u32_1 %comp_unit %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %u32_3 %VS_OUTPUT_pos_info)",
+ "Source"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %dbg_src %VS_OUTPUT_linkage_name %u32_128 %u32_3 %VS_OUTPUT_pos_info)",
+ "Parent"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %u32_128 %u32_128 %u32_3 %VS_OUTPUT_pos_info)",
+ "Linkage Name"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %dbg_src %u32_3 %VS_OUTPUT_pos_info)",
+ "Size"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %dbg_src %VS_OUTPUT_pos_info)",
+ "Flags"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %VS_OUTPUT_linkage_name %u32_128 %u32_3 %dbg_src)",
+ "Members"),
+ }));
+
+TEST_P(ValidateVulkan100DebugInfoDebugTypeMember, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float pos : SV_POSITION;
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+%u32_128 = OpConstant %u32 128
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << 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
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember )";
+ ss << param.first;
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ if (!param.second.empty()) {
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second +
+ " must be a result id of "));
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugTypeMember,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%dbg_src %float_info %dbg_src %u32_2 %u32_3 %u32_0 %u32_32 %u32_3)",
+ "Name"),
+ std::make_pair(
+ R"(%VS_OUTPUT_pos_name %dbg_src %dbg_src %u32_2 %u32_3 %u32_0 %u32_32 %u32_3)",
+ ""),
+ std::make_pair(
+ R"(%VS_OUTPUT_pos_name %float_info %float_info %u32_2 %u32_3 %u32_0 %u32_32 %u32_3)",
+ "Source"),
+ std::make_pair(
+ R"(%VS_OUTPUT_pos_name %float_info %dbg_src %u32_2 %u32_3 %void %u32_32 %u32_3)",
+ "Offset"),
+ std::make_pair(
+ R"(%VS_OUTPUT_pos_name %float_info %dbg_src %u32_2 %u32_3 %u32_0 %void %u32_3)",
+ "Size"),
+ std::make_pair(
+ R"(%VS_OUTPUT_pos_name %float_info %dbg_src %u32_2 %u32_3 %u32_0 %u32_32 %void)",
+ "Flags"),
+ }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionDeclaration) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+};
+main() {}
+"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header,
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugFunction, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+};
+main() {}
+"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic
+%main_info = OpExtInst %void %DbgExt DebugFunction )"
+ << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugFunction,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)",
+ "Name"),
+ std::make_pair(
+ R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)",
+ "Type"),
+ std::make_pair(
+ R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)",
+ "Source"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic 13 %main)",
+ "Parent"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic 13 %main)",
+ "Linkage Name"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %void)",
+ "Function"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main %dbg_src)",
+ "Declaration"),
+ }));
+
+TEST_P(ValidateOpenCL100DebugInfoDebugFunctionDeclaration, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+};
+main() {}
+"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration )"
+ << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail,
+ ValidateOpenCL100DebugInfoDebugFunctionDeclaration,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)",
+ "Name"),
+ std::make_pair(
+ R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)",
+ "Type"),
+ std::make_pair(
+ R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic)",
+ "Source"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic)",
+ "Parent"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic)",
+ "Linkage Name"),
+ }));
+
+TEST_F(ValidateVulkan100DebugInfo, DebugFunctionDeclaration) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+};
+main() {}
+"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_12 = OpConstant %u32 12
+%u32_13 = OpConstant %u32 13
+)";
+
+ 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
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13
+)";
+
+ 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_P(ValidateVulkan100DebugInfoDebugFunction, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+};
+main() {}
+"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_12 = OpConstant %u32 12
+%u32_13 = OpConstant %u32 13
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3
+%main_info = OpExtInst %void %DbgExt DebugFunction )"
+ << param.first;
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugFunction,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%u32_0 %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13)",
+ "Name"),
+ std::make_pair(
+ R"(%main_name %dbg_src %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13)",
+ "Type"),
+ std::make_pair(
+ R"(%main_name %main_type_info %comp_unit %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13)",
+ "Source"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %dbg_src %main_linkage_name %u32_3 %u32_13)",
+ "Parent"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %void %u32_3 %u32_13)",
+ "Linkage Name"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3 %u32_13 %dbg_src)",
+ "Declaration"),
+ }));
+
+TEST_P(ValidateVulkan100DebugInfoDebugFunctionDeclaration, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+};
+main() {}
+"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_12 = OpConstant %u32 12
+%u32_13 = OpConstant %u32 13
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration )"
+ << param.first;
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllVulkan100DebugInfoFail,
+ ValidateVulkan100DebugInfoDebugFunctionDeclaration,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%u32_0 %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3)",
+ "Name"),
+ std::make_pair(
+ R"(%main_name %dbg_src %dbg_src %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3)",
+ "Type"),
+ std::make_pair(
+ R"(%main_name %main_type_info %comp_unit %u32_12 %u32_1 %comp_unit %main_linkage_name %u32_3)",
+ "Source"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %dbg_src %main_linkage_name %u32_3)",
+ "Parent"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %void %u32_3)",
+ "Linkage Name"),
+ }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugLexicalBlock) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_block = OpExtInst %void %DbgExt DebugLexicalBlock %dbg_src 1 1 %comp_unit %main_name)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header,
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugLexicalBlock, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_block = OpExtInst %void %DbgExt DebugLexicalBlock )"
+ << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLexicalBlock,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(R"(%comp_unit 1 1 %comp_unit %main_name)", "Source"),
+ std::make_pair(R"(%dbg_src 1 1 %dbg_src %main_name)", "Parent"),
+ std::make_pair(R"(%dbg_src 1 1 %comp_unit %void)", "Name"),
+ }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailScope) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+)";
+
+ const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %dbg_src
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, "", dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailInlinedAt) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+)";
+
+ const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %comp_unit %dbg_src
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, "", dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined At"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugLexicalBlock) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+)";
+
+ 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
+%main_block = OpExtInst %void %DbgExt DebugLexicalBlock %dbg_src %u32_1 %u32_1 %comp_unit %main_name
+)";
+
+ 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_P(ValidateVulkan100DebugInfoDebugLexicalBlock, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%main_block = OpExtInst %void %DbgExt DebugLexicalBlock )"
+ << param.first;
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugLexicalBlock,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(R"(%comp_unit %u32_1 %u32_1 %comp_unit %main_name)",
+ "Source"),
+ std::make_pair(R"(%dbg_src %u32_1 %u32_1 %dbg_src %main_name)",
+ "Parent"),
+ std::make_pair(R"(%dbg_src %u32_1 %u32_1 %comp_unit %void)", "Name"),
+ }));
+
+TEST_F(ValidateVulkan100DebugInfo, DebugScopeFailScope) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+)";
+
+ 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
+)";
+
+ const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %dbg_src
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, constants, dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugScopeFailInlinedAt) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+)";
+
+ 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
+)";
+
+ const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %comp_unit %dbg_src
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, constants, dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined At"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugLocalVariable) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugLocalVariable, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo = OpExtInst %void %DbgExt DebugLocalVariable )"
+ << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLocalVariable,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%void %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0)",
+ "Name"),
+ std::make_pair(
+ R"(%foo_name %dbg_src %dbg_src 1 10 %comp_unit FlagIsLocal 0)",
+ "Type"),
+ std::make_pair(
+ R"(%foo_name %float_info %comp_unit 1 10 %comp_unit FlagIsLocal 0)",
+ "Source"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src 1 10 %dbg_src FlagIsLocal 0)",
+ "Parent"),
+ }));
+
+TEST_F(ValidateVulkan100DebugInfo, DebugLocalVariable) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_10 = OpConstant %u32 10
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%foo = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4
+)";
+
+ 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_P(ValidateVulkan100DebugInfoDebugLocalVariable, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_10 = OpConstant %u32 10
+%u32_32 = OpConstant %u32 32
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << 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
+%foo = OpExtInst %void %DbgExt DebugLocalVariable )"
+ << param.first;
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugLocalVariable,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%void %float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_3 %u32_0)",
+ "Name"),
+ std::make_pair(
+ R"(%foo_name %dbg_src %dbg_src %u32_1 %u32_10 %comp_unit %u32_3 %u32_0)",
+ "Type"),
+ std::make_pair(
+ R"(%foo_name %float_info %comp_unit %u32_1 %u32_10 %comp_unit %u32_3 %u32_0)",
+ "Source"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src %u32_1 %u32_10 %dbg_src %u32_3 %u32_0)",
+ "Parent"),
+ }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugDeclare) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%null_expr = OpExtInst %void %DbgExt DebugExpression
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0
+)";
+
+ const std::string body = R"(
+%foo = OpVariable %f32_ptr_function Function
+%decl = OpExtInst %void %DbgExt DebugDeclare %foo_info %foo %null_expr
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugDeclareParam) {
+ CompileSuccessfully(R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %in_var_COLOR
+ %4 = OpString "test.hlsl"
+ OpSource HLSL 620 %4 "#line 1 \"test.hlsl\"
+void main(float foo:COLOR) {}
+"
+ %11 = OpString "#line 1 \"test.hlsl\"
+void main(float foo:COLOR) {}
+"
+ %14 = OpString "float"
+ %17 = OpString "src.main"
+ %20 = OpString "foo"
+ OpName %in_var_COLOR "in.var.COLOR"
+ OpName %main "main"
+ OpName %param_var_foo "param.var.foo"
+ OpName %src_main "src.main"
+ OpName %foo "foo"
+ OpName %bb_entry "bb.entry"
+ OpDecorate %in_var_COLOR Location 0
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+ %float = OpTypeFloat 32
+%_ptr_Input_float = OpTypePointer Input %float
+ %void = OpTypeVoid
+ %23 = OpTypeFunction %void
+%_ptr_Function_float = OpTypePointer Function %float
+ %29 = OpTypeFunction %void %_ptr_Function_float
+ OpLine %4 1 21
+%in_var_COLOR = OpVariable %_ptr_Input_float Input
+ %10 = OpExtInst %void %1 DebugExpression
+ %12 = OpExtInst %void %1 DebugSource %4 %11
+ %13 = OpExtInst %void %1 DebugCompilationUnit 1 4 %12 HLSL
+ %15 = OpExtInst %void %1 DebugTypeBasic %14 %uint_32 Float
+ %16 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void %15
+ %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 1 1 %13 %17 FlagIsProtected|FlagIsPrivate 1 %src_main
+ %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 1 17 %18 FlagIsLocal 0
+ %22 = OpExtInst %void %1 DebugLexicalBlock %12 1 28 %18
+ OpLine %4 1 1
+ %main = OpFunction %void None %23
+ %24 = OpLabel
+ OpLine %4 1 17
+%param_var_foo = OpVariable %_ptr_Function_float Function
+ %27 = OpLoad %float %in_var_COLOR
+ OpLine %4 1 1
+ %28 = OpFunctionCall %void %src_main %param_var_foo
+ OpReturn
+ OpFunctionEnd
+ %src_main = OpFunction %void None %29
+ OpLine %4 1 17
+ %foo = OpFunctionParameter %_ptr_Function_float
+ %31 = OpExtInst %void %1 DebugDeclare %21 %foo %10
+ %bb_entry = OpLabel
+ OpLine %4 1 29
+ OpReturn
+ OpFunctionEnd
+)");
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugDeclare, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%null_expr = OpExtInst %void %DbgExt DebugExpression
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%foo = OpVariable %f32_ptr_function Function
+%decl = OpExtInst %void %DbgExt DebugDeclare )"
+ << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, ss.str(), extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugDeclare,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(R"(%dbg_src %foo %null_expr)", "Local Variable"),
+ std::make_pair(R"(%foo_info %void %null_expr)", "Variable"),
+ std::make_pair(R"(%foo_info %foo %dbg_src)", "Expression"),
+ }));
+
+TEST_F(ValidateVulkan100DebugInfo, DebugDeclare) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_10 = OpConstant %u32 10
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%null_expr = OpExtInst %void %DbgExt DebugExpression
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ const std::string body = R"(
+%foo = OpVariable %f32_ptr_function Function
+%decl = OpExtInst %void %DbgExt DebugDeclare %foo_info %foo %null_expr
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, constants, dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugDeclareParam) {
+ CompileSuccessfully(R"(
+ OpCapability Shader
+ OpExtension "SPV_KHR_non_semantic_info"
+ %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %in_var_COLOR
+ %4 = OpString "test.hlsl"
+ OpSource HLSL 620 %4 "#line 1 \"test.hlsl\"
+void main(float foo:COLOR) {}
+"
+ %11 = OpString "#line 1 \"test.hlsl\"
+void main(float foo:COLOR) {}
+"
+ %14 = OpString "float"
+ %17 = OpString "src.main"
+ %20 = OpString "foo"
+ OpName %in_var_COLOR "in.var.COLOR"
+ OpName %main "main"
+ OpName %param_var_foo "param.var.foo"
+ OpName %src_main "src.main"
+ OpName %foo "foo"
+ OpName %bb_entry "bb.entry"
+ OpDecorate %in_var_COLOR Location 0
+ %uint = OpTypeInt 32 0
+ %u32_0 = OpConstant %uint 0
+ %u32_1 = OpConstant %uint 1
+ %u32_2 = OpConstant %uint 2
+ %u32_3 = OpConstant %uint 3
+ %u32_4 = OpConstant %uint 4
+ %u32_5 = OpConstant %uint 5
+ %u32_10 = OpConstant %uint 10
+ %u32_17 = OpConstant %uint 17
+ %u32_28 = OpConstant %uint 28
+ %u32_32 = OpConstant %uint 32
+ %uint_32 = OpConstant %uint 32
+ %float = OpTypeFloat 32
+%_ptr_Input_float = OpTypePointer Input %float
+ %void = OpTypeVoid
+ %23 = OpTypeFunction %void
+%_ptr_Function_float = OpTypePointer Function %float
+ %29 = OpTypeFunction %void %_ptr_Function_float
+ OpLine %4 1 21
+%in_var_COLOR = OpVariable %_ptr_Input_float Input
+ %10 = OpExtInst %void %1 DebugExpression
+ %12 = OpExtInst %void %1 DebugSource %4 %11
+ %13 = OpExtInst %void %1 DebugCompilationUnit %u32_1 %u32_4 %12 %u32_5
+ %15 = OpExtInst %void %1 DebugTypeBasic %14 %uint_32 %u32_3 %u32_0
+ %16 = OpExtInst %void %1 DebugTypeFunction %u32_3 %void %15
+ %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 %u32_1 %u32_1 %13 %17 %u32_3 %u32_1
+ %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 %u32_1 %u32_17 %18 %u32_4 %u32_0
+ %22 = OpExtInst %void %1 DebugLexicalBlock %12 %u32_1 %u32_28 %18
+ %main = OpFunction %void None %23
+ %24 = OpLabel
+%param_var_foo = OpVariable %_ptr_Function_float Function
+ %27 = OpLoad %float %in_var_COLOR
+ %28 = OpFunctionCall %void %src_main %param_var_foo
+ OpReturn
+ OpFunctionEnd
+ %src_main = OpFunction %void None %29
+ %foo = OpFunctionParameter %_ptr_Function_float
+ %31 = OpExtInst %void %1 DebugDeclare %21 %foo %10
+ %bb_entry = OpLabel
+ OpReturn
+ OpFunctionEnd
+)");
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateVulkan100DebugInfoDebugDeclare, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_10 = OpConstant %u32 10
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%null_expr = OpExtInst %void %DbgExt DebugExpression
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%foo = OpVariable %f32_ptr_function Function
+%decl = OpExtInst %void %DbgExt DebugDeclare )"
+ << param.first;
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, constants, dbg_inst_header, ss.str(), extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugDeclare,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(R"(%dbg_src %foo %null_expr)", "Local Variable"),
+ std::make_pair(R"(%foo_info %void %null_expr)", "Variable"),
+ std::make_pair(R"(%foo_info %foo %dbg_src)", "Expression"),
+ }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugExpression) {
+ const std::string dbg_inst_header = R"(
+%op0 = OpExtInst %void %DbgExt DebugOperation Deref
+%op1 = OpExtInst %void %DbgExt DebugOperation Plus
+%null_expr = OpExtInst %void %DbgExt DebugExpression %op0 %op1
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header,
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugExpressionFail) {
+ const std::string dbg_inst_header = R"(
+%op = OpExtInst %void %DbgExt DebugOperation Deref
+%null_expr = OpExtInst %void %DbgExt DebugExpression %op %void
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header,
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "expected operand Operation must be a result id of DebugOperation"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugExpression) {
+ const std::string dbg_inst_header = R"(
+%op0 = OpExtInst %void %DbgExt DebugOperation %u32_0
+%op1 = OpExtInst %void %DbgExt DebugOperation %u32_1
+%null_expr = OpExtInst %void %DbgExt DebugExpression %op0 %op1
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header,
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugExpressionFail) {
+ const std::string dbg_inst_header = R"(
+%op = OpExtInst %void %DbgExt DebugOperation %u32_0
+%null_expr = OpExtInst %void %DbgExt DebugExpression %op %void
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header,
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "expected operand Operation must be a result id of DebugOperation"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplate) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "OpaqueType foo;
+main() {}
+"
+%float_name = OpString "float"
+%ty_name = OpString "Texture"
+%t_name = OpString "T"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic
+%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0
+%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateUsedForVariableType) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "OpaqueType foo;
+main() {}
+"
+%float_name = OpString "float"
+%ty_name = OpString "Texture"
+%t_name = OpString "T"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic
+%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0
+%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param
+%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %temp %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFunction) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "OpaqueType foo;
+main() {}
+"
+%float_name = OpString "float"
+%ty_name = OpString "Texture"
+%t_name = OpString "T"
+%main_name = OpString "main"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main
+%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %param
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFailTarget) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "OpaqueType foo;
+main() {}
+"
+%float_name = OpString "float"
+%ty_name = OpString "Texture"
+%t_name = OpString "T"
+%main_name = OpString "main"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0
+%temp = OpExtInst %void %DbgExt DebugTypeTemplate %float_info %param
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Target must be DebugTypeComposite or "
+ "DebugFunction"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFailParam) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "OpaqueType foo;
+main() {}
+"
+%float_name = OpString "float"
+%ty_name = OpString "Texture"
+%t_name = OpString "T"
+%main_name = OpString "main"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main
+%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %float_info
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "expected operand Parameters must be DebugTypeTemplateParameter or "
+ "DebugTypeTemplateTemplateParameter"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplate) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "OpaqueType foo;
+main() {}
+"
+%float_name = OpString "float"
+%ty_name = OpString "Texture"
+%t_name = OpString "T"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
+%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
+%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %ty_name %dbg_none %u32_3
+%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0
+%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param
+)";
+
+ 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, DebugTypeTemplateUsedForVariableType) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "OpaqueType foo;
+main() {}
+"
+%float_name = OpString "float"
+%ty_name = OpString "Texture"
+%t_name = OpString "T"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
+%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
+%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %ty_name %dbg_none %u32_3
+%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0
+%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param
+%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %temp %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3
+)";
+
+ 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, DebugTypeTemplateFunction) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "OpaqueType foo;
+main() {}
+"
+%float_name = OpString "float"
+%ty_name = OpString "Texture"
+%t_name = OpString "T"
+%main_name = OpString "main"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
+%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
+%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %param %param
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1
+%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %param
+)";
+
+ 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, DebugTypeTemplateFailTarget) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "OpaqueType foo;
+main() {}
+"
+%float_name = OpString "float"
+%ty_name = OpString "Texture"
+%t_name = OpString "T"
+%main_name = OpString "main"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
+%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
+%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0
+%temp = OpExtInst %void %DbgExt DebugTypeTemplate %float_info %param
+)";
+
+ 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 Target must be DebugTypeComposite or "
+ "DebugFunction"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeTemplateFailParam) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "OpaqueType foo;
+main() {}
+"
+%float_name = OpString "float"
+%ty_name = OpString "Texture"
+%t_name = OpString "T"
+%main_name = OpString "main"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
+%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
+%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %ty_name %dbg_none %u32_3
+%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_0 %u32_0
+%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %float_info
+)";
+
+ 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 Parameters must be DebugTypeTemplateParameter or "
+ "DebugTypeTemplateTemplateParameter"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariable) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float foo; void main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableStaticMember) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float foo; void main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Class %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsPublic %a
+%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src 0 0 %t %u32_0 %int_32 FlagIsPublic
+%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate %a
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableDebugInfoNone) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float foo; void main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbgNone = OpExtInst %void %DbgExt DebugInfoNone
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %dbgNone FlagIsProtected|FlagIsPrivate
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableConst) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float foo; void main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsProtected|FlagIsPrivate
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugGlobalVariable, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float foo; void main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo = OpExtInst %void %DbgExt DebugGlobalVariable )"
+ << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugGlobalVariable,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%void %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)",
+ "Name"),
+ std::make_pair(
+ R"(%foo_name %dbg_src %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)",
+ "Type"),
+ std::make_pair(
+ R"(%foo_name %float_info %comp_unit 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)",
+ "Source"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src 0 0 %dbg_src %foo_name %f32_input FlagIsProtected|FlagIsPrivate)",
+ "Scope"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %void %f32_input FlagIsProtected|FlagIsPrivate)",
+ "Linkage Name"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %void FlagIsProtected|FlagIsPrivate)",
+ "Variable"),
+ }));
+
+TEST_F(ValidateVulkan100DebugInfo, DebugGlobalVariable) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float foo; void main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3
+)";
+
+ 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, DebugGlobalVariableStaticMember) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float foo; void main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src %u32_0 %u32_0 %u32_0 %u32_32 %u32_3
+%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %u32_32 %u32_3 %a
+%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %t %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3
+)";
+
+ 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, DebugGlobalVariableDebugInfoNone) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float foo; void main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbgNone = OpExtInst %void %DbgExt DebugInfoNone
+%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
+%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %dbgNone %u32_3
+)";
+
+ 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, DebugGlobalVariableConst) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float foo; void main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %u32_32 %u32_3
+)";
+
+ 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_P(ValidateVulkan100DebugInfoDebugGlobalVariable, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float foo; void main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << 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
+%foo = OpExtInst %void %DbgExt DebugGlobalVariable )"
+ << param.first;
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, constants, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateVulkan100DebugInfoDebugGlobalVariable,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%void %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3)",
+ "Name"),
+ std::make_pair(
+ R"(%foo_name %dbg_src %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3)",
+ "Type"),
+ std::make_pair(
+ R"(%foo_name %float_info %comp_unit %u32_0 %u32_0 %comp_unit %foo_name %f32_input %u32_3)",
+ "Source"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src %u32_0 %u32_0 %dbg_src %foo_name %f32_input %u32_3)",
+ "Scope"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %void %f32_input %u32_3)",
+ "Linkage Name"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src %u32_0 %u32_0 %comp_unit %foo_name %void %u32_3)",
+ "Variable"),
+ }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAt) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%void_name = OpString "void"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main
+%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info
+%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %inlined_at
+)";
+
+ const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, "", dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAtFail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%void_name = OpString "void"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main
+%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info
+%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %inlined_at
+)";
+
+ const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, "", dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAtFail2) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%void_name = OpString "void"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main
+%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info
+%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %main_info
+)";
+
+ const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, "", dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAt) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%void_name = OpString "void"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1
+%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info
+%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info %inlined_at
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, constants, dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAtFail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%void_name = OpString "void"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1
+%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info
+%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %inlined_at %inlined_at
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, constants, dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugInlinedAtFail2) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%void_name = OpString "void"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1
+%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info
+%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt %u32_0 %main_info %main_info
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, constants, dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugValue) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_3 = OpConstant %u32 3
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%null_expr = OpExtInst %void %DbgExt DebugExpression
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0
+)";
+
+ const std::string body = R"(
+%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %int_3
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugValueWithVariableIndex) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%int_name = OpString "int"
+%foo_name = OpString "foo"
+%len_name = OpString "length"
+)";
+
+ const std::string size_const = R"(
+%int_3 = OpConstant %u32 3
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%null_expr = OpExtInst %void %DbgExt DebugExpression
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Signed
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal
+%len_info = OpExtInst %void %DbgExt DebugLocalVariable %len_name %int_info %dbg_src 0 0 %comp_unit FlagIsLocal
+)";
+
+ const std::string body = R"(
+%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %len_info
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugValue, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%null_expr = OpExtInst %void %DbgExt DebugExpression
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%decl = OpExtInst %void %DbgExt DebugValue )"
+ << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, ss.str(), extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugValue,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(R"(%dbg_src %int_32 %null_expr)", "Local Variable"),
+ std::make_pair(R"(%foo_info %int_32 %dbg_src)", "Expression"),
+ std::make_pair(R"(%foo_info %int_32 %null_expr %dbg_src)", "Indexes"),
+ }));
+
+TEST_F(ValidateVulkan100DebugInfo, DebugValue) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_10 = OpConstant %u32 10
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%null_expr = OpExtInst %void %DbgExt DebugExpression
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ const std::string body = R"(
+%value = OpExtInst %void %DbgExt DebugValue %foo_info %u32_32 %null_expr %u32_3
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, constants, dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugValueWithVariableIndex) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%int_name = OpString "int"
+%foo_name = OpString "foo"
+%len_name = OpString "length"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_10 = OpConstant %u32 10
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%null_expr = OpExtInst %void %DbgExt DebugExpression
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_32 %u32_4 %u32_0
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 %u32_0
+%len_info = OpExtInst %void %DbgExt DebugLocalVariable %len_name %int_info %dbg_src %u32_0 %u32_0 %comp_unit %u32_4
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ const std::string body = R"(
+%value = OpExtInst %void %DbgExt DebugValue %foo_info %u32_32 %null_expr %len_info
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, constants, dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateVulkan100DebugInfoDebugValue, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string constants = R"(
+%u32_4 = OpConstant %u32 4
+%u32_5 = OpConstant %u32 5
+%u32_10 = OpConstant %u32 10
+%u32_32 = OpConstant %u32 32
+)";
+
+ 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
+%null_expr = OpExtInst %void %DbgExt DebugExpression
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src %u32_1 %u32_10 %comp_unit %u32_4 %u32_0
+)";
+
+ const std::string extension = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%decl = OpExtInst %void %DbgExt DebugValue )"
+ << param.first;
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, constants, dbg_inst_header, ss.str(), extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateVulkan100DebugInfoDebugValue,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(R"(%dbg_src %u32_32 %null_expr %u32_3)",
+ "Local Variable"),
+ std::make_pair(R"(%foo_info %u32_32 %dbg_src %u32_3)", "Expression"),
+ std::make_pair(R"(%foo_info %u32_32 %null_expr %dbg_src)", "Indexes"),
+ }));
+
+TEST_F(ValidateVulkan100DebugInfo, VulkanDebugInfoSample) {
+ std::ostringstream ss;
+ ss << R"(
+ OpCapability Shader
+ OpExtension "SPV_KHR_non_semantic_info"
+ %id_1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %id_MainPs "MainPs" %id_in_var_TEXCOORD2 %id_out_var_SV_Target0
+ OpExecutionMode %id_MainPs OriginUpperLeft
+ %id_7 = OpString "foo.frag"
+ %id_27 = OpString "float"
+ %id_32 = OpString "vColor"
+ %id_36 = OpString "PS_OUTPUT"
+ %id_42 = OpString "vTextureCoords"
+ %id_46 = OpString "PS_INPUT"
+ %id_49 = OpString "MainPs"
+ %id_50 = OpString ""
+ %id_55 = OpString "ps_output"
+ %id_59 = OpString "i"
+ %id_63 = OpString "@type.sampler"
+ %id_64 = OpString "type.sampler"
+ %id_66 = OpString "g_sAniso"
+ %id_69 = OpString "@type.2d.image"
+ %id_70 = OpString "type.2d.image"
+ %id_72 = OpString "TemplateParam"
+ %id_75 = OpString "g_tColor"
+ OpName %id_type_2d_image "type.2d.image"
+ OpName %id_g_tColor "g_tColor"
+ OpName %id_type_sampler "type.sampler"
+ OpName %id_g_sAniso "g_sAniso"
+ OpName %id_in_var_TEXCOORD2 "in.var.TEXCOORD2"
+ OpName %id_out_var_SV_Target0 "out.var.SV_Target0"
+ OpName %id_MainPs "MainPs"
+ OpName %id_PS_INPUT "PS_INPUT"
+ OpMemberName %id_PS_INPUT 0 "vTextureCoords"
+ OpName %id_param_var_i "param.var.i"
+ OpName %id_PS_OUTPUT "PS_OUTPUT"
+ OpMemberName %id_PS_OUTPUT 0 "vColor"
+ OpName %id_src_MainPs "src.MainPs"
+ OpName %id_i "i"
+ OpName %id_bb_entry "bb.entry"
+ OpName %id_ps_output "ps_output"
+ OpName %id_type_sampled_image "type.sampled.image"
+ OpDecorate %id_in_var_TEXCOORD2 Location 0
+ OpDecorate %id_out_var_SV_Target0 Location 0
+ OpDecorate %id_g_tColor DescriptorSet 0
+ OpDecorate %id_g_tColor Binding 0
+ OpDecorate %id_g_sAniso DescriptorSet 0
+ OpDecorate %id_g_sAniso Binding 1
+ %id_int = OpTypeInt 32 1
+ %id_int_0 = OpConstant %id_int 0
+ %id_uint = OpTypeInt 32 0
+ %id_uint_32 = OpConstant %id_uint 32
+ %id_float = OpTypeFloat 32
+%id_type_2d_image = OpTypeImage %id_float 2D 2 0 0 1 Unknown
+%id__ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %id_type_2d_image
+%id_type_sampler = OpTypeSampler
+%id__ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %id_type_sampler
+ %id_v2float = OpTypeVector %id_float 2
+%id__ptr_Input_v2float = OpTypePointer Input %id_v2float
+ %id_v4float = OpTypeVector %id_float 4
+%id__ptr_Output_v4float = OpTypePointer Output %id_v4float
+ %id_void = OpTypeVoid
+ %id_uint_1 = OpConstant %id_uint 1
+ %id_uint_4 = OpConstant %id_uint 4
+ %id_uint_5 = OpConstant %id_uint 5
+ %id_uint_3 = OpConstant %id_uint 3
+ %id_uint_0 = OpConstant %id_uint 0
+ %id_uint_128 = OpConstant %id_uint 128
+ %id_uint_12 = OpConstant %id_uint 12
+ %id_uint_10 = OpConstant %id_uint 10
+ %id_uint_8 = OpConstant %id_uint 8
+ %id_uint_2 = OpConstant %id_uint 2
+ %id_uint_64 = OpConstant %id_uint 64
+ %id_uint_7 = OpConstant %id_uint 7
+ %id_uint_15 = OpConstant %id_uint 15
+ %id_uint_16 = OpConstant %id_uint 16
+ %id_uint_17 = OpConstant %id_uint 17
+ %id_uint_29 = OpConstant %id_uint 29
+ %id_uint_14 = OpConstant %id_uint 14
+ %id_uint_11 = OpConstant %id_uint 11
+ %id_78 = OpTypeFunction %id_void
+ %id_PS_INPUT = OpTypeStruct %id_v2float
+%id__ptr_Function_PS_INPUT = OpTypePointer Function %id_PS_INPUT
+ %id_PS_OUTPUT = OpTypeStruct %id_v4float
+ %id_89 = OpTypeFunction %id_PS_OUTPUT %id__ptr_Function_PS_INPUT
+%id__ptr_Function_PS_OUTPUT = OpTypePointer Function %id_PS_OUTPUT
+ %id_uint_20 = OpConstant %id_uint 20
+ %id_uint_19 = OpConstant %id_uint 19
+ %id_uint_26 = OpConstant %id_uint 26
+ %id_uint_46 = OpConstant %id_uint 46
+%id__ptr_Function_v2float = OpTypePointer Function %id_v2float
+ %id_uint_57 = OpConstant %id_uint 57
+ %id_uint_78 = OpConstant %id_uint 78
+%id_type_sampled_image = OpTypeSampledImage %id_type_2d_image
+ %id_uint_81 = OpConstant %id_uint 81
+%id__ptr_Function_v4float = OpTypePointer Function %id_v4float
+ %id_g_tColor = OpVariable %id__ptr_UniformConstant_type_2d_image UniformConstant
+ %id_g_sAniso = OpVariable %id__ptr_UniformConstant_type_sampler UniformConstant
+%id_in_var_TEXCOORD2 = OpVariable %id__ptr_Input_v2float Input
+%id_out_var_SV_Target0 = OpVariable %id__ptr_Output_v4float Output
+ %id_22 = OpExtInst %id_void %id_1 DebugSource %id_7
+ %id_23 = OpExtInst %id_void %id_1 DebugCompilationUnit %id_uint_1 %id_uint_4 %id_22 %id_uint_5
+ %id_28 = OpExtInst %id_void %id_1 DebugTypeBasic %id_27 %id_uint_32 %id_uint_3 %id_uint_0
+ %id_31 = OpExtInst %id_void %id_1 DebugTypeVector %id_28 %id_uint_4
+ %id_34 = OpExtInst %id_void %id_1 DebugTypeMember %id_32 %id_31 %id_22 %id_uint_12 %id_uint_12 %id_uint_0 %id_uint_128 %id_uint_3
+ %id_37 = OpExtInst %id_void %id_1 DebugTypeComposite %id_36 %id_uint_1 %id_22 %id_uint_10 %id_uint_8 %id_23 %id_36 %id_uint_128 %id_uint_3 %id_34
+ %id_40 = OpExtInst %id_void %id_1 DebugTypeVector %id_28 %id_uint_2
+ %id_44 = OpExtInst %id_void %id_1 DebugTypeMember %id_42 %id_40 %id_22 %id_uint_7 %id_uint_12 %id_uint_0 %id_uint_64 %id_uint_3
+ %id_47 = OpExtInst %id_void %id_1 DebugTypeComposite %id_46 %id_uint_1 %id_22 %id_uint_5 %id_uint_8 %id_23 %id_46 %id_uint_64 %id_uint_3 %id_44
+ %id_48 = OpExtInst %id_void %id_1 DebugTypeFunction %id_uint_3 %id_37 %id_47
+ %id_51 = OpExtInst %id_void %id_1 DebugFunction %id_49 %id_48 %id_22 %id_uint_15 %id_uint_1 %id_23 %id_50 %id_uint_3 %id_uint_16
+ %id_54 = OpExtInst %id_void %id_1 DebugLexicalBlock %id_22 %id_uint_16 %id_uint_1 %id_51
+ %id_56 = OpExtInst %id_void %id_1 DebugLocalVariable %id_55 %id_37 %id_22 %id_uint_17 %id_uint_15 %id_54 %id_uint_4
+ %id_58 = OpExtInst %id_void %id_1 DebugExpression
+ %id_60 = OpExtInst %id_void %id_1 DebugLocalVariable %id_59 %id_47 %id_22 %id_uint_15 %id_uint_29 %id_51 %id_uint_4 %id_uint_1
+ %id_62 = OpExtInst %id_void %id_1 DebugInfoNone
+ %id_65 = OpExtInst %id_void %id_1 DebugTypeComposite %id_63 %id_uint_1 %id_22 %id_uint_0 %id_uint_0 %id_23 %id_64 %id_62 %id_uint_3
+ %id_67 = OpExtInst %id_void %id_1 DebugGlobalVariable %id_66 %id_65 %id_22 %id_uint_3 %id_uint_14 %id_23 %id_66 %id_g_sAniso %id_uint_8
+ %id_71 = OpExtInst %id_void %id_1 DebugTypeComposite %id_69 %id_uint_0 %id_22 %id_uint_0 %id_uint_0 %id_23 %id_70 %id_62 %id_uint_3
+ %id_73 = OpExtInst %id_void %id_1 DebugTypeTemplateParameter %id_72 %id_31 %id_62 %id_22 %id_uint_0 %id_uint_0
+ %id_74 = OpExtInst %id_void %id_1 DebugTypeTemplate %id_71 %id_73
+ %id_76 = OpExtInst %id_void %id_1 DebugGlobalVariable %id_75 %id_74 %id_22 %id_uint_1 %id_uint_11 %id_23 %id_75 %id_g_tColor %id_uint_8
+ %id_MainPs = OpFunction %id_void None %id_78
+ %id_79 = OpLabel
+%id_param_var_i = OpVariable %id__ptr_Function_PS_INPUT Function
+ %id_83 = OpLoad %id_v2float %id_in_var_TEXCOORD2
+ %id_84 = OpCompositeConstruct %id_PS_INPUT %id_83
+ OpStore %id_param_var_i %id_84
+ %id_86 = OpFunctionCall %id_PS_OUTPUT %id_src_MainPs %id_param_var_i
+ %id_88 = OpCompositeExtract %id_v4float %id_86 0
+ OpStore %id_out_var_SV_Target0 %id_88
+ OpReturn
+ OpFunctionEnd
+ %id_src_MainPs = OpFunction %id_PS_OUTPUT None %id_89
+ %id_i = OpFunctionParameter %id__ptr_Function_PS_INPUT
+ %id_bb_entry = OpLabel
+ %id_ps_output = OpVariable %id__ptr_Function_PS_OUTPUT Function
+ %id_94 = OpExtInst %id_void %id_1 DebugScope %id_51
+ %id_97 = OpExtInst %id_void %id_1 DebugDeclare %id_60 %id_i %id_58
+ %id_99 = OpExtInst %id_void %id_1 DebugFunctionDefinition %id_51 %id_src_MainPs
+ %id_100 = OpExtInst %id_void %id_1 DebugScope %id_54
+ %id_102 = OpExtInst %id_void %id_1 DebugDeclare %id_56 %id_ps_output %id_58
+ %id_106 = OpLoad %id_type_2d_image %id_g_tColor
+ %id_109 = OpLoad %id_type_sampler %id_g_sAniso
+ %id_114 = OpAccessChain %id__ptr_Function_v2float %id_i %id_int_0
+ %id_115 = OpLoad %id_v2float %id_114
+ %id_119 = OpSampledImage %id_type_sampled_image %id_106 %id_109
+ %id_120 = OpImageSampleImplicitLod %id_v4float %id_119 %id_115 None
+ %id_123 = OpAccessChain %id__ptr_Function_v4float %id_ps_output %id_int_0
+ OpStore %id_123 %id_120
+ %id_125 = OpLoad %id_PS_OUTPUT %id_ps_output
+ OpReturnValue %id_125
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(ss.str());
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+} // namespace
+} // namespace val
+} // namespace spvtools
diff --git a/test/val/val_ext_inst_test.cpp b/test/val/val_ext_inst_test.cpp
index b73ec341..a2109db0 100644
--- a/test/val/val_ext_inst_test.cpp
+++ b/test/val/val_ext_inst_test.cpp
@@ -33,33 +33,6 @@ using ::testing::HasSubstr;
using ::testing::Not;
using ValidateExtInst = spvtest::ValidateBase<bool>;
-using ValidateOldDebugInfo = spvtest::ValidateBase<std::string>;
-using ValidateOpenCL100DebugInfo = spvtest::ValidateBase<std::string>;
-using ValidateLocalDebugInfoOutOfFunction = spvtest::ValidateBase<std::string>;
-using ValidateOpenCL100DebugInfoDebugTypedef =
- spvtest::ValidateBase<std::pair<std::string, std::string>>;
-using ValidateOpenCL100DebugInfoDebugTypeEnum =
- spvtest::ValidateBase<std::pair<std::string, std::string>>;
-using ValidateOpenCL100DebugInfoDebugTypeComposite =
- spvtest::ValidateBase<std::pair<std::string, std::string>>;
-using ValidateOpenCL100DebugInfoDebugTypeMember =
- spvtest::ValidateBase<std::pair<std::string, std::string>>;
-using ValidateOpenCL100DebugInfoDebugTypeInheritance =
- spvtest::ValidateBase<std::pair<std::string, std::string>>;
-using ValidateOpenCL100DebugInfoDebugFunction =
- spvtest::ValidateBase<std::pair<std::string, std::string>>;
-using ValidateOpenCL100DebugInfoDebugFunctionDeclaration =
- spvtest::ValidateBase<std::pair<std::string, std::string>>;
-using ValidateOpenCL100DebugInfoDebugLexicalBlock =
- spvtest::ValidateBase<std::pair<std::string, std::string>>;
-using ValidateOpenCL100DebugInfoDebugLocalVariable =
- spvtest::ValidateBase<std::pair<std::string, std::string>>;
-using ValidateOpenCL100DebugInfoDebugGlobalVariable =
- spvtest::ValidateBase<std::pair<std::string, std::string>>;
-using ValidateOpenCL100DebugInfoDebugDeclare =
- spvtest::ValidateBase<std::pair<std::string, std::string>>;
-using ValidateOpenCL100DebugInfoDebugValue =
- spvtest::ValidateBase<std::pair<std::string, std::string>>;
using ValidateGlslStd450SqrtLike = spvtest::ValidateBase<std::string>;
using ValidateGlslStd450FMinLike = spvtest::ValidateBase<std::string>;
using ValidateGlslStd450FClampLike = spvtest::ValidateBase<std::string>;
@@ -488,1685 +461,6 @@ OpFunctionEnd)";
return ss.str();
}
-std::string GenerateShaderCodeForDebugInfo(
- const std::string& op_string_instructions,
- const std::string& op_const_instructions,
- const std::string& debug_instructions_before_main, const std::string& body,
- const std::string& capabilities_and_extensions = "",
- const std::string& execution_model = "Fragment") {
- std::ostringstream ss;
- ss << R"(
-OpCapability Shader
-OpCapability Float16
-OpCapability Float64
-OpCapability Int16
-OpCapability Int64
-)";
-
- ss << capabilities_and_extensions;
- ss << "%extinst = OpExtInstImport \"GLSL.std.450\"\n";
- ss << "OpMemoryModel Logical GLSL450\n";
- ss << "OpEntryPoint " << execution_model << " %main \"main\""
- << " %f32_output"
- << " %f32vec2_output"
- << " %u32_output"
- << " %u32vec2_output"
- << " %u64_output"
- << " %f32_input"
- << " %f32vec2_input"
- << " %u32_input"
- << " %u32vec2_input"
- << " %u64_input"
- << "\n";
- if (execution_model == "Fragment") {
- ss << "OpExecutionMode %main OriginUpperLeft\n";
- }
-
- ss << op_string_instructions;
-
- ss << R"(
-%void = OpTypeVoid
-%func = OpTypeFunction %void
-%bool = OpTypeBool
-%f16 = OpTypeFloat 16
-%f32 = OpTypeFloat 32
-%f64 = OpTypeFloat 64
-%u32 = OpTypeInt 32 0
-%s32 = OpTypeInt 32 1
-%u64 = OpTypeInt 64 0
-%s64 = OpTypeInt 64 1
-%u16 = OpTypeInt 16 0
-%s16 = OpTypeInt 16 1
-%f32vec2 = OpTypeVector %f32 2
-%f32vec3 = OpTypeVector %f32 3
-%f32vec4 = OpTypeVector %f32 4
-%f64vec2 = OpTypeVector %f64 2
-%f64vec3 = OpTypeVector %f64 3
-%f64vec4 = OpTypeVector %f64 4
-%u32vec2 = OpTypeVector %u32 2
-%u32vec3 = OpTypeVector %u32 3
-%s32vec2 = OpTypeVector %s32 2
-%u32vec4 = OpTypeVector %u32 4
-%s32vec4 = OpTypeVector %s32 4
-%u64vec2 = OpTypeVector %u64 2
-%s64vec2 = OpTypeVector %s64 2
-%f64mat22 = OpTypeMatrix %f64vec2 2
-%f32mat22 = OpTypeMatrix %f32vec2 2
-%f32mat23 = OpTypeMatrix %f32vec2 3
-%f32mat32 = OpTypeMatrix %f32vec3 2
-%f32mat33 = OpTypeMatrix %f32vec3 3
-
-%f32_0 = OpConstant %f32 0
-%f32_1 = OpConstant %f32 1
-%f32_2 = OpConstant %f32 2
-%f32_3 = OpConstant %f32 3
-%f32_4 = OpConstant %f32 4
-%f32_h = OpConstant %f32 0.5
-%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1
-%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2
-%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2
-%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3
-%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3
-%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4
-
-%f64_0 = OpConstant %f64 0
-%f64_1 = OpConstant %f64 1
-%f64_2 = OpConstant %f64 2
-%f64_3 = OpConstant %f64 3
-%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1
-%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2
-%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3
-
-%f16_0 = OpConstant %f16 0
-%f16_1 = OpConstant %f16 1
-%f16_h = OpConstant %f16 0.5
-
-%u32_0 = OpConstant %u32 0
-%u32_1 = OpConstant %u32 1
-%u32_2 = OpConstant %u32 2
-%u32_3 = OpConstant %u32 3
-
-%s32_0 = OpConstant %s32 0
-%s32_1 = OpConstant %s32 1
-%s32_2 = OpConstant %s32 2
-%s32_3 = OpConstant %s32 3
-
-%u64_0 = OpConstant %u64 0
-%u64_1 = OpConstant %u64 1
-%u64_2 = OpConstant %u64 2
-%u64_3 = OpConstant %u64 3
-
-%s64_0 = OpConstant %s64 0
-%s64_1 = OpConstant %s64 1
-%s64_2 = OpConstant %s64 2
-%s64_3 = OpConstant %s64 3
-)";
-
- ss << op_const_instructions;
-
- ss << R"(
-%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1
-%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1
-
-%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2
-%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2
-
-%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3
-%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3
-
-%s64vec2_01 = OpConstantComposite %s64vec2 %s64_0 %s64_1
-%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1
-
-%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12
-%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12
-
-%f32_ptr_output = OpTypePointer Output %f32
-%f32vec2_ptr_output = OpTypePointer Output %f32vec2
-
-%u32_ptr_output = OpTypePointer Output %u32
-%u32vec2_ptr_output = OpTypePointer Output %u32vec2
-
-%u64_ptr_output = OpTypePointer Output %u64
-
-%f32_output = OpVariable %f32_ptr_output Output
-%f32vec2_output = OpVariable %f32vec2_ptr_output Output
-
-%u32_output = OpVariable %u32_ptr_output Output
-%u32vec2_output = OpVariable %u32vec2_ptr_output Output
-
-%u64_output = OpVariable %u64_ptr_output Output
-
-%f32_ptr_input = OpTypePointer Input %f32
-%f32vec2_ptr_input = OpTypePointer Input %f32vec2
-
-%u32_ptr_input = OpTypePointer Input %u32
-%u32vec2_ptr_input = OpTypePointer Input %u32vec2
-
-%u64_ptr_input = OpTypePointer Input %u64
-
-%f32_ptr_function = OpTypePointer Function %f32
-
-%f32_input = OpVariable %f32_ptr_input Input
-%f32vec2_input = OpVariable %f32vec2_ptr_input Input
-
-%u32_input = OpVariable %u32_ptr_input Input
-%u32vec2_input = OpVariable %u32vec2_ptr_input Input
-
-%u64_input = OpVariable %u64_ptr_input Input
-
-%u32_ptr_function = OpTypePointer Function %u32
-
-%struct_f16_u16 = OpTypeStruct %f16 %u16
-%struct_f32_f32 = OpTypeStruct %f32 %f32
-%struct_f32_f32_f32 = OpTypeStruct %f32 %f32 %f32
-%struct_f32_u32 = OpTypeStruct %f32 %u32
-%struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32
-%struct_u32_f32 = OpTypeStruct %u32 %f32
-%struct_u32_u32 = OpTypeStruct %u32 %u32
-%struct_f32_f64 = OpTypeStruct %f32 %f64
-%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2
-%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2
-)";
-
- ss << debug_instructions_before_main;
-
- ss << R"(
-%main = OpFunction %void None %func
-%main_entry = OpLabel
-)";
-
- ss << body;
-
- ss << R"(
-OpReturn
-OpFunctionEnd)";
-
- return ss.str();
-}
-
-TEST_F(ValidateOldDebugInfo, UseDebugInstructionOutOfFunction) {
- const std::string src = R"(
-%code = OpString "main() {}"
-)";
-
- const std::string dbg_inst = R"(
-%cu = OpExtInst %void %DbgExt DebugCompilationUnit %code 1 1
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "DebugInfo"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
- extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, UseDebugInstructionOutOfFunction) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-)";
-
- const std::string dbg_inst = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
- extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugSourceInFunction) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-)";
-
- const std::string dbg_inst = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst,
- extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Debug info extension instructions other than DebugScope, "
- "DebugNoScope, DebugDeclare, DebugValue must appear between "
- "section 9 (types, constants, global variables) and section 10 "
- "(function declarations)"));
-}
-
-TEST_P(ValidateLocalDebugInfoOutOfFunction, OpenCLDebugInfo100DebugScope) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "void main() {}"
-%void_name = OpString "void"
-%main_name = OpString "main"
-%main_linkage_name = OpString "v_main"
-%int_name = OpString "int"
-%foo_name = OpString "foo"
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_0 Signed
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main
-%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %int_info %dbg_src 1 1 %main_info FlagIsLocal
-%expr = OpExtInst %void %DbgExt DebugExpression
-)";
-
- const std::string body = R"(
-%foo = OpVariable %u32_ptr_function Function
-%foo_val = OpLoad %u32 %foo
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, "", dbg_inst_header + GetParam(), body, extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue "
- "of debug info extension must appear in a function "
- "body"));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- AllLocalDebugInfo, ValidateLocalDebugInfoOutOfFunction,
- ::testing::ValuesIn(std::vector<std::string>{
- "%main_scope = OpExtInst %void %DbgExt DebugScope %main_info",
- "%no_scope = OpExtInst %void %DbgExt DebugNoScope",
- }));
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionForwardReference) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "void main() {}"
-%void_name = OpString "void"
-%main_name = OpString "main"
-%main_linkage_name = OpString "v_main"
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main
-)";
-
- const std::string body = R"(
-%main_scope = OpExtInst %void %DbgExt DebugScope %main_info
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, "", dbg_inst_header, body, extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionMissingOpFunction) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "void main() {}"
-%void_name = OpString "void"
-%main_name = OpString "main"
-%main_linkage_name = OpString "v_main"
-)";
-
- const std::string dbg_inst_header = R"(
-%dbgNone = OpExtInst %void %DbgExt DebugInfoNone
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %dbgNone
-)";
-
- const std::string body = R"(
-%main_scope = OpExtInst %void %DbgExt DebugScope %main_info
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, "", dbg_inst_header, body, extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugScopeBeforeOpVariableInFunction) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "float4 main(float arg) {
- float foo;
- return float4(0, 0, 0, 0);
-}
-"
-%float_name = OpString "float"
-%main_name = OpString "main"
-%main_linkage_name = OpString "v4f_main_f"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main
-)";
-
- const std::string body = R"(
-%main_scope = OpExtInst %void %DbgExt DebugScope %main_info
-%foo = OpVariable %f32_ptr_function Function
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, body, extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeSizeDebugInfoNone) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "OpaqueType foo;
-main() {}
-"
-%ty_name = OpString "struct VS_OUTPUT"
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header,
- "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeForwardReference) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "struct VS_OUTPUT {
- float4 pos : SV_POSITION;
- float4 color : COLOR;
-};
-main() {}
-"
-%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
-%float_name = OpString "float"
-%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
-%VS_OUTPUT_color_name = OpString "color : COLOR"
-%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-%int_128 = OpConstant %u32 128
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
-%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
-%VS_OUTPUT_color_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_color_name %v4float_info %dbg_src 3 3 %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeMissingReference) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "struct VS_OUTPUT {
- float4 pos : SV_POSITION;
- float4 color : COLOR;
-};
-main() {}
-"
-%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
-%float_name = OpString "float"
-%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
-%VS_OUTPUT_color_name = OpString "color : COLOR"
-%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-%int_128 = OpConstant %u32 128
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
-%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("forward referenced IDs have not been defined"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugInstructionWrongResultType) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-)";
-
- const std::string dbg_inst = R"(
-%dbg_src = OpExtInst %bool %DbgExt DebugSource %src %code
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
- extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected result type must be a result id of "
- "OpTypeVoid"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnit) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-)";
-
- const std::string dbg_inst = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
- extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnitFail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-)";
-
- const std::string dbg_inst = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %src HLSL
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
- extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand Source must be a result id of "
- "DebugSource"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugSourceFailFile) {
- const std::string src = R"(
-%code = OpString "main() {}"
-)";
-
- const std::string dbg_inst = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %DbgExt %code
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
- extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand File must be a result id of "
- "OpString"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugSourceFailSource) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-)";
-
- const std::string dbg_inst = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %DbgExt
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
- extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand Text must be a result id of "
- "OpString"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugSourceNoText) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-)";
-
- const std::string dbg_inst = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
- extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailName) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "float4 main(float arg) {
- float foo;
- return float4(0, 0, 0, 0);
-}
-"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %int_32 %int_32 Float
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand Name must be a result id of "
- "OpString"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailSize) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "float4 main(float arg) {
- float foo;
- return float4(0, 0, 0, 0);
-}
-"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %float_name Float
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand Size must be a result id of "
- "OpConstant"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointer) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "float4 main(float arg) {
- float foo;
- return float4(0, 0, 0, 0);
-}
-"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %float_info Function FlagIsLocal
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointerFail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "float4 main(float arg) {
- float foo;
- return float4(0, 0, 0, 0);
-}
-"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %dbg_src Function FlagIsLocal
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand Base Type must be a result id of "
- "DebugTypeBasic"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifier) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "float4 main(float arg) {
- float foo;
- return float4(0, 0, 0, 0);
-}
-"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %float_info ConstType
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifierFail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "float4 main(float arg) {
- float foo;
- return float4(0, 0, 0, 0);
-}
-"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %comp_unit ConstType
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand Base Type must be a result id of "
- "DebugTypeBasic"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArray) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %int_32
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayWithVariableSize) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%float_name = OpString "float"
-%int_name = OpString "int"
-%main_name = OpString "main"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Unsigned
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main
-%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %uint_info %dbg_src 1 1 %main_info FlagIsLocal
-%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailBaseType) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %comp_unit %int_32
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand Base Type is not a valid debug "
- "type"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCount) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %float_info
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, 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"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountFloat) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %f32_4
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, 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"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountZero) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_0
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, 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"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%float_name = OpString "float"
-%main_name = OpString "main"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main
-%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 1 %main_info FlagIsLocal
-%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, 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"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVector) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 4
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand Base Type must be a result id of "
- "DebugTypeBasic"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentZero) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 0
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand Base Type must be a result id of "
- "DebugTypeBasic"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentFive) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 5
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand Base Type must be a result id of "
- "DebugTypeBasic"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypedef) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%foo_info = OpExtInst %void %DbgExt DebugTypedef %foo_name %float_info %dbg_src 1 1 %comp_unit
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_P(ValidateOpenCL100DebugInfoDebugTypedef, Fail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const auto& param = GetParam();
-
- std::ostringstream ss;
- ss << R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%foo_info = OpExtInst %void %DbgExt DebugTypedef )";
- ss << param.first;
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
- "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand " + param.second +
- " must be a result id of "));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypedef,
- ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
- std::make_pair(R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit)",
- "Name"),
- std::make_pair(R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit)",
- "Base Type"),
- std::make_pair(R"(%foo_name %float_info %comp_unit 1 1 %comp_unit)",
- "Source"),
- std::make_pair(R"(%foo_name %float_info %dbg_src 1 1 %dbg_src)",
- "Parent"),
- }));
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunction) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%main_name = OpString "main"
-%main_linkage_name = OpString "v_main"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%main_type_info1 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
-%main_type_info2 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info
-%main_type_info3 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %float_info
-%main_type_info4 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void %float_info %float_info
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailReturn) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%main_name = OpString "main"
-%main_linkage_name = OpString "v_main"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %dbg_src %float_info
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("expected operand Return Type is not a valid debug type"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailParam) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%main_name = OpString "main"
-%main_linkage_name = OpString "v_main"
-%float_name = OpString "float"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %void
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("expected operand Parameter Types is not a valid debug type"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeEnum) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%none = OpExtInst %void %DbgExt DebugInfoNone
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%foo_info1 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name
-%foo_info2 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name
-%foo_info3 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_P(ValidateOpenCL100DebugInfoDebugTypeEnum, Fail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const auto& param = GetParam();
-
- std::ostringstream ss;
- ss << R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%foo_info = OpExtInst %void %DbgExt DebugTypeEnum )";
- ss << param.first;
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
- "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand " + param.second));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeEnum,
- ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
- std::make_pair(
- R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)",
- "Name"),
- std::make_pair(
- R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)",
- "Underlying Types"),
- std::make_pair(
- R"(%foo_name %float_info %comp_unit 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)",
- "Source"),
- std::make_pair(
- R"(%foo_name %float_info %dbg_src 1 1 %dbg_src %int_32 FlagIsPublic %u32_0 %foo_name)",
- "Parent"),
- std::make_pair(
- R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %void FlagIsPublic %u32_0 %foo_name)",
- "Size"),
- std::make_pair(
- R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %u32_0 FlagIsPublic %u32_0 %foo_name)",
- "Size"),
- std::make_pair(
- R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %foo_name %foo_name)",
- "Value"),
- std::make_pair(
- R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %u32_1)",
- "Name"),
- }));
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeFunctionAndInheritance) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "struct VS_OUTPUT {
- float4 pos : SV_POSITION;
-};
-struct foo : VS_OUTPUT {
-};
-main() {}
-"
-%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
-%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
-%main_name = OpString "main"
-%main_linkage_name = OpString "v4f_main_f"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-%int_128 = OpConstant %u32 128
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
-%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main
-%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
-%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_P(ValidateOpenCL100DebugInfoDebugTypeComposite, Fail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "struct VS_OUTPUT {
- float4 pos : SV_POSITION;
-};
-struct foo : VS_OUTPUT {
-};
-main() {}
-"
-%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
-%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
-%main_name = OpString "main"
-%main_linkage_name = OpString "v4f_main_f"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-%int_128 = OpConstant %u32 128
-)";
-
- const auto& param = GetParam();
-
- std::ostringstream ss;
- ss << R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite )";
- ss << param.first;
- ss << R"(
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
-%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main
-%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
-%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
- "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand " + param.second + " must be "));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeComposite,
- ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
- std::make_pair(
- R"(%dbg_src Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
- "Name"),
- std::make_pair(
- R"(%VS_OUTPUT_name Structure %comp_unit 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
- "Source"),
- std::make_pair(
- R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %dbg_src %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
- "Parent"),
- std::make_pair(
- R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %int_128 %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
- "Linkage Name"),
- std::make_pair(
- R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %dbg_src FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
- "Size"),
- std::make_pair(
- R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %dbg_src %main_info %child)",
- "Members"),
- std::make_pair(
- R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %dbg_src %child)",
- "Members"),
- std::make_pair(
- R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %dbg_src)",
- "Members"),
- }));
-
-TEST_P(ValidateOpenCL100DebugInfoDebugTypeMember, Fail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "struct VS_OUTPUT {
- float pos : SV_POSITION;
-};
-main() {}
-"
-%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
-%float_name = OpString "float"
-%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
-%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const auto& param = GetParam();
-
- std::ostringstream ss;
- ss << R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_32 FlagIsPublic %VS_OUTPUT_pos_info
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember )";
- ss << param.first;
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
- "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- if (!param.second.empty()) {
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand " + param.second +
- " must be a result id of "));
- }
-}
-
-INSTANTIATE_TEST_SUITE_P(
- AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeMember,
- ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
- std::make_pair(
- R"(%dbg_src %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)",
- "Name"),
- std::make_pair(
- R"(%VS_OUTPUT_pos_name %dbg_src %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)",
- ""),
- std::make_pair(
- R"(%VS_OUTPUT_pos_name %float_info %float_info 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)",
- "Source"),
- std::make_pair(
- R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %float_info %u32_0 %int_32 FlagIsPublic)",
- "Parent"),
- std::make_pair(
- R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %void %int_32 FlagIsPublic)",
- "Offset"),
- std::make_pair(
- R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %void FlagIsPublic)",
- "Size"),
- }));
-
-TEST_P(ValidateOpenCL100DebugInfoDebugTypeInheritance, Fail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "struct VS_OUTPUT {};
-struct foo : VS_OUTPUT {};
-"
-%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
-%foo_name = OpString "foo"
-)";
-
- const auto& param = GetParam();
-
- std::ostringstream ss;
- ss << R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_name %u32_0 FlagIsPublic %child
-%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
-%bar_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Union %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
-%child = OpExtInst %void %DbgExt DebugTypeInheritance )"
- << param.first;
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "",
- extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand " + param.second));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeInheritance,
- ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
- std::make_pair(R"(%dbg_src %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)",
- "Child must be a result id of"),
- std::make_pair(R"(%foo_info %dbg_src %u32_0 %u32_0 FlagIsPublic)",
- "Parent must be a result id of"),
- std::make_pair(
- R"(%bar_info %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)",
- "Child must be class or struct debug type"),
- std::make_pair(R"(%foo_info %bar_info %u32_0 %u32_0 FlagIsPublic)",
- "Parent must be class or struct debug type"),
- std::make_pair(R"(%foo_info %VS_OUTPUT_info %void %u32_0 FlagIsPublic)",
- "Offset"),
- std::make_pair(R"(%foo_info %VS_OUTPUT_info %u32_0 %void FlagIsPublic)",
- "Size"),
- }));
TEST_P(ValidateGlslStd450SqrtLike, Success) {
const std::string ext_inst_name = GetParam();
std::ostringstream ss;
@@ -2178,1086 +472,6 @@ TEST_P(ValidateGlslStd450SqrtLike, Success) {
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
-TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionDeclaration) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "struct VS_OUTPUT {
- float4 pos : SV_POSITION;
-};
-main() {}
-"
-%main_name = OpString "main"
-%main_linkage_name = OpString "v4f_main_f"
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
-%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header,
- "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_P(ValidateOpenCL100DebugInfoDebugFunction, Fail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "struct VS_OUTPUT {
- float4 pos : SV_POSITION;
-};
-main() {}
-"
-%main_name = OpString "main"
-%main_linkage_name = OpString "v4f_main_f"
-)";
-
- const auto& param = GetParam();
-
- std::ostringstream ss;
- ss << R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
-%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic
-%main_info = OpExtInst %void %DbgExt DebugFunction )"
- << param.first;
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "",
- extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand " + param.second));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugFunction,
- ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
- std::make_pair(
- R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)",
- "Name"),
- std::make_pair(
- R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)",
- "Type"),
- std::make_pair(
- R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)",
- "Source"),
- std::make_pair(
- R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic 13 %main)",
- "Parent"),
- std::make_pair(
- R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic 13 %main)",
- "Linkage Name"),
- std::make_pair(
- R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %void)",
- "Function"),
- std::make_pair(
- R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main %dbg_src)",
- "Declaration"),
- }));
-
-TEST_P(ValidateOpenCL100DebugInfoDebugFunctionDeclaration, Fail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "struct VS_OUTPUT {
- float4 pos : SV_POSITION;
-};
-main() {}
-"
-%main_name = OpString "main"
-%main_linkage_name = OpString "v4f_main_f"
-)";
-
- const auto& param = GetParam();
-
- std::ostringstream ss;
- ss << R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
-%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration )"
- << param.first;
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "",
- extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand " + param.second));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- AllOpenCL100DebugInfoFail,
- ValidateOpenCL100DebugInfoDebugFunctionDeclaration,
- ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
- std::make_pair(
- R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)",
- "Name"),
- std::make_pair(
- R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)",
- "Type"),
- std::make_pair(
- R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic)",
- "Source"),
- std::make_pair(
- R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic)",
- "Parent"),
- std::make_pair(
- R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic)",
- "Linkage Name"),
- }));
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugLexicalBlock) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%main_name = OpString "main"
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%main_block = OpExtInst %void %DbgExt DebugLexicalBlock %dbg_src 1 1 %comp_unit %main_name)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header,
- "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_P(ValidateOpenCL100DebugInfoDebugLexicalBlock, Fail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "main() {}"
-%main_name = OpString "main"
-)";
-
- const auto& param = GetParam();
-
- std::ostringstream ss;
- ss << R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%main_block = OpExtInst %void %DbgExt DebugLexicalBlock )"
- << param.first;
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "",
- extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand " + param.second));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLexicalBlock,
- ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
- std::make_pair(R"(%comp_unit 1 1 %comp_unit %main_name)", "Source"),
- std::make_pair(R"(%dbg_src 1 1 %dbg_src %main_name)", "Parent"),
- std::make_pair(R"(%dbg_src 1 1 %comp_unit %void)", "Name"),
- }));
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailScope) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "void main() {}"
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-)";
-
- const std::string body = R"(
-%main_scope = OpExtInst %void %DbgExt DebugScope %dbg_src
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, "", dbg_inst_header, body, extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailInlinedAt) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "void main() {}"
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-)";
-
- const std::string body = R"(
-%main_scope = OpExtInst %void %DbgExt DebugScope %comp_unit %dbg_src
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, "", dbg_inst_header, body, extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined At"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugLocalVariable) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "void main() { float foo; }"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%foo = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_P(ValidateOpenCL100DebugInfoDebugLocalVariable, Fail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "void main() { float foo; }"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const auto& param = GetParam();
-
- std::ostringstream ss;
- ss << R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%foo = OpExtInst %void %DbgExt DebugLocalVariable )"
- << param.first;
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
- "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand " + param.second));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLocalVariable,
- ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
- std::make_pair(
- R"(%void %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0)",
- "Name"),
- std::make_pair(
- R"(%foo_name %dbg_src %dbg_src 1 10 %comp_unit FlagIsLocal 0)",
- "Type"),
- std::make_pair(
- R"(%foo_name %float_info %comp_unit 1 10 %comp_unit FlagIsLocal 0)",
- "Source"),
- std::make_pair(
- R"(%foo_name %float_info %dbg_src 1 10 %dbg_src FlagIsLocal 0)",
- "Parent"),
- }));
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugDeclare) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "void main() { float foo; }"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%null_expr = OpExtInst %void %DbgExt DebugExpression
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0
-)";
-
- const std::string body = R"(
-%foo = OpVariable %f32_ptr_function Function
-%decl = OpExtInst %void %DbgExt DebugDeclare %foo_info %foo %null_expr
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, body, extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugDeclareParam) {
- CompileSuccessfully(R"(
- OpCapability Shader
- %1 = OpExtInstImport "OpenCL.DebugInfo.100"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Vertex %main "main" %in_var_COLOR
- %4 = OpString "test.hlsl"
- OpSource HLSL 620 %4 "#line 1 \"test.hlsl\"
-void main(float foo:COLOR) {}
-"
- %11 = OpString "#line 1 \"test.hlsl\"
-void main(float foo:COLOR) {}
-"
- %14 = OpString "float"
- %17 = OpString "src.main"
- %20 = OpString "foo"
- OpName %in_var_COLOR "in.var.COLOR"
- OpName %main "main"
- OpName %param_var_foo "param.var.foo"
- OpName %src_main "src.main"
- OpName %foo "foo"
- OpName %bb_entry "bb.entry"
- OpDecorate %in_var_COLOR Location 0
- %uint = OpTypeInt 32 0
- %uint_32 = OpConstant %uint 32
- %float = OpTypeFloat 32
-%_ptr_Input_float = OpTypePointer Input %float
- %void = OpTypeVoid
- %23 = OpTypeFunction %void
-%_ptr_Function_float = OpTypePointer Function %float
- %29 = OpTypeFunction %void %_ptr_Function_float
- OpLine %4 1 21
-%in_var_COLOR = OpVariable %_ptr_Input_float Input
- %10 = OpExtInst %void %1 DebugExpression
- %12 = OpExtInst %void %1 DebugSource %4 %11
- %13 = OpExtInst %void %1 DebugCompilationUnit 1 4 %12 HLSL
- %15 = OpExtInst %void %1 DebugTypeBasic %14 %uint_32 Float
- %16 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void %15
- %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 1 1 %13 %17 FlagIsProtected|FlagIsPrivate 1 %src_main
- %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 1 17 %18 FlagIsLocal 0
- %22 = OpExtInst %void %1 DebugLexicalBlock %12 1 28 %18
- OpLine %4 1 1
- %main = OpFunction %void None %23
- %24 = OpLabel
- OpLine %4 1 17
-%param_var_foo = OpVariable %_ptr_Function_float Function
- %27 = OpLoad %float %in_var_COLOR
- OpLine %4 1 1
- %28 = OpFunctionCall %void %src_main %param_var_foo
- OpReturn
- OpFunctionEnd
- %src_main = OpFunction %void None %29
- OpLine %4 1 17
- %foo = OpFunctionParameter %_ptr_Function_float
- %31 = OpExtInst %void %1 DebugDeclare %21 %foo %10
- %bb_entry = OpLabel
- OpLine %4 1 29
- OpReturn
- OpFunctionEnd
-)");
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_P(ValidateOpenCL100DebugInfoDebugDeclare, Fail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "void main() { float foo; }"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%null_expr = OpExtInst %void %DbgExt DebugExpression
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0
-)";
-
- const auto& param = GetParam();
-
- std::ostringstream ss;
- ss << R"(
-%foo = OpVariable %f32_ptr_function Function
-%decl = OpExtInst %void %DbgExt DebugDeclare )"
- << param.first;
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, ss.str(), extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand " + param.second));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugDeclare,
- ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
- std::make_pair(R"(%dbg_src %foo %null_expr)", "Local Variable"),
- std::make_pair(R"(%foo_info %void %null_expr)", "Variable"),
- std::make_pair(R"(%foo_info %foo %dbg_src)", "Expression"),
- }));
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugExpression) {
- const std::string dbg_inst_header = R"(
-%op0 = OpExtInst %void %DbgExt DebugOperation Deref
-%op1 = OpExtInst %void %DbgExt DebugOperation Plus
-%null_expr = OpExtInst %void %DbgExt DebugExpression %op0 %op1
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header,
- "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugExpressionFail) {
- const std::string dbg_inst_header = R"(
-%op = OpExtInst %void %DbgExt DebugOperation Deref
-%null_expr = OpExtInst %void %DbgExt DebugExpression %op %void
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header,
- "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "expected operand Operation must be a result id of DebugOperation"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplate) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "OpaqueType foo;
-main() {}
-"
-%float_name = OpString "float"
-%ty_name = OpString "Texture"
-%t_name = OpString "T"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-%int_128 = OpConstant %u32 128
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic
-%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0
-%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateUsedForVariableType) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "OpaqueType foo;
-main() {}
-"
-%float_name = OpString "float"
-%ty_name = OpString "Texture"
-%t_name = OpString "T"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-%int_128 = OpConstant %u32 128
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic
-%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0
-%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param
-%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %temp %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFunction) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "OpaqueType foo;
-main() {}
-"
-%float_name = OpString "float"
-%ty_name = OpString "Texture"
-%t_name = OpString "T"
-%main_name = OpString "main"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-%int_128 = OpConstant %u32 128
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main
-%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %param
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFailTarget) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "OpaqueType foo;
-main() {}
-"
-%float_name = OpString "float"
-%ty_name = OpString "Texture"
-%t_name = OpString "T"
-%main_name = OpString "main"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-%int_128 = OpConstant %u32 128
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0
-%temp = OpExtInst %void %DbgExt DebugTypeTemplate %float_info %param
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand Target must be DebugTypeComposite or "
- "DebugFunction"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFailParam) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "OpaqueType foo;
-main() {}
-"
-%float_name = OpString "float"
-%ty_name = OpString "Texture"
-%t_name = OpString "T"
-%main_name = OpString "main"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-%int_128 = OpConstant %u32 128
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main
-%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %float_info
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr(
- "expected operand Parameters must be DebugTypeTemplateParameter or "
- "DebugTypeTemplateTemplateParameter"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariable) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "float foo; void main() {}"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableStaticMember) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "float foo; void main() {}"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Class %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsPublic %a
-%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src 0 0 %t %u32_0 %int_32 FlagIsPublic
-%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate %a
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableDebugInfoNone) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "float foo; void main() {}"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbgNone = OpExtInst %void %DbgExt DebugInfoNone
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %dbgNone FlagIsProtected|FlagIsPrivate
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableConst) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "float foo; void main() {}"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsProtected|FlagIsPrivate
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, "", extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_P(ValidateOpenCL100DebugInfoDebugGlobalVariable, Fail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "float foo; void main() {}"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const auto& param = GetParam();
-
- std::ostringstream ss;
- ss << R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%foo = OpExtInst %void %DbgExt DebugGlobalVariable )"
- << param.first;
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
- "", extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand " + param.second));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugGlobalVariable,
- ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
- std::make_pair(
- R"(%void %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)",
- "Name"),
- std::make_pair(
- R"(%foo_name %dbg_src %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)",
- "Type"),
- std::make_pair(
- R"(%foo_name %float_info %comp_unit 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)",
- "Source"),
- std::make_pair(
- R"(%foo_name %float_info %dbg_src 0 0 %dbg_src %foo_name %f32_input FlagIsProtected|FlagIsPrivate)",
- "Scope"),
- std::make_pair(
- R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %void %f32_input FlagIsProtected|FlagIsPrivate)",
- "Linkage Name"),
- std::make_pair(
- R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %void FlagIsProtected|FlagIsPrivate)",
- "Variable"),
- }));
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAt) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "void main() {}"
-%void_name = OpString "void"
-%main_name = OpString "main"
-%main_linkage_name = OpString "v_main"
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main
-%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info
-%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %inlined_at
-)";
-
- const std::string body = R"(
-%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, "", dbg_inst_header, body, extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAtFail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "void main() {}"
-%void_name = OpString "void"
-%main_name = OpString "main"
-%main_linkage_name = OpString "v_main"
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main
-%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info
-%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %inlined_at
-)";
-
- const std::string body = R"(
-%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, "", dbg_inst_header, body, extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAtFail2) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "void main() {}"
-%void_name = OpString "void"
-%main_name = OpString "main"
-%main_linkage_name = OpString "v_main"
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main
-%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info
-%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %main_info
-)";
-
- const std::string body = R"(
-%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, "", dbg_inst_header, body, extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined"));
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugValue) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "void main() { float foo; }"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_3 = OpConstant %u32 3
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%null_expr = OpExtInst %void %DbgExt DebugExpression
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
-%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0
-)";
-
- const std::string body = R"(
-%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %int_3
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, body, extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateOpenCL100DebugInfo, DebugValueWithVariableIndex) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "void main() { float foo; }"
-%float_name = OpString "float"
-%int_name = OpString "int"
-%foo_name = OpString "foo"
-%len_name = OpString "length"
-)";
-
- const std::string size_const = R"(
-%int_3 = OpConstant %u32 3
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%null_expr = OpExtInst %void %DbgExt DebugExpression
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Signed
-%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
-%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal
-%len_info = OpExtInst %void %DbgExt DebugLocalVariable %len_name %int_info %dbg_src 0 0 %comp_unit FlagIsLocal
-)";
-
- const std::string body = R"(
-%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %len_info
-)";
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, body, extension, "Vertex"));
- ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_P(ValidateOpenCL100DebugInfoDebugValue, Fail) {
- const std::string src = R"(
-%src = OpString "simple.hlsl"
-%code = OpString "void main() { float foo; }"
-%float_name = OpString "float"
-%foo_name = OpString "foo"
-)";
-
- const std::string size_const = R"(
-%int_32 = OpConstant %u32 32
-)";
-
- const std::string dbg_inst_header = R"(
-%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
-%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
-%null_expr = OpExtInst %void %DbgExt DebugExpression
-%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
-%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0
-)";
-
- const auto& param = GetParam();
-
- std::ostringstream ss;
- ss << R"(
-%decl = OpExtInst %void %DbgExt DebugValue )"
- << param.first;
-
- const std::string extension = R"(
-%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
-)";
-
- CompileSuccessfully(GenerateShaderCodeForDebugInfo(
- src, size_const, dbg_inst_header, ss.str(), extension, "Vertex"));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected operand " + param.second));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugValue,
- ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
- std::make_pair(R"(%dbg_src %int_32 %null_expr)", "Local Variable"),
- std::make_pair(R"(%foo_info %int_32 %dbg_src)", "Expression"),
- std::make_pair(R"(%foo_info %int_32 %null_expr %dbg_src)", "Indexes"),
- }));
-
TEST_P(ValidateGlslStd450SqrtLike, IntResultType) {
const std::string ext_inst_name = GetParam();
const std::string body =
@@ -3507,6 +721,19 @@ TEST_P(ValidateGlslStd450SAbsLike, WrongBitWidthOperand) {
"Result Type"));
}
+TEST_P(ValidateGlslStd450SAbsLike, TypelessOperand) {
+ const std::string ext_inst_name = GetParam();
+ const std::string body =
+ "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + " %main_entry\n";
+
+ CompileSuccessfully(GenerateShaderCode(body));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("GLSL.std.450 " + ext_inst_name +
+ ": expected all operands to be int scalars or vectors"));
+}
+
INSTANTIATE_TEST_SUITE_P(AllSAbsLike, ValidateGlslStd450SAbsLike,
::testing::ValuesIn(std::vector<std::string>{
"SAbs",
@@ -3656,6 +883,19 @@ TEST_P(ValidateGlslStd450UMinLike, WrongBitWidthOperand2) {
"Result Type"));
}
+TEST_P(ValidateGlslStd450UMinLike, TypelessOperand) {
+ const std::string ext_inst_name = GetParam();
+ const std::string body = "%val1 = OpExtInst %s64 %extinst " + ext_inst_name +
+ " %s64_0 %main_entry\n";
+
+ CompileSuccessfully(GenerateShaderCode(body));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("GLSL.std.450 " + ext_inst_name +
+ ": expected all operands to be int scalars or vectors"));
+}
+
INSTANTIATE_TEST_SUITE_P(AllUMinLike, ValidateGlslStd450UMinLike,
::testing::ValuesIn(std::vector<std::string>{
"UMin",
@@ -3819,6 +1059,19 @@ TEST_P(ValidateGlslStd450UClampLike, WrongBitWidthOperand3) {
"Result Type"));
}
+TEST_P(ValidateGlslStd450UClampLike, TypelessOperand) {
+ const std::string ext_inst_name = GetParam();
+ const std::string body = "%val1 = OpExtInst %s64 %extinst " + ext_inst_name +
+ " %main_entry %s64_0 %s64_0\n";
+
+ CompileSuccessfully(GenerateShaderCode(body));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("GLSL.std.450 " + ext_inst_name +
+ ": expected all operands to be int scalars or vectors"));
+}
+
INSTANTIATE_TEST_SUITE_P(AllUClampLike, ValidateGlslStd450UClampLike,
::testing::ValuesIn(std::vector<std::string>{
"UClamp",
@@ -4327,6 +1580,19 @@ TEST_F(ValidateExtInst, GlslStd450LdexpExpWrongSize) {
"number as Result Type"));
}
+TEST_F(ValidateExtInst, GlslStd450LdexpExpNoType) {
+ const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Ldexp %f32_1 %main_entry
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("GLSL.std.450 Ldexp: "
+ "expected operand Exp to be a 32-bit int scalar "
+ "or vector type"));
+}
+
TEST_F(ValidateExtInst, GlslStd450FrexpStructSuccess) {
const std::string body = R"(
%val1 = OpExtInst %struct_f32_u32 %extinst FrexpStruct %f32_h
@@ -5552,7 +2818,8 @@ TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetInternalInvalidDataF32) {
"expected Interpolant to be a pointer"));
}
-TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetInternalInvalidDataF32Vec2) {
+TEST_F(ValidateExtInst,
+ GlslStd450InterpolateAtOffsetInternalInvalidDataF32Vec2) {
const std::string body = R"(
%ld2 = OpLoad %f32vec2 %f32vec2_input
%val2 = OpExtInst %f32vec2 %extinst InterpolateAtOffset %ld2 %f32vec2_01
@@ -8778,6 +6045,18 @@ OpMemoryModel Logical GLSL450
"declared without SPV_KHR_non_semantic_info"));
}
+TEST_F(ValidateClspvReflection, DoesNotRequiresNonSemanticExtensionPost1p6) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "NonSemantic.ClspvReflection.1"
+OpMemoryModel Logical GLSL450
+)";
+
+ CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+}
+
TEST_F(ValidateClspvReflection, MissingVersion) {
const std::string text = R"(
OpCapability Shader
diff --git a/test/val/val_extension_spv_khr_bit_instructions.cpp b/test/val/val_extension_spv_khr_bit_instructions.cpp
new file mode 100644
index 00000000..0e926716
--- /dev/null
+++ b/test/val/val_extension_spv_khr_bit_instructions.cpp
@@ -0,0 +1,117 @@
+// Copyright (c) 2021 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 for OpExtension validator rules.
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "source/enum_string_mapping.h"
+#include "source/extensions.h"
+#include "source/spirv_target_env.h"
+#include "test/test_fixture.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Values;
+using ::testing::ValuesIn;
+
+using ValidateSpvKHRBitInstructions = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateSpvKHRBitInstructions, Valid) {
+ const std::string str = R"(
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability BitInstructions
+ OpExtension "SPV_KHR_bit_instructions"
+ OpMemoryModel Physical32 OpenCL
+ OpEntryPoint Kernel %main "main"
+
+ %void = OpTypeVoid
+ %void_fn = OpTypeFunction %void
+ %u32 = OpTypeInt 32 0
+ %u32_1 = OpConstant %u32 1
+
+ %main = OpFunction %void None %void_fn
+ %entry = OpLabel
+ %unused = OpBitReverse %u32 %u32_1
+ OpReturn
+ OpFunctionEnd
+)";
+ CompileSuccessfully(str.c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateSpvKHRBitInstructions, RequiresExtension) {
+ const std::string str = R"(
+ OpCapability Kernel
+ OpCapability Addresses
+ OpCapability BitInstructions
+ OpMemoryModel Physical32 OpenCL
+ OpEntryPoint Kernel %main "main"
+
+ %void = OpTypeVoid
+ %void_fn = OpTypeFunction %void
+ %u32 = OpTypeInt 32 0
+ %u32_1 = OpConstant %u32 1
+
+ %main = OpFunction %void None %void_fn
+ %entry = OpLabel
+ %unused = OpBitReverse %u32 %u32_1
+ OpReturn
+ OpFunctionEnd
+)";
+ CompileSuccessfully(str.c_str());
+ EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("1st operand of Capability: operand BitInstructions(6025) "
+ "requires one of these extensions: SPV_KHR_bit_instructions"));
+}
+
+TEST_F(ValidateSpvKHRBitInstructions, RequiresCapability) {
+ const std::string str = R"(
+ OpCapability Kernel
+ OpCapability Addresses
+ OpExtension "SPV_KHR_bit_instructions"
+ OpMemoryModel Physical32 OpenCL
+ OpEntryPoint Kernel %main "main"
+
+ %void = OpTypeVoid
+ %void_fn = OpTypeFunction %void
+ %u32 = OpTypeInt 32 0
+ %u32_1 = OpConstant %u32 1
+
+ %main = OpFunction %void None %void_fn
+ %entry = OpLabel
+ %unused = OpBitReverse %u32 %u32_1
+ OpReturn
+ OpFunctionEnd
+)";
+ CompileSuccessfully(str.c_str());
+ EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Opcode BitReverse requires one of these capabilities: "
+ "Shader BitInstructions"));
+}
+
+} // namespace
+} // namespace val
+} // namespace spvtools
diff --git a/test/val/val_extension_spv_khr_terminate_invocation.cpp b/test/val/val_extension_spv_khr_terminate_invocation.cpp
index 4cabf9e2..8d924149 100644
--- a/test/val/val_extension_spv_khr_terminate_invocation.cpp
+++ b/test/val/val_extension_spv_khr_terminate_invocation.cpp
@@ -55,7 +55,7 @@ TEST_F(ValidateSpvKHRTerminateInvocation, Valid) {
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
-TEST_F(ValidateSpvKHRTerminateInvocation, RequiresExtension) {
+TEST_F(ValidateSpvKHRTerminateInvocation, RequiresExtensionPre1p6) {
const std::string str = R"(
OpCapability Shader
OpMemoryModel Logical Simple
@@ -72,9 +72,30 @@ TEST_F(ValidateSpvKHRTerminateInvocation, RequiresExtension) {
)";
CompileSuccessfully(str.c_str());
EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("TerminateInvocation requires one of the following "
- "extensions: SPV_KHR_terminate_invocation"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "TerminateInvocation requires SPIR-V version 1.6 at minimum or one "
+ "of the following extensions: SPV_KHR_terminate_invocation"));
+}
+
+TEST_F(ValidateSpvKHRTerminateInvocation, RequiresNoExtensionPost1p6) {
+ const std::string str = R"(
+ OpCapability Shader
+ OpMemoryModel Logical Simple
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+
+ %void = OpTypeVoid
+ %void_fn = OpTypeFunction %void
+
+ %main = OpFunction %void None %void_fn
+ %entry = OpLabel
+ OpTerminateInvocation
+ OpFunctionEnd
+)";
+ CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_6);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
}
TEST_F(ValidateSpvKHRTerminateInvocation, RequiresShaderCapability) {
diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp
index dd4c952c..69257a58 100644
--- a/test/val/val_id_test.cpp
+++ b/test/val/val_id_test.cpp
@@ -1015,7 +1015,7 @@ TEST_F(ValidateIdWithMessage, OpTypeRuntimeArrayBad) {
"type."));
}
// TODO: Object of this type can only be created with OpVariable using the
-// Unifrom Storage Class
+// Uniform Storage Class
TEST_F(ValidateIdWithMessage, OpTypeStructGood) {
std::string spirv = kGLSL450MemoryModel + R"(
@@ -2859,7 +2859,7 @@ TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedStruct) {
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
-// Same code as the last test excect for an extra decoration on one of the
+// Same code as the last test except for an extra decoration on one of the
// members. With the relaxed rules, the code is still valid.
TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedStructWithExtraDecoration) {
std::string spirv = kGLSL450MemoryModel + R"(
@@ -5524,9 +5524,9 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("OpDecorate SpecId decoration target <id> "
- "'1[%uint_3]' is not a scalar specialization "
- "constant."));
+ HasSubstr("SpecId decoration on target <id> "
+ "'1[%uint_3]' must be a scalar specialization "
+ "constant"));
}
TEST_F(ValidateIdWithMessage, SpecIdTargetOpSpecConstantOpBad) {
@@ -5546,8 +5546,8 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("OpDecorate SpecId decoration target <id> '1[%1]' is "
- "not a scalar specialization constant."));
+ HasSubstr("SpecId decoration on target <id> '1[%1]' "
+ "must be a scalar specialization constant"));
}
TEST_F(ValidateIdWithMessage, SpecIdTargetOpSpecConstantCompositeBad) {
@@ -5566,8 +5566,8 @@ OpFunctionEnd
CompileSuccessfully(spirv.c_str());
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("OpDecorate SpecId decoration target <id> '1[%1]' is "
- "not a scalar specialization constant."));
+ HasSubstr("SpecId decoration on target <id> '1[%1]' "
+ "must be a scalar specialization constant"));
}
TEST_F(ValidateIdWithMessage, SpecIdTargetGood) {
diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp
index 701e35e1..a11d07ce 100644
--- a/test/val/val_image_test.cpp
+++ b/test/val/val_image_test.cpp
@@ -61,8 +61,11 @@ OpCapability ImageBuffer
// In 1.4, the entry point must list all module-scope variables used. Just
// list all of them.
- std::string interface_vars = (env != SPV_ENV_UNIVERSAL_1_4) ? "" :
- R"(
+ //
+ // For Vulkan, anything Location decoration needs to be an interface variable
+ std::string interface_vars =
+ (env != SPV_ENV_UNIVERSAL_1_4) ? "%input_flat_u32" :
+ R"(
%uniform_image_f32_1d_0001
%uniform_image_f32_1d_0002_rgba32f
%uniform_image_f32_2d_0001
@@ -1059,7 +1062,7 @@ TEST_F(ValidateImage, ImageTexelPointerImageNotResultTypePointer) {
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 136[%136] cannot be a "
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 137[%137] cannot be a "
"type"));
}
@@ -1815,9 +1818,10 @@ TEST_F(ValidateImage, SampleImplicitLodMoreThanOneOffset) {
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets "
- "cannot be used together"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets, Offsets "
+ "cannot be used together"));
}
TEST_F(ValidateImage, SampleImplicitLodVulkanMoreThanOneOffset) {
@@ -1833,9 +1837,10 @@ TEST_F(ValidateImage, SampleImplicitLodVulkanMoreThanOneOffset) {
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
AnyVUID("VUID-StandaloneSpirv-Offset-04662"));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets "
- "cannot be used together"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets, Offsets "
+ "cannot be used together"));
}
TEST_F(ValidateImage, SampleImplicitLodMinLodWrongType) {
@@ -3129,7 +3134,7 @@ TEST_F(ValidateImage, GatherConstOffsetsArrayNotVector) {
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Expected Image Operand ConstOffsets array componenets "
+ HasSubstr("Expected Image Operand ConstOffsets array components "
"to be int vectors of size 2"));
}
@@ -3144,7 +3149,7 @@ TEST_F(ValidateImage, GatherConstOffsetsArrayVectorWrongSize) {
CompileSuccessfully(GenerateShaderCode(body).c_str());
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Expected Image Operand ConstOffsets array componenets "
+ HasSubstr("Expected Image Operand ConstOffsets array components "
"to be int vectors of size 2"));
}
@@ -6061,6 +6066,93 @@ TEST_F(ValidateImage, ImageTexelPointerRgba16fVulkan) {
"R32f, R32i, or R32ui for Vulkan environment"));
}
+TEST_F(ValidateImage, ImageExecutionModeLimitationNoMode) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %2 " " %4
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%12 = OpTypeImage %float 2D 0 0 0 1 Rgba8ui
+%13 = OpTypeSampledImage %12
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%5 = OpVariable %_ptr_UniformConstant_13 UniformConstant
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%4 = OpVariable %_ptr_Input_v4float Input
+%v2float = OpTypeVector %float 2
+%float_1_35631564en19 = OpConstant %float 1.35631564e-19
+%2 = OpFunction %void None %8
+%8224 = OpLabel
+%6 = OpLoad %13 %5
+%19 = OpLoad %v4float %4
+%20 = OpVectorShuffle %v2float %19 %19 0 1
+%21 = OpVectorTimesScalar %v2float %20 %float_1_35631564en19
+%65312 = OpImageSampleImplicitLod %v4float %6 %21
+OpUnreachable
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("ImplicitLod instructions require "
+ "DerivativeGroupQuadsNV or DerivativeGroupLinearNV "
+ "execution mode for GLCompute execution model"));
+}
+
+TEST_F(ValidateImage, TypeSampledImageNotBufferPost1p6) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability SampledBuffer
+OpMemoryModel Logical GLSL450
+%float = OpTypeFloat 32
+%image = OpTypeImage %float Buffer 0 0 0 1 Unknown
+%sampled = OpTypeSampledImage %image
+)";
+
+ CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("In SPIR-V 1.6 or later, sampled image dimension must "
+ "not be Buffer"));
+}
+
+TEST_F(ValidateImage, NonTemporalImage) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 " " %4 %5
+OpExecutionMode %2 OriginUpperLeft
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%12 = OpTypeImage %float 2D 0 0 0 1 Rgba8ui
+%13 = OpTypeSampledImage %12
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%5 = OpVariable %_ptr_UniformConstant_13 UniformConstant
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%4 = OpVariable %_ptr_Input_v4float Input
+%v2float = OpTypeVector %float 2
+%float_1_35631564en19 = OpConstant %float 1.35631564e-19
+%2 = OpFunction %void None %8
+%8224 = OpLabel
+%6 = OpLoad %13 %5
+%19 = OpLoad %v4float %4
+%20 = OpVectorShuffle %v2float %19 %19 0 1
+%21 = OpVectorTimesScalar %v2float %20 %float_1_35631564en19
+%65312 = OpImageSampleImplicitLod %v4float %6 %21 Nontemporal
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_interfaces_test.cpp b/test/val/val_interfaces_test.cpp
index 6869e794..bec8d026 100644
--- a/test/val/val_interfaces_test.cpp
+++ b/test/val/val_interfaces_test.cpp
@@ -448,6 +448,8 @@ OpFunctionEnd
CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Location-04918"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("Members cannot be assigned a location"));
}
@@ -476,6 +478,8 @@ OpFunctionEnd
CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Location-04918"));
+ EXPECT_THAT(getDiagnosticString(),
HasSubstr("Members cannot be assigned a location"));
}
@@ -1267,10 +1271,9 @@ OpFunctionEnd
)";
CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
- EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Index can only be applied to Fragment output variables"));
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("must be in the Output storage class"));
}
TEST_F(ValidateInterfacesTest, VulkanLocationsArrayWithComponent) {
@@ -1410,6 +1413,129 @@ OpFunctionEnd
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
}
+TEST_F(ValidateInterfacesTest, VulkanLocationArrayWithComponent1) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Location 0
+OpMemberDecorate %struct 0 Component 0
+OpMemberDecorate %struct 1 Location 0
+OpMemberDecorate %struct 1 Component 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_2 = OpConstant %int 2
+%float_arr = OpTypeArray %float %int_2
+%struct = OpTypeStruct %float_arr %float_arr
+%ptr = OpTypePointer Input %struct
+%in = OpVariable %ptr Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationArrayWithComponent2) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Float64
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Location 0
+OpMemberDecorate %struct 0 Component 0
+OpMemberDecorate %struct 1 Location 0
+OpMemberDecorate %struct 1 Component 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%double = OpTypeFloat 64
+%int = OpTypeInt 32 0
+%int_2 = OpConstant %int 2
+%double_arr = OpTypeArray %double %int_2
+%struct = OpTypeStruct %float %double_arr
+%ptr = OpTypePointer Input %struct
+%in = OpVariable %ptr Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest, DuplicateInterfaceVariableSuccess) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in %out %in
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %in Location 0
+OpDecorate %out Location 0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%in_ptr = OpTypePointer Input %float
+%out_ptr = OpTypePointer Output %float
+%in = OpVariable %in_ptr Input
+%out = OpVariable %out_ptr Output
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest, StructWithBuiltinsMissingBlock_Bad) {
+ // See https://github.com/KhronosGroup/SPIRV-Registry/issues/134
+ //
+ // When a shader input or output is a struct that does not have Block,
+ // then it must have a Location.
+ // But BuiltIns must not have locations.
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in
+OpExecutionMode %main OriginUpperLeft
+; %struct needs a Block decoration
+OpMemberDecorate %struct 0 BuiltIn Position
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%struct = OpTypeStruct %v4float
+%in_ptr = OpTypePointer Input %struct
+%in = OpVariable %in_ptr Input
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-Location-04919"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Interface struct has no Block decoration but has BuiltIn members."));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_layout_test.cpp b/test/val/val_layout_test.cpp
index d34c97f9..7ebd7c00 100644
--- a/test/val/val_layout_test.cpp
+++ b/test/val/val_layout_test.cpp
@@ -538,7 +538,6 @@ TEST_F(ValidateLayout, ModuleProcessedInvalidIn10) {
OpMemoryModel Logical GLSL450
OpName %void "void"
OpModuleProcessed "this is ok in 1.1 and later"
- OpDecorate %void Volatile ; bogus, but makes the example short
%void = OpTypeVoid
)";
@@ -558,7 +557,6 @@ TEST_F(ValidateLayout, ModuleProcessedValidIn11) {
OpMemoryModel Logical GLSL450
OpName %void "void"
OpModuleProcessed "this is ok in 1.1 and later"
- OpDecorate %void Volatile ; bogus, but makes the example short
%void = OpTypeVoid
)";
diff --git a/test/val/val_limits_test.cpp b/test/val/val_limits_test.cpp
index 8fb80a46..364d514e 100644
--- a/test/val/val_limits_test.cpp
+++ b/test/val/val_limits_test.cpp
@@ -750,7 +750,7 @@ TEST_F(ValidateLimits, CustomizedControlFlowDepthBad) {
}
// Valid. The purpose here is to test the CFG depth calculation code when a loop
-// continue target is the loop iteself. It also exercises the case where a loop
+// continue target is the loop itself. It also exercises the case where a loop
// is unreachable.
TEST_F(ValidateLimits, ControlFlowNoEntryToLoopGood) {
std::string str = header + R"(
diff --git a/test/val/val_literals_test.cpp b/test/val/val_literals_test.cpp
index 6eadf321..7c9aad67 100644
--- a/test/val/val_literals_test.cpp
+++ b/test/val/val_literals_test.cpp
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// Validation tests for ilegal literals
+// Validation tests for illegal literals
#include <string>
#include <utility>
diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp
index 9799b404..8ff40e14 100644
--- a/test/val/val_memory_test.cpp
+++ b/test/val/val_memory_test.cpp
@@ -746,7 +746,7 @@ TEST_F(ValidateMemory, ArrayLenIndexNotPointerToStruct) {
%float = OpTypeFloat 32
%uint = OpTypeInt 32 0
%_runtimearr_float = OpTypeRuntimeArray %float
- %_struct_7 = OpTypeStruct %float %_runtimearr_float
+ %_struct_7 = OpTypeStruct %float
%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7
%1 = OpFunction %void None %3
%9 = OpLabel
@@ -2344,11 +2344,12 @@ OpExtension "SPV_EXT_descriptor_indexing"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
-OpDecorate %array_t Block
+OpDecorate %struct Block
%uint_t = OpTypeInt 32 0
%inner_array_t = OpTypeRuntimeArray %uint_t
%array_t = OpTypeRuntimeArray %inner_array_t
-%array_ptr = OpTypePointer StorageBuffer %array_t
+%struct = OpTypeStruct %array_t
+%array_ptr = OpTypePointer StorageBuffer %struct
%2 = OpVariable %array_ptr StorageBuffer
%void = OpTypeVoid
%func_t = OpTypeFunction %void
@@ -2504,13 +2505,14 @@ OpExtension "SPV_EXT_descriptor_indexing"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
-OpDecorate %array_t Block
+OpDecorate %struct Block
%uint_t = OpTypeInt 32 0
%dim = OpConstant %uint_t 1
%sampler_t = OpTypeSampler
%inner_array_t = OpTypeRuntimeArray %uint_t
%array_t = OpTypeRuntimeArray %inner_array_t
-%array_ptr = OpTypePointer StorageBuffer %array_t
+%struct = OpTypeStruct %array_t
+%array_ptr = OpTypePointer StorageBuffer %struct
%2 = OpVariable %array_ptr StorageBuffer
%void = OpTypeVoid
%func_t = OpTypeFunction %void
@@ -4046,7 +4048,7 @@ TEST_F(ValidateMemory, VulkanInvariantOutputSuccess) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
-OpEntryPoint Vertex %main "main"
+OpEntryPoint Vertex %main "main" %var
OpDecorate %var Location 0
OpDecorate %var Invariant
%void = OpTypeVoid
@@ -4068,7 +4070,7 @@ TEST_F(ValidateMemory, VulkanInvariantInputStructSuccess) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
-OpEntryPoint Fragment %main "main"
+OpEntryPoint Fragment %main "main" %var
OpExecutionMode %main OriginUpperLeft
OpDecorate %var Location 0
OpMemberDecorate %struct 1 Invariant
@@ -4284,6 +4286,117 @@ OpFunctionEnd
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
}
+TEST_F(ValidateMemory, LoadRuntimeArray) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%rta = OpTypeRuntimeArray %int
+%block = OpTypeStruct %rta
+%ptr_rta = OpTypePointer StorageBuffer %rta
+%ptr_block = OpTypePointer StorageBuffer %block
+%var = OpVariable %ptr_block StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpAccessChain %ptr_rta %var %int_0
+%ld = OpLoad %rta %gep
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Cannot load a runtime-sized array"));
+}
+
+TEST_F(ValidateMemory, LoadRuntimeArrayInStruct) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%rta = OpTypeRuntimeArray %int
+%block = OpTypeStruct %rta
+%ptr_rta = OpTypePointer StorageBuffer %rta
+%ptr_block = OpTypePointer StorageBuffer %block
+%var = OpVariable %ptr_block StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld = OpLoad %block %var
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Cannot load a runtime-sized array"));
+}
+
+TEST_F(ValidateMemory, LoadRuntimeArrayInArray) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%rta = OpTypeRuntimeArray %int
+%block = OpTypeStruct %rta
+%array = OpTypeArray %block %int_4
+%ptr_rta = OpTypePointer StorageBuffer %rta
+%ptr_block = OpTypePointer StorageBuffer %block
+%ptr_array = OpTypePointer StorageBuffer %array
+%var = OpVariable %ptr_array StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld = OpLoad %array %var
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Cannot load a runtime-sized array"));
+}
+
+TEST_F(ValidateMemory, Pre1p4WorkgroupMemoryBadLayoutOk) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+%void = OpTypeVoid
+%bool = OpTypeBool
+%struct = OpTypeStruct %bool
+%ptr = OpTypePointer Workgroup %struct
+%var = OpVariable %ptr Workgroup
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp
index 99f5c9cf..a37989b4 100644
--- a/test/val/val_modes_test.cpp
+++ b/test/val/val_modes_test.cpp
@@ -63,12 +63,13 @@ OpEntryPoint GLCompute %main "main"
CompileSuccessfully(spirv, env);
EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
EXPECT_THAT(getDiagnosticString(),
- AnyVUID("VUID-StandaloneSpirv-LocalSize-04683"));
+ AnyVUID("VUID-StandaloneSpirv-LocalSize-06426"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("In the Vulkan environment, GLCompute execution model entry "
- "points require either the LocalSize execution mode or an "
- "object decorated with WorkgroupSize must be specified."));
+ HasSubstr(
+ "In the Vulkan environment, GLCompute execution model entry "
+ "points require either the LocalSize or LocalSizeId execution mode "
+ "or an object decorated with WorkgroupSize must be specified."));
}
TEST_F(ValidateMode, GLComputeNoModeVulkanWorkgroupSize) {
@@ -101,6 +102,40 @@ OpExecutionMode %main LocalSize 1 1 1
EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env));
}
+TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdBad) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+)" + kVoidFunction;
+
+ spv_target_env env = SPV_ENV_VULKAN_1_1; // need SPIR-V 1.2
+ CompileSuccessfully(spirv, env);
+ EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("LocalSizeId mode is not allowed by the current environment."));
+}
+
+TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdGood) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+)" + kVoidFunction;
+
+ spv_target_env env = SPV_ENV_VULKAN_1_1; // need SPIR-V 1.2
+ CompileSuccessfully(spirv, env);
+ spvValidatorOptionsSetAllowLocalSizeId(getValidatorOptions(), true);
+ EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env));
+}
+
TEST_F(ValidateMode, FragmentOriginLowerLeftVulkan) {
const std::string spirv = R"(
OpCapability Shader
@@ -672,7 +707,7 @@ INSTANTIATE_TEST_SUITE_P(ValidateModeKernelOnlyGoodSpv13, ValidateModeExecution,
Values("Kernel"),
Values("LocalSizeHint 1 1 1", "VecTypeHint 4",
"ContractionOff",
- "LocalSizeHintId %int1"),
+ "LocalSizeHintId %int1 %int1 %int1"),
Values(SPV_ENV_UNIVERSAL_1_3)));
INSTANTIATE_TEST_SUITE_P(
@@ -684,7 +719,7 @@ INSTANTIATE_TEST_SUITE_P(
Values("Geometry", "TessellationControl", "TessellationEvaluation",
"GLCompute", "Vertex", "Fragment"),
Values("LocalSizeHint 1 1 1", "VecTypeHint 4", "ContractionOff",
- "LocalSizeHintId %int1"),
+ "LocalSizeHintId %int1 %int1 %int1"),
Values(SPV_ENV_UNIVERSAL_1_3)));
INSTANTIATE_TEST_SUITE_P(
@@ -863,7 +898,7 @@ OpCapability Kernel
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Kernel %main "main"
-OpExecutionMode %main LocalSizeHintId %int_1
+OpExecutionMode %main LocalSizeHintId %int_1 %int_1 %int_1
%int = OpTypeInt 32 0
%int_1 = OpConstant %int 1
)" + kVoidFunction;
@@ -882,7 +917,7 @@ OpCapability Kernel
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Kernel %main "main"
-OpExecutionModeId %main LocalSizeHintId %int_1
+OpExecutionModeId %main LocalSizeHintId %int_1 %int_1 %int_1
%int = OpTypeInt 32 0
%int_1 = OpConstant %int 1
)" + kVoidFunction;
@@ -898,7 +933,7 @@ OpCapability Kernel
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
-OpExecutionModeId %main LocalSizeHintId %int_1
+OpExecutionModeId %main LocalSizeHintId %int_1 %int_1 %int_1
%int = OpTypeInt 32 0
%int_ptr = OpTypePointer Private %int
%int_1 = OpVariable %int_ptr Private
@@ -1143,6 +1178,26 @@ OpFunctionEnd
HasSubstr("Expected bool scalar type as Result Type"));
}
+TEST_F(ValidateMode, LocalSizeIdVulkan1p3DoesNotRequireOption) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_non_semantic_test.cpp b/test/val/val_non_semantic_test.cpp
index b80bb1ac..210cd1ac 100644
--- a/test/val/val_non_semantic_test.cpp
+++ b/test/val/val_non_semantic_test.cpp
@@ -105,7 +105,7 @@ INSTANTIATE_TEST_SUITE_P(OnlyOpExtension, ValidateNonSemanticGenerated,
Values(""), Values(TestResult())));
INSTANTIATE_TEST_SUITE_P(
- MissingOpExtension, ValidateNonSemanticGenerated,
+ MissingOpExtensionPre1p6, ValidateNonSemanticGenerated,
Combine(Values(false), Values(true), Values(""), Values(""),
Values(TestResult(
SPV_ERROR_INVALID_DATA,
@@ -190,6 +190,26 @@ OpEntryPoint Vertex %main "main"
HasSubstr("ID 2[%2] has not been defined"));
}
+TEST_F(ValidateNonSemanticString, MissingOpExtensionPost1p6) {
+ const std::string spirv = R"(
+OpCapability Shader
+%extinst = OpExtInstImport "NonSemantic.Testing.Set"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+%void = OpTypeVoid
+%test = OpExtInst %void %extinst 3
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_6);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_storage_test.cpp b/test/val/val_storage_test.cpp
index 35f6a8d5..ae4047b9 100644
--- a/test/val/val_storage_test.cpp
+++ b/test/val/val_storage_test.cpp
@@ -280,7 +280,7 @@ TEST_P(ValidateStorageExecutionModel, VulkanOutsideStoreFailure) {
AnyVUID("VUID-StandaloneSpirv-None-04644"));
EXPECT_THAT(
getDiagnosticString(),
- HasSubstr("in Vulkan evironment, Output Storage Class must not be used "
+ HasSubstr("in Vulkan environment, Output Storage Class must not be used "
"in GLCompute, RayGenerationKHR, IntersectionKHR, AnyHitKHR, "
"ClosestHitKHR, MissKHR, or CallableKHR execution models"));
}
diff --git a/test/val/val_version_test.cpp b/test/val/val_version_test.cpp
index 98565ddb..6b7c4fe8 100644
--- a/test/val/val_version_test.cpp
+++ b/test/val/val_version_test.cpp
@@ -74,6 +74,12 @@ std::string version(spv_target_env env) {
case SPV_ENV_UNIVERSAL_1_4:
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
return "1.4";
+ case SPV_ENV_UNIVERSAL_1_5:
+ case SPV_ENV_VULKAN_1_2:
+ return "1.5";
+ case SPV_ENV_UNIVERSAL_1_6:
+ case SPV_ENV_VULKAN_1_3:
+ return "1.6";
default:
return "0";
}
@@ -103,8 +109,14 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion,
std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true),
std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true),
std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true),
std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_0, vulkan_spirv, true),
std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_1, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_1_SPIRV_1_4,vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_2, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_3, vulkan_spirv, true),
std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_0, vulkan_spirv, true),
std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_1, vulkan_spirv, true),
std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_2, vulkan_spirv, true),
@@ -115,8 +127,14 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion,
std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true),
std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true),
std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true),
std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_0, vulkan_spirv, false),
std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_1, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_2, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_3, vulkan_spirv, true),
std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_0, vulkan_spirv, false),
std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_1, vulkan_spirv, false),
std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
@@ -127,8 +145,14 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion,
std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false),
std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true),
std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true),
std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0, vulkan_spirv, false),
std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_1, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_2, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_3, vulkan_spirv, true),
std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_0, vulkan_spirv, false),
std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_1, vulkan_spirv, false),
std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
@@ -139,13 +163,73 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion,
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false),
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false),
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true),
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, vulkan_spirv, false),
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_2, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_3, vulkan_spirv, true),
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_0, vulkan_spirv, false),
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_1, vulkan_spirv, false),
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_3, vulkan_spirv, false),
- std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false)
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false),
+
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_2, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_3, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_3, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_5, vulkan_spirv, false),
+
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_2, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_3, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_3, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_5, vulkan_spirv, false),
+
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_2, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_3, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_3, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_5, vulkan_spirv, false)
)
);
@@ -156,27 +240,91 @@ INSTANTIATE_TEST_SUITE_P(Vulkan, ValidateVersion,
std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true),
std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true),
std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true),
std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_0, vulkan_spirv, true),
std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_2, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_3, vulkan_spirv, true),
std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_0, vulkan_spirv, true),
std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_1, vulkan_spirv, true),
std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_2, vulkan_spirv, true),
std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_3, vulkan_spirv, true),
std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_5, vulkan_spirv, true),
- std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true),
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false),
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false),
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false),
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true),
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_0, vulkan_spirv, false),
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_3, vulkan_spirv, true),
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_0, vulkan_spirv, false),
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_1, vulkan_spirv, false),
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_3, vulkan_spirv, false),
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_5, vulkan_spirv, false),
- std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true)
+
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_2, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_3, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_3, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_5, vulkan_spirv, false),
+
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_2, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_3, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_5, vulkan_spirv, false),
+
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_2, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_3, vulkan_spirv, true),
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_0, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_1, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_3, vulkan_spirv, false),
+ std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false)
)
);
@@ -187,6 +335,9 @@ INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion,
std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true),
+ std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_4, opencl_spirv, true),
+ std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_5, opencl_spirv, true),
+ std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_6, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_2_0, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_2_1, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_2_2, opencl_spirv, true),
@@ -199,6 +350,9 @@ INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion,
std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true),
+ std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_4, opencl_spirv, true),
+ std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_5, opencl_spirv, true),
+ std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_6, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_0, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_1, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_2, opencl_spirv, true),
@@ -211,6 +365,9 @@ INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion,
std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, false),
std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true),
+ std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_4, opencl_spirv, true),
+ std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_5, opencl_spirv, true),
+ std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_6, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_2_0, opencl_spirv, false),
std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_2_1, opencl_spirv, false),
std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_2_2, opencl_spirv, true),
@@ -223,6 +380,9 @@ INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion,
std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true),
+ std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_4, opencl_spirv, true),
+ std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_5, opencl_spirv, true),
+ std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_6, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_0, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_1, opencl_spirv, true),
std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_2, opencl_spirv, true),
diff --git a/test/wasm/test.js b/test/wasm/test.js
new file mode 100644
index 00000000..7f0d8f3c
--- /dev/null
+++ b/test/wasm/test.js
@@ -0,0 +1,64 @@
+// Copyright (c) 2020 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.
+
+const spirvTools = require("../../out/web/spirv-tools");
+const fs = require("fs");
+const util = require("util");
+const readFile = util.promisify(fs.readFile);
+
+const SPV_PATH = "./test/fuzzers/corpora/spv/simple.spv";
+
+const test = async () => {
+ const spv = await spirvTools();
+
+ // disassemble from file
+ const buffer = await readFile(SPV_PATH);
+ const disFileResult = spv.dis(
+ buffer,
+ spv.SPV_ENV_UNIVERSAL_1_3,
+ spv.SPV_BINARY_TO_TEXT_OPTION_INDENT |
+ spv.SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES |
+ spv.SPV_BINARY_TO_TEXT_OPTION_COLOR
+ );
+ console.log("dis from file:\n", disFileResult);
+
+ // assemble
+ const source = `
+ OpCapability Linkage
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpSource GLSL 450
+ OpDecorate %spec SpecId 1
+ %int = OpTypeInt 32 1
+ %spec = OpSpecConstant %int 0
+ %const = OpConstant %int 42`;
+ const asResult = spv.as(
+ source,
+ spv.SPV_ENV_UNIVERSAL_1_3,
+ spv.SPV_TEXT_TO_BINARY_OPTION_NONE
+ );
+ console.log(`as returned ${asResult.byteLength} bytes`);
+
+ // re-disassemble
+ const disResult = spv.dis(
+ asResult,
+ spv.SPV_ENV_UNIVERSAL_1_3,
+ spv.SPV_BINARY_TO_TEXT_OPTION_INDENT |
+ spv.SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES |
+ spv.SPV_BINARY_TO_TEXT_OPTION_COLOR
+ );
+ console.log("dis:\n", disResult);
+};
+
+test();
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 124a3328..0a7e8651 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -44,10 +44,11 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES})
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})
- if (NOT DEFINED IOS_PLATFORM) # iOS does not allow std::system calls which spirv-reduce requires
+ if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "iOS")) # iOS does not allow std::system calls which spirv-reduce requires
add_spvtools_tool(TARGET spirv-reduce SRCS reduce/reduce.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-reduce ${SPIRV_TOOLS_FULL_VISIBILITY})
endif()
add_spvtools_tool(TARGET spirv-link SRCS link/linker.cpp LIBS SPIRV-Tools-link ${SPIRV_TOOLS_FULL_VISIBILITY})
+ add_spvtools_tool(TARGET spirv-lint SRCS lint/lint.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-lint SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
add_spvtools_tool(TARGET spirv-cfg
SRCS cfg/cfg.cpp
cfg/bin_to_dot.h
@@ -56,8 +57,8 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES})
target_include_directories(spirv-cfg PRIVATE ${spirv-tools_SOURCE_DIR}
${SPIRV_HEADER_INCLUDE_DIR})
set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val spirv-opt
- spirv-cfg spirv-link)
- if(NOT DEFINED IOS_PLATFORM)
+ spirv-cfg spirv-link spirv-lint)
+ if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "iOS"))
set(SPIRV_INSTALL_TARGETS ${SPIRV_INSTALL_TARGETS} spirv-reduce)
endif()
@@ -67,9 +68,6 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES})
endif(SPIRV_BUILD_FUZZER)
if(ENABLE_SPIRV_TOOLS_INSTALL)
- install(TARGETS ${SPIRV_INSTALL_TARGETS}
- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
- LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
- ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+ install(TARGETS ${SPIRV_INSTALL_TARGETS} DESTINATION ${CMAKE_INSTALL_BINDIR})
endif(ENABLE_SPIRV_TOOLS_INSTALL)
endif()
diff --git a/tools/as/as.cpp b/tools/as/as.cpp
index c8a44456..506b0585 100644
--- a/tools/as/as.cpp
+++ b/tools/as/as.cpp
@@ -48,7 +48,7 @@ Options:
argv0, argv0, target_env_list.c_str());
}
-static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
+static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
int main(int argc, char** argv) {
const char* inFile = nullptr;
diff --git a/tools/cfg/bin_to_dot.cpp b/tools/cfg/bin_to_dot.cpp
index 2561eea4..72e7693a 100644
--- a/tools/cfg/bin_to_dot.cpp
+++ b/tools/cfg/bin_to_dot.cpp
@@ -57,13 +57,13 @@ class DotConverter {
// Ends processing for the current block, emitting its dot code.
void FlushBlock(const std::vector<uint32_t>& successors);
- // The ID of the current functio, or 0 if outside of a function.
+ // The ID of the current function, or 0 if outside of a function.
uint32_t current_function_id_ = 0;
// The ID of the current basic block, or 0 if outside of a block.
uint32_t current_block_id_ = 0;
- // Have we completed processing for the entry block to this fuction?
+ // Have we completed processing for the entry block to this function?
bool seen_function_entry_block_ = false;
// The Id of the merge block for this block if it exists, or 0 otherwise.
diff --git a/tools/cfg/bin_to_dot.h b/tools/cfg/bin_to_dot.h
index 4de2e07f..a61c9759 100644
--- a/tools/cfg/bin_to_dot.h
+++ b/tools/cfg/bin_to_dot.h
@@ -20,7 +20,7 @@
#include "spirv-tools/libspirv.h"
// Dumps the control flow graph for the given module to the output stream.
-// Returns SPV_SUCCESS on succes.
+// Returns SPV_SUCCESS on success.
spv_result_t BinaryToDot(const spv_const_context context, const uint32_t* words,
size_t num_words, std::iostream* out,
spv_diagnostic* diagnostic);
diff --git a/tools/cfg/cfg.cpp b/tools/cfg/cfg.cpp
index a93488d5..5380c21e 100644
--- a/tools/cfg/cfg.cpp
+++ b/tools/cfg/cfg.cpp
@@ -44,7 +44,7 @@ Options:
argv0, argv0);
}
-static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
+static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
int main(int argc, char** argv) {
const char* inFile = nullptr;
@@ -69,16 +69,16 @@ int main(int argc, char** argv) {
if (0 == strcmp(argv[argi], "--help")) {
print_usage(argv[0]);
return 0;
- } else if (0 == strcmp(argv[argi], "--version")) {
+ }
+ if (0 == strcmp(argv[argi], "--version")) {
printf("%s EXPERIMENTAL\n", spvSoftwareVersionDetailsString());
printf("Target: %s\n",
spvTargetEnvDescription(kDefaultEnvironment));
return 0;
- } else {
- print_usage(argv[0]);
- return 1;
}
- } break;
+ print_usage(argv[0]);
+ return 1;
+ }
case 0: {
// Setting a filename of "-" to indicate stdin.
if (!inFile) {
diff --git a/tools/fuzz/fuzz.cpp b/tools/fuzz/fuzz.cpp
index 422bea53..306f9255 100644
--- a/tools/fuzz/fuzz.cpp
+++ b/tools/fuzz/fuzz.cpp
@@ -630,7 +630,7 @@ bool Fuzz(const spv_target_env& target_env,
std::move(ir_context), std::move(transformation_context),
std::move(fuzzer_context), message_consumer, donor_suppliers,
fuzzer_options->all_passes_enabled, repeated_pass_strategy,
- fuzzer_options->fuzzer_pass_validation_enabled, validator_options);
+ fuzzer_options->fuzzer_pass_validation_enabled, validator_options, false);
auto fuzz_result = fuzzer.Run(0);
if (fuzz_result.status ==
spvtools::fuzz::Fuzzer::Status::kFuzzerPassLedToInvalidModule) {
@@ -673,6 +673,19 @@ 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(
@@ -683,7 +696,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 == GetProtobufOkStatus()) {
std::ofstream transformations_json_file(filename);
transformations_json_file << json_string;
transformations_json_file.close();
@@ -734,9 +747,8 @@ 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 (google::protobuf::util::Status::OK !=
- google::protobuf::util::JsonStringToMessage(facts_json_string,
- &initial_facts)) {
+ if (GetProtobufOkStatus() != google::protobuf::util::JsonStringToMessage(
+ facts_json_string, &initial_facts)) {
spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error reading facts data");
return 1;
}
@@ -816,7 +828,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 != google::protobuf::util::Status::OK) {
+ if (json_generation_status != GetProtobufOkStatus()) {
spvtools::Error(FuzzDiagnostic, nullptr, {},
"Error writing out transformations in JSON format");
return 1;
diff --git a/tools/io.h b/tools/io.h
index aff9eabd..83a85c1d 100644
--- a/tools/io.h
+++ b/tools/io.h
@@ -89,7 +89,7 @@ bool ReadBinaryFile(const char* filename, std::vector<T>* data) {
ReadFile(fp, data);
bool succeeded = WasFileCorrectlyRead<T>(fp, filename);
- if (use_file) fclose(fp);
+ if (use_file && fp) fclose(fp);
return succeeded;
}
@@ -111,7 +111,7 @@ bool ReadTextFile(const char* filename, std::vector<T>* data) {
ReadFile(fp, data);
bool succeeded = WasFileCorrectlyRead<T>(fp, filename);
- if (use_file) fclose(fp);
+ if (use_file && fp) fclose(fp);
return succeeded;
}
diff --git a/tools/link/linker.cpp b/tools/link/linker.cpp
index 359e8030..bdddeb89 100644
--- a/tools/link/linker.cpp
+++ b/tools/link/linker.cpp
@@ -25,7 +25,7 @@
namespace {
-const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
+const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
void print_usage(const char* program) {
std::string target_env_list = spvTargetEnvList(16, 80);
@@ -49,15 +49,18 @@ Options (in lexicographical order):
--create-library
Link the binaries into a library, keeping all exported symbols.
-h, --help
- Print this help.
+ Print this help.
--target-env <env>
- Set the target environment. Without this flag the target
- environment defaults to spv1.5. <env> must be one of
- {%s}
+ Set the environment used for interpreting the inputs. Without
+ this option the environment defaults to spv1.6. <env> must be
+ one of {%s}.
+ NOTE: The SPIR-V version used by the linked binary module
+ depends only on the version of the inputs, and is not affected
+ by this option.
--verify-ids
Verify that IDs in the resulting modules are truly unique.
--version
- Display linker version information
+ Display linker version information.
)",
program, program, target_env_list.c_str());
}
@@ -160,10 +163,11 @@ int main(int argc, char** argv) {
std::vector<uint32_t> linkingResult;
spv_result_t status = Link(context, contents, &linkingResult, options);
+ if (status != SPV_SUCCESS && status != SPV_WARNING) return 1;
if (!WriteFile<uint32_t>(outFile, "wb", linkingResult.data(),
linkingResult.size()))
return 1;
- return status == SPV_SUCCESS ? 0 : 1;
+ return 0;
}
diff --git a/tools/lint/lint.cpp b/tools/lint/lint.cpp
new file mode 100644
index 00000000..d37df830
--- /dev/null
+++ b/tools/lint/lint.cpp
@@ -0,0 +1,75 @@
+// Copyright (c) 2021 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <iostream>
+
+#include "source/opt/log.h"
+#include "spirv-tools/linter.hpp"
+#include "tools/io.h"
+#include "tools/util/cli_consumer.h"
+
+const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
+
+namespace {
+// Status and actions to perform after parsing command-line arguments.
+enum LintActions { LINT_CONTINUE, LINT_STOP };
+
+struct LintStatus {
+ LintActions action;
+ int code;
+};
+
+// Parses command-line flags. |argc| contains the number of command-line flags.
+// |argv| points to an array of strings holding the flags.
+//
+// On return, this function stores the name of the input program in |in_file|.
+// The return value indicates whether optimization should continue and a status
+// code indicating an error or success.
+LintStatus ParseFlags(int argc, const char** argv, const char** in_file) {
+ // TODO (dongja): actually parse flags, etc.
+ if (argc != 2) {
+ spvtools::Error(spvtools::utils::CLIMessageConsumer, nullptr, {},
+ "expected exactly one argument: in_file");
+ return {LINT_STOP, 1};
+ }
+
+ *in_file = argv[1];
+
+ return {LINT_CONTINUE, 0};
+}
+} // namespace
+
+int main(int argc, const char** argv) {
+ const char* in_file = nullptr;
+
+ spv_target_env target_env = kDefaultEnvironment;
+
+ spvtools::Linter linter(target_env);
+ linter.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
+
+ LintStatus status = ParseFlags(argc, argv, &in_file);
+
+ if (status.action == LINT_STOP) {
+ return status.code;
+ }
+
+ std::vector<uint32_t> binary;
+ if (!ReadBinaryFile(in_file, &binary)) {
+ return 1;
+ }
+
+ bool ok = linter.Run(binary.data(), binary.size());
+
+ return ok ? 0 : 1;
+}
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index b9339abe..63511a6a 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -44,7 +44,7 @@ struct OptStatus {
// initialization and setup. Note that |source| and |position| are irrelevant
// here because we are still not processing a SPIR-V input file.
void opt_diagnostic(spv_message_level_t level, const char* /*source*/,
- const spv_position_t& /*positon*/, const char* message) {
+ const spv_position_t& /*position*/, const char* message) {
if (level == SPV_MSG_ERROR) {
fprintf(stderr, "error: ");
}
@@ -59,7 +59,7 @@ std::string GetListOfPassesAsString(const spvtools::Optimizer& optimizer) {
return ss.str();
}
-const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
+const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
std::string GetLegalizationPasses() {
spvtools::Optimizer optimizer(kDefaultEnvironment);
@@ -143,6 +143,15 @@ Options (in lexicographical order):)",
does not support RelaxedPrecision or ignores it. This pass also
removes all RelaxedPrecision decorations.)");
printf(R"(
+ --convert-to-sampled-image "<descriptor set>:<binding> ..."
+ convert images and/or samplers with the given pairs of descriptor
+ set and binding to sampled images. If a pair of an image and a
+ sampler have the same pair of descriptor set and binding that is
+ one of the given pairs, they will be converted to a sampled
+ image. In addition, if only an image or a sampler has the
+ descriptor set and binding that is one of the given pairs, it
+ will be converted to a sampled image.)");
+ printf(R"(
--copy-propagate-arrays
Does propagation of memory references when an array is a copy of
another. It will only propagate an array if the source is never
@@ -154,6 +163,21 @@ Options (in lexicographical order):)",
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
+ index.)");
+ printf(R"(
+ --spread-volatile-semantics
+ Spread Volatile semantics to variables with SMIDNV, WarpIDNV,
+ SubgroupSize, SubgroupLocalInvocationId, SubgroupEqMask,
+ SubgroupGeMask, SubgroupGtMask, SubgroupLeMask, or SubgroupLtMask
+ BuiltIn decorations or OpLoad for them when the shader model is
+ ray generation, closest hit, miss, intersection, or callable.
+ For the SPIR-V version is 1.6 or above, it also spreads Volatile
+ semantics to a variable with HelperInvocation BuiltIn decoration
+ in the fragement shader.)");
+ printf(R"(
--descriptor-scalar-replacement
Replaces every array variable |desc| that has a DescriptorSet
and Binding decorations with a new variable for each element of
@@ -378,9 +402,12 @@ Options (in lexicographical order):)",
Change the scope of private variables that are used in a single
function to that function.)");
printf(R"(
- --reduce-load-size
+ --reduce-load-size[=<threshold>]
Replaces loads of composite objects where not every component is
- used by loads of just the elements that are used.)");
+ used by loads of just the elements that are used. If the ratio
+ of the used components of the load is less than the <threshold>,
+ we replace the load. <threshold> is a double type number. If
+ it is bigger than 1.0, we always replaces the load.)");
printf(R"(
--redundancy-elimination
Looks for instructions in the same function that compute the
@@ -406,6 +433,12 @@ Options (in lexicographical order):)",
Removes duplicate types, decorations, capabilities and extension
instructions.)");
printf(R"(
+ --remove-unused-interface-variables
+ Removes variables referenced on the |OpEntryPoint| instruction
+ that are not referenced in the entry point function or any function
+ in its call tree. Note that this could cause the shader interface
+ to no longer match other shader stages.)");
+ printf(R"(
--replace-invalid-opcode
Replaces instructions whose opcode is valid for shader modules,
but not for the current shader stage. To have an effect, all
@@ -456,10 +489,13 @@ Options (in lexicographical order):)",
--strip-debug
Remove all debug instructions.)");
printf(R"(
+ --strip-nonsemantic
+ Remove all reflection and nonsemantic information.)");
+ printf(R"(
--strip-reflect
- Remove all reflection information. For now, this covers
- reflection information defined by SPV_GOOGLE_hlsl_functionality1
- and SPV_KHR_non_semantic_info)");
+ DEPRECATED. Remove all reflection information. For now, this
+ covers reflection information defined by
+ SPV_GOOGLE_hlsl_functionality1 and SPV_KHR_non_semantic_info)");
printf(R"(
--target-env=<env>
Set the target environment. Without this flag the target
diff --git a/tools/reduce/reduce.cpp b/tools/reduce/reduce.cpp
index 4447b356..37600543 100644
--- a/tools/reduce/reduce.cpp
+++ b/tools/reduce/reduce.cpp
@@ -262,7 +262,7 @@ void DumpShader(spvtools::opt::IRContext* context, const char* filename) {
DumpShader(binary, filename);
}
-const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
+const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
int main(int argc, const char** argv) {
std::string in_binary_file;
diff --git a/tools/sva/README.md b/tools/sva/README.md
index d80b4d2c..cd3d13c9 100644
--- a/tools/sva/README.md
+++ b/tools/sva/README.md
@@ -1,6 +1,6 @@
# SVA
-SPIR-V Assember for WebGPU. The SPIR-V Assembler is a JavaScript library to
+SPIR-V Assembler for WebGPU. The SPIR-V Assembler is a JavaScript library to
convert SPIR-V assembly (as produced by spirv-dis in SPIR-V Tools) into a
SPIR-V binary. The assembler assumes it is generating WebGPU SPIR-V and thus has
the following limitations.
diff --git a/tools/sva/yarn.lock b/tools/sva/yarn.lock
index 4924690a..afa11f6c 100644
--- a/tools/sva/yarn.lock
+++ b/tools/sva/yarn.lock
@@ -694,9 +694,9 @@ get-stream@^4.0.0:
pump "^3.0.0"
glob-parent@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954"
- integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
diff --git a/tools/val/val.cpp b/tools/val/val.cpp
index 21a7d8f4..880ce46b 100644
--- a/tools/val/val.cpp
+++ b/tools/val/val.cpp
@@ -64,6 +64,8 @@ Options:
--relax-struct-store Allow store from one struct type to a
different type with compatible layout and
members.
+ --allow-localsizeid Allow use of the LocalSizeId decoration where it would otherwise not
+ be allowed by the target environment.
--before-hlsl-legalization Allows code patterns that are intended to be
fixed by spirv-opt's legalization passes.
--version Display validator version information.
@@ -75,7 +77,7 @@ Options:
int main(int argc, char** argv) {
const char* inFile = nullptr;
- spv_target_env target_env = SPV_ENV_UNIVERSAL_1_5;
+ spv_target_env target_env = SPV_ENV_UNIVERSAL_1_6;
spvtools::ValidatorOptions options;
bool continue_processing = true;
int return_code = 0;
@@ -109,17 +111,20 @@ int main(int argc, char** argv) {
printf("%s\n", spvSoftwareVersionDetailsString());
printf(
"Targets:\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n "
- "%s\n",
+ "%s\n %s\n %s\n %s\n",
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_0),
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1),
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2),
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_3),
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_4),
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_5),
+ spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_6),
spvTargetEnvDescription(SPV_ENV_OPENCL_2_2),
spvTargetEnvDescription(SPV_ENV_VULKAN_1_0),
spvTargetEnvDescription(SPV_ENV_VULKAN_1_1),
- spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+ spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4),
+ spvTargetEnvDescription(SPV_ENV_VULKAN_1_2),
+ spvTargetEnvDescription(SPV_ENV_VULKAN_1_3));
continue_processing = false;
return_code = 0;
} else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
@@ -153,6 +158,8 @@ int main(int argc, char** argv) {
options.SetWorkgroupScalarBlockLayout(true);
} else if (0 == strcmp(cur_arg, "--skip-block-layout")) {
options.SetSkipBlockLayout(true);
+ } else if (0 == strcmp(cur_arg, "--allow-localsizeid")) {
+ options.SetAllowLocalSizeId(true);
} else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
options.SetRelaxStructStore(true);
} else if (0 == cur_arg[1]) {
diff --git a/utils/check_copyright.py b/utils/check_copyright.py
index c5251230..b6dc933e 100755
--- a/utils/check_copyright.py
+++ b/utils/check_copyright.py
@@ -39,10 +39,11 @@ AUTHORS = ['The Khronos Group Inc.',
'Stefano Milizia',
'Alastair F. Donaldson',
'Mostafa Ashraf',
- 'Shiyu Liu']
+ 'Shiyu Liu',
+ 'ZHOU He']
CURRENT_YEAR='2021'
-YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020|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)))
diff --git a/utils/generate_grammar_tables.py b/utils/generate_grammar_tables.py
index 9ccf410b..74aa2829 100755
--- a/utils/generate_grammar_tables.py
+++ b/utils/generate_grammar_tables.py
@@ -23,7 +23,7 @@ import re
PYGEN_VARIABLE_PREFIX = 'pygen_variable'
# Extensions to recognize, but which don't necessarily come from the SPIR-V
-# core or KHR grammar files. Get this list from the SPIR-V registery web page.
+# core or KHR grammar files. Get this list from the SPIR-V registry web page.
# NOTE: Only put things on this list if it is not in those grammar files.
EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS = """
SPV_AMD_gcn_shader
diff --git a/utils/git-sync-deps b/utils/git-sync-deps
index eecfbe93..7a7e606f 100755
--- a/utils/git-sync-deps
+++ b/utils/git-sync-deps
@@ -168,7 +168,7 @@ def git_checkout_to_directory(git, repo, checkoutable, directory, verbose):
with open(os.devnull, 'w') as devnull:
# If this fails, we will fetch before trying again. Don't spam user
- # with error infomation.
+ # with error information.
if 0 == subprocess.call([git, 'checkout', '--quiet', checkoutable],
cwd=directory, stderr=devnull):
# if this succeeds, skip slow `git fetch`.
diff --git a/utils/roll_deps.sh b/utils/roll_deps.sh
index 7ecfdd3b..cef8b526 100755
--- a/utils/roll_deps.sh
+++ b/utils/roll_deps.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-# Copyright (c) 2019 Google Inc.
+# Copyright (c) 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,14 +15,17 @@
# Attempts to roll all entries in DEPS to tip-of-tree and create a commit.
#
-# Depends on roll-dep from depot_path being in PATH.
+# Depends on roll-dep from depot_tools
+# (https://chromium.googlesource.com/chromium/tools/depot_tools) being in PATH.
+
+set -eo pipefail
effcee_dir="external/effcee/"
effcee_trunk="origin/main"
googletest_dir="external/googletest/"
-googletest_trunk="origin/master"
+googletest_trunk="origin/main"
re2_dir="external/re2/"
-re2_trunk="origin/master"
+re2_trunk="origin/main"
spirv_headers_dir="external/spirv-headers/"
spirv_headers_trunk="origin/master"
@@ -38,9 +41,11 @@ fi
old_head=$(git rev-parse HEAD)
+set +e
roll-dep --ignore-dirty-tree --roll-to="${effcee_trunk}" "${effcee_dir}"
roll-dep --ignore-dirty-tree --roll-to="${googletest_trunk}" "${googletest_dir}"
roll-dep --ignore-dirty-tree --roll-to="${re2_trunk}" "${re2_dir}"
roll-dep --ignore-dirty-tree --roll-to="${spirv_headers_trunk}" "${spirv_headers_dir}"
git rebase --interactive "${old_head}"
+
diff --git a/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go b/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go
index b8436d27..44dd2205 100644
--- a/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go
+++ b/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go
@@ -48,7 +48,7 @@ const (
requestDone
)
-// Request is sent to a server to represent a Call or Notify operaton.
+// Request is sent to a server to represent a Call or Notify operation.
type Request struct {
conn *Conn
cancel context.CancelFunc
diff --git a/utils/vscode/src/lsp/jsonrpc2/wire.go b/utils/vscode/src/lsp/jsonrpc2/wire.go
index 3e31c340..fed9a25b 100644
--- a/utils/vscode/src/lsp/jsonrpc2/wire.go
+++ b/utils/vscode/src/lsp/jsonrpc2/wire.go
@@ -44,7 +44,7 @@ const (
CodeServerOverloaded = -32000
)
-// WireRequest is sent to a server to represent a Call or Notify operaton.
+// WireRequest is sent to a server to represent a Call or Notify operation.
type WireRequest struct {
// VersionTag is always encoded as the string "2.0"
VersionTag VersionTag `json:"jsonrpc"`
diff --git a/utils/vscode/src/lsp/protocol/tsprotocol.go b/utils/vscode/src/lsp/protocol/tsprotocol.go
index 50543fc8..e0a35946 100644
--- a/utils/vscode/src/lsp/protocol/tsprotocol.go
+++ b/utils/vscode/src/lsp/protocol/tsprotocol.go
@@ -143,7 +143,7 @@ type WorkspaceFoldersServerCapabilities struct {
* change notifications.
*
* If a strings is provided the string is treated as a ID
- * under which the notification is registed on the client
+ * under which the notification is registered on the client
* side. The ID can be used to unregister for these events
* using the `client/unregisterCapability` request.
*/
@@ -162,7 +162,7 @@ type WorkspaceFolder struct {
/*Name defined:
* The name of the workspace folder. Used to refer to this
- * workspace folder in thge user interface.
+ * workspace folder in the user interface.
*/
Name string `json:"name"`
}
@@ -1129,7 +1129,7 @@ type ServerCapabilities struct {
* change notifications.
*
* If a strings is provided the string is treated as a ID
- * under which the notification is registed on the client
+ * under which the notification is registered on the client
* side. The ID can be used to unregister for these events
* using the `client/unregisterCapability` request.
*/
@@ -1803,7 +1803,7 @@ type CompletionOptions struct {
/*AllCommitCharacters defined:
* The list of all possible characters that commit a completion. This field can be used
- * if clients don't support individual commmit characters per completion item. See
+ * if clients don't support individual commit characters per completion item. See
* `ClientCapabilities.textDocument.completion.completionItem.commitCharactersSupport`
*
* @since 3.2.0
@@ -2844,7 +2844,7 @@ type LocationLink struct {
/*TargetSelectionRange defined:
* The range that should be selected and revealed when this link is being followed, e.g the name of a function.
- * Must be contained by the the `targetRange`. See also `DocumentSymbol#range`
+ * Must be contained by the `targetRange`. See also `DocumentSymbol#range`
*/
TargetSelectionRange Range `json:"targetSelectionRange"`
}
@@ -2881,7 +2881,7 @@ type Color struct {
type ColorInformation struct {
/*Range defined:
- * The range in the document where this color appers.
+ * The range in the document where this color appears.
*/
Range Range `json:"range"`
@@ -3627,14 +3627,14 @@ type DocumentSymbol struct {
/*Range defined:
* The range enclosing this symbol not including leading/trailing whitespace but everything else
- * like comments. This information is typically used to determine if the the clients cursor is
+ * like comments. This information is typically used to determine if the clients cursor is
* inside the symbol to reveal in the symbol in the UI.
*/
Range Range `json:"range"`
/*SelectionRange defined:
* The range that should be selected and revealed when this symbol is being picked, e.g the name of a function.
- * Must be contained by the the `range`.
+ * Must be contained by the `range`.
*/
SelectionRange Range `json:"selectionRange"`
@@ -3652,7 +3652,7 @@ type CodeActionContext struct {
/*Diagnostics defined:
* An array of diagnostics known on the client side overlapping the range provided to the
- * `textDocument/codeAction` request. They are provied so that the server knows which
+ * `textDocument/codeAction` request. They are provided so that the server knows which
* errors are currently presented to the user for the given range. There is no guarantee
* that these accurately reflect the error state of the resource. The primary parameter
* to compute code actions is the provided range.
@@ -4081,13 +4081,13 @@ const (
/*TextOnlyTransactional defined:
* If the workspace edit contains only textual file changes they are executed transactional.
* If resource changes (create, rename or delete file) are part of the change the failure
- * handling startegy is abort.
+ * handling strategy is abort.
*/
TextOnlyTransactional FailureHandlingKind = "textOnlyTransactional"
/*Undo defined:
* The client tries to undo the operations already executed. But there is no
- * guaruntee that this is succeeding.
+ * guarantee that this is succeeding.
*/
Undo FailureHandlingKind = "undo"
diff --git a/utils/vscode/src/parser/parser.go b/utils/vscode/src/parser/parser.go
index cc6f3332..4c0fa8f7 100644
--- a/utils/vscode/src/parser/parser.go
+++ b/utils/vscode/src/parser/parser.go
@@ -798,7 +798,7 @@ type Identifier struct {
References []*Token // all the places the identifier was referenced
}
-// Severity is an enumerator of diagnositc seeverities
+// Severity is an enumerator of diagnostic severities
type Severity int
// Severity levels