From 7c8270ea32dcc14da04999bad711d0e6161b6889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=91=D1=80=D0=B0=D0=BD=D0=B8=D0=BC=D0=B8=D1=80=20=D0=9A?= =?UTF-8?q?=D0=B0=D1=80=D0=B0=D1=9F=D0=B8=D1=9B?= Date: Sat, 20 Apr 2019 15:45:51 -0700 Subject: [PATCH] Updated spirv-tools. --- 3rdparty/spirv-tools/Android.mk | 1 + 3rdparty/spirv-tools/BUILD.gn | 2 + .../include/generated/build-version.inc | 2 +- .../include/spirv-tools/optimizer.hpp | 4 + 3rdparty/spirv-tools/source/CMakeLists.txt | 18 +- .../spirv-tools/source/opt/CMakeLists.txt | 2 + .../decompose_initialized_variables_pass.cpp | 77 +++ .../decompose_initialized_variables_pass.h | 57 ++ .../opt/generate_webgpu_initializers_pass.cpp | 2 +- .../opt/generate_webgpu_initializers_pass.h | 2 +- .../opt/legalize_vector_shuffle_pass.cpp | 2 +- .../source/opt/legalize_vector_shuffle_pass.h | 2 +- 3rdparty/spirv-tools/source/opt/optimizer.cpp | 9 +- 3rdparty/spirv-tools/source/opt/passes.h | 1 + .../opt/strip_atomic_counter_memory_pass.cpp | 2 +- .../opt/strip_atomic_counter_memory_pass.h | 2 +- .../spirv-tools/source/reduce/CMakeLists.txt | 10 +- ..._conditional_branch_opportunity_finder.cpp | 89 ++++ ...le_conditional_branch_opportunity_finder.h | 37 ++ ...nditional_branch_reduction_opportunity.cpp | 54 ++ ...conditional_branch_reduction_opportunity.h | 52 ++ .../spirv-tools/source/reduce/reducer.cpp | 7 + .../source/reduce/reduction_util.cpp | 3 + .../source/reduce/reduction_util.h | 3 + ...selection_reduction_opportunity_finder.cpp | 2 +- ...al_branch_to_branch_opportunity_finder.cpp | 65 +++ ...onal_branch_to_branch_opportunity_finder.h | 37 ++ ...branch_to_branch_reduction_opportunity.cpp | 59 +++ ...l_branch_to_branch_reduction_opportunity.h | 45 ++ 3rdparty/spirv-tools/source/val/validate.cpp | 48 +- .../spirv-tools/source/val/validate_cfg.cpp | 32 -- .../source/val/validate_memory_semantics.cpp | 28 +- 3rdparty/spirv-tools/test/opt/CMakeLists.txt | 1 + .../decompose_initialized_variables_test.cpp | 85 +++ .../opt/generate_webgpu_initializers_test.cpp | 2 +- .../test/opt/legalize_vector_shuffle_test.cpp | 2 +- .../spirv-tools/test/opt/optimizer_test.cpp | 108 +++- .../opt/strip_atomic_counter_memory_test.cpp | 2 +- .../spirv-tools/test/reduce/CMakeLists.txt | 5 +- ...anch_to_simple_conditional_branch_test.cpp | 501 ++++++++++++++++++ ...mple_conditional_branch_to_branch_test.cpp | 486 +++++++++++++++++ .../spirv-tools/test/val/val_atomics_test.cpp | 50 +- .../spirv-tools/test/val/val_cfg_test.cpp | 41 +- .../spirv-tools/test/val/val_webgpu_test.cpp | 57 ++ 44 files changed, 1933 insertions(+), 163 deletions(-) create mode 100644 3rdparty/spirv-tools/source/opt/decompose_initialized_variables_pass.cpp create mode 100644 3rdparty/spirv-tools/source/opt/decompose_initialized_variables_pass.h create mode 100644 3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp create mode 100644 3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h create mode 100644 3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp create mode 100644 3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h create mode 100644 3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp create mode 100644 3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h create mode 100644 3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp create mode 100644 3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h create mode 100644 3rdparty/spirv-tools/test/opt/decompose_initialized_variables_test.cpp create mode 100644 3rdparty/spirv-tools/test/reduce/conditional_branch_to_simple_conditional_branch_test.cpp create mode 100644 3rdparty/spirv-tools/test/reduce/simple_conditional_branch_to_branch_test.cpp diff --git a/3rdparty/spirv-tools/Android.mk b/3rdparty/spirv-tools/Android.mk index ba54d6512..7c6a076d2 100644 --- a/3rdparty/spirv-tools/Android.mk +++ b/3rdparty/spirv-tools/Android.mk @@ -92,6 +92,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/dead_branch_elim_pass.cpp \ source/opt/dead_insert_elim_pass.cpp \ source/opt/dead_variable_elimination.cpp \ + source/opt/decompose_initialized_variables_pass.cpp \ source/opt/decoration_manager.cpp \ source/opt/def_use_manager.cpp \ source/opt/dominator_analysis.cpp \ diff --git a/3rdparty/spirv-tools/BUILD.gn b/3rdparty/spirv-tools/BUILD.gn index 7c869b09b..6c28118f6 100644 --- a/3rdparty/spirv-tools/BUILD.gn +++ b/3rdparty/spirv-tools/BUILD.gn @@ -484,6 +484,8 @@ static_library("spvtools_opt") { "source/opt/dead_insert_elim_pass.h", "source/opt/dead_variable_elimination.cpp", "source/opt/dead_variable_elimination.h", + "source/opt/decompose_initialized_variables_pass.cpp", + "source/opt/decompose_initialized_variables_pass.h", "source/opt/decoration_manager.cpp", "source/opt/decoration_manager.h", "source/opt/def_use_manager.cpp", diff --git a/3rdparty/spirv-tools/include/generated/build-version.inc b/3rdparty/spirv-tools/include/generated/build-version.inc index 27a9750ee..6a12433c7 100644 --- a/3rdparty/spirv-tools/include/generated/build-version.inc +++ b/3rdparty/spirv-tools/include/generated/build-version.inc @@ -1 +1 @@ -"v2019.3-dev", "SPIRV-Tools v2019.3-dev v2019.1-140-g102e430a" +"v2019.3-dev", "SPIRV-Tools v2019.3-dev v2019.1-147-g7aad9653" diff --git a/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp b/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp index 213e50b3d..af9f3e54a 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp +++ b/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp @@ -768,6 +768,10 @@ Optimizer::PassToken CreateFixStorageClassPass(); // converts those literals to 0. Optimizer::PassToken CreateLegalizeVectorShufflePass(); +// Create a pass to decompose initialized variables into a seperate variable +// declaration and an initial store. +Optimizer::PassToken CreateDecomposeInitializedVariablesPass(); + } // namespace spvtools #endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_ diff --git a/3rdparty/spirv-tools/source/CMakeLists.txt b/3rdparty/spirv-tools/source/CMakeLists.txt index 07e31e1e3..1f96018c6 100644 --- a/3rdparty/spirv-tools/source/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/CMakeLists.txt @@ -101,7 +101,7 @@ macro(spvtools_opencl_tables CONFIG_VERSION) list(APPEND EXTINST_CPP_DEPENDS ${GRAMMAR_INC_FILE}) endmacro(spvtools_opencl_tables) -macro(spvtools_vendor_tables VENDOR_TABLE) +macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME) set(INSTS_FILE "${spirv-tools_BINARY_DIR}/${VENDOR_TABLE}.insts.inc") set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json") add_custom_command(OUTPUT ${INSTS_FILE} @@ -110,9 +110,9 @@ macro(spvtools_vendor_tables VENDOR_TABLE) --vendor-insts-output=${INSTS_FILE} DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_FILE} COMMENT "Generate extended instruction tables for ${VENDOR_TABLE}.") - add_custom_target(spirv-tools-${VENDOR_TABLE} DEPENDS ${INSTS_FILE}) - set_property(TARGET spirv-tools-${VENDOR_TABLE} PROPERTY FOLDER "SPIRV-Tools build") - list(APPEND EXTINST_CPP_DEPENDS spirv-tools-${VENDOR_TABLE}) + add_custom_target(spv-tools-${SHORT_NAME} DEPENDS ${INSTS_FILE}) + set_property(TARGET spv-tools-${SHORT_NAME} PROPERTY FOLDER "SPIRV-Tools build") + list(APPEND EXTINST_CPP_DEPENDS spv-tools-${SHORT_NAME}) endmacro(spvtools_vendor_tables) macro(spvtools_extinst_lang_headers NAME GRAMMAR_FILE) @@ -134,11 +134,11 @@ spvtools_core_tables("unified1") spvtools_enum_string_mapping("unified1") spvtools_opencl_tables("unified1") spvtools_glsl_tables("unified1") -spvtools_vendor_tables("spv-amd-shader-explicit-vertex-parameter") -spvtools_vendor_tables("spv-amd-shader-trinary-minmax") -spvtools_vendor_tables("spv-amd-gcn-shader") -spvtools_vendor_tables("spv-amd-shader-ballot") -spvtools_vendor_tables("debuginfo") +spvtools_vendor_tables("spv-amd-shader-explicit-vertex-parameter" "spv-amd-sevp") +spvtools_vendor_tables("spv-amd-shader-trinary-minmax" "spv-amd-stm") +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_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE}) spvtools_vimsyntax("unified1" "1.0") diff --git a/3rdparty/spirv-tools/source/opt/CMakeLists.txt b/3rdparty/spirv-tools/source/opt/CMakeLists.txt index e270a9fae..b02485a71 100644 --- a/3rdparty/spirv-tools/source/opt/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/opt/CMakeLists.txt @@ -31,6 +31,7 @@ set(SPIRV_TOOLS_OPT_SOURCES dead_branch_elim_pass.h dead_insert_elim_pass.h dead_variable_elimination.h + decompose_initialized_variables_pass.h decoration_manager.h def_use_manager.h dominator_analysis.h @@ -130,6 +131,7 @@ set(SPIRV_TOOLS_OPT_SOURCES dead_branch_elim_pass.cpp dead_insert_elim_pass.cpp dead_variable_elimination.cpp + decompose_initialized_variables_pass.cpp decoration_manager.cpp def_use_manager.cpp dominator_analysis.cpp diff --git a/3rdparty/spirv-tools/source/opt/decompose_initialized_variables_pass.cpp b/3rdparty/spirv-tools/source/opt/decompose_initialized_variables_pass.cpp new file mode 100644 index 000000000..b2471cad2 --- /dev/null +++ b/3rdparty/spirv-tools/source/opt/decompose_initialized_variables_pass.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2019 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/decompose_initialized_variables_pass.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +using inst_iterator = InstructionList::iterator; + +namespace { + +bool HasInitializer(Instruction* inst) { + if (inst->opcode() != SpvOpVariable) return false; + if (inst->NumOperands() < 4) return false; + + return true; +} + +} // namespace + +Pass::Status DecomposeInitializedVariablesPass::Process() { + auto* module = context()->module(); + bool changed = false; + + // TODO(zoddicus): Handle 'Output' variables + // TODO(zoddicus): Handle 'Private' variables + + // Handle 'Function' variables + for (auto func = module->begin(); func != module->end(); ++func) { + auto block = func->entry().get(); + std::vector new_stores; + + auto last_var = block->begin(); + for (auto iter = block->begin(); + iter != block->end() && iter->opcode() == SpvOpVariable; ++iter) { + last_var = iter; + Instruction* inst = &(*iter); + if (!HasInitializer(inst)) continue; + + changed = true; + auto var_id = inst->result_id(); + auto val_id = inst->GetOperand(3).words[0]; + Instruction* store_inst = new Instruction( + context(), SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {var_id}}, {SPV_OPERAND_TYPE_ID, {val_id}}}); + new_stores.push_back(store_inst); + iter->RemoveOperand(3); + get_def_use_mgr()->UpdateDefUse(&*iter); + } + + for (auto store = new_stores.begin(); store != new_stores.end(); ++store) { + context()->AnalyzeDefUse(*store); + context()->set_instr_block(*store, block); + (*store)->InsertAfter(&*last_var); + last_var = *store; + } + } + + return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/opt/decompose_initialized_variables_pass.h b/3rdparty/spirv-tools/source/opt/decompose_initialized_variables_pass.h new file mode 100644 index 000000000..c0bd35e76 --- /dev/null +++ b/3rdparty/spirv-tools/source/opt/decompose_initialized_variables_pass.h @@ -0,0 +1,57 @@ +// Copyright (c) 2019 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_DECOMPOSE_INITALIZED_VAIRABLES_PASS_H_ +#define SOURCE_OPT_DECOMPOSE_INITALIZED_VAIRABLES_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Converts variable declartions with initializers into seperate declaration and +// assignment statements. This is done due to known issues with some Vulkan +// implementations' handling of initialized variables. +// +// Only decomposes variables with storage classes that are valid in Vulkan +// execution environments; Output, Private, and Function. +// Currently only Function is implemented. +class DecomposeInitializedVariablesPass : public Pass { + public: + const char* name() const override { + return "decompose-initialized-variables"; + } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisScalarEvolution | + IRContext::kAnalysisRegisterPressure | + IRContext::kAnalysisValueNumberTable | + IRContext::kAnalysisStructuredCFG | + IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisTypes | + IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DECOMPOSE_INITALIZED_VAIRABLES_PASS_H_ diff --git a/3rdparty/spirv-tools/source/opt/generate_webgpu_initializers_pass.cpp b/3rdparty/spirv-tools/source/opt/generate_webgpu_initializers_pass.cpp index a8e00b666..9334b43ee 100644 --- a/3rdparty/spirv-tools/source/opt/generate_webgpu_initializers_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/generate_webgpu_initializers_pass.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Google Inc. +// Copyright (c) 2019 Google LLC. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/3rdparty/spirv-tools/source/opt/generate_webgpu_initializers_pass.h b/3rdparty/spirv-tools/source/opt/generate_webgpu_initializers_pass.h index 9aa970dd9..f95e84c5b 100644 --- a/3rdparty/spirv-tools/source/opt/generate_webgpu_initializers_pass.h +++ b/3rdparty/spirv-tools/source/opt/generate_webgpu_initializers_pass.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Google Inc. +// Copyright (c) 2019 Google LLC. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/3rdparty/spirv-tools/source/opt/legalize_vector_shuffle_pass.cpp b/3rdparty/spirv-tools/source/opt/legalize_vector_shuffle_pass.cpp index dcaf26350..b5d5d5993 100644 --- a/3rdparty/spirv-tools/source/opt/legalize_vector_shuffle_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/legalize_vector_shuffle_pass.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Google Inc. +// Copyright (c) 2019 Google LLC. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/3rdparty/spirv-tools/source/opt/legalize_vector_shuffle_pass.h b/3rdparty/spirv-tools/source/opt/legalize_vector_shuffle_pass.h index 20ccbd7f8..ca6e1dfbe 100644 --- a/3rdparty/spirv-tools/source/opt/legalize_vector_shuffle_pass.h +++ b/3rdparty/spirv-tools/source/opt/legalize_vector_shuffle_pass.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Google Inc. +// Copyright (c) 2019 Google LLC. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/3rdparty/spirv-tools/source/opt/optimizer.cpp b/3rdparty/spirv-tools/source/opt/optimizer.cpp index 29c5466e8..34ddedca7 100644 --- a/3rdparty/spirv-tools/source/opt/optimizer.cpp +++ b/3rdparty/spirv-tools/source/opt/optimizer.cpp @@ -231,7 +231,9 @@ Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() { .RegisterPass(CreateDeadBranchElimPass()); } -Optimizer& Optimizer::RegisterWebGPUToVulkanPasses() { return *this; } +Optimizer& Optimizer::RegisterWebGPUToVulkanPasses() { + return RegisterPass(CreateDecomposeInitializedVariablesPass()); +} bool Optimizer::RegisterPassesFromFlags(const std::vector& flags) { for (const auto& flag : flags) { @@ -866,4 +868,9 @@ Optimizer::PassToken CreateLegalizeVectorShufflePass() { MakeUnique()); } +Optimizer::PassToken CreateDecomposeInitializedVariablesPass() { + return MakeUnique( + MakeUnique()); +} + } // namespace spvtools diff --git a/3rdparty/spirv-tools/source/opt/passes.h b/3rdparty/spirv-tools/source/opt/passes.h index c9f1ae840..06464b060 100644 --- a/3rdparty/spirv-tools/source/opt/passes.h +++ b/3rdparty/spirv-tools/source/opt/passes.h @@ -29,6 +29,7 @@ #include "source/opt/dead_branch_elim_pass.h" #include "source/opt/dead_insert_elim_pass.h" #include "source/opt/dead_variable_elimination.h" +#include "source/opt/decompose_initialized_variables_pass.h" #include "source/opt/eliminate_dead_constant_pass.h" #include "source/opt/eliminate_dead_functions_pass.h" #include "source/opt/eliminate_dead_members_pass.h" diff --git a/3rdparty/spirv-tools/source/opt/strip_atomic_counter_memory_pass.cpp b/3rdparty/spirv-tools/source/opt/strip_atomic_counter_memory_pass.cpp index 46a727cb7..47714b746 100644 --- a/3rdparty/spirv-tools/source/opt/strip_atomic_counter_memory_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/strip_atomic_counter_memory_pass.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Google Inc. +// Copyright (c) 2019 Google LLC. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/3rdparty/spirv-tools/source/opt/strip_atomic_counter_memory_pass.h b/3rdparty/spirv-tools/source/opt/strip_atomic_counter_memory_pass.h index d94cf324b..62e274a19 100644 --- a/3rdparty/spirv-tools/source/opt/strip_atomic_counter_memory_pass.h +++ b/3rdparty/spirv-tools/source/opt/strip_atomic_counter_memory_pass.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Google Inc. +// Copyright (c) 2019 Google LLC. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/3rdparty/spirv-tools/source/reduce/CMakeLists.txt b/3rdparty/spirv-tools/source/reduce/CMakeLists.txt index 4a5d54028..def4d2104 100644 --- a/3rdparty/spirv-tools/source/reduce/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/reduce/CMakeLists.txt @@ -35,6 +35,10 @@ set(SPIRV_TOOLS_REDUCE_SOURCES remove_unreferenced_instruction_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 change_operand_reduction_opportunity.cpp change_operand_to_undef_reduction_opportunity.cpp @@ -58,7 +62,11 @@ set(SPIRV_TOOLS_REDUCE_SOURCES remove_opname_instruction_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 +) if(MSVC) # Enable parallel builds across four cores for this lib diff --git a/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp b/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp new file mode 100644 index 000000000..2feca4acc --- /dev/null +++ b/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp @@ -0,0 +1,89 @@ +// Copyright (c) 2019 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/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h" + +#include "source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h" +#include "source/reduce/reduction_util.h" + +namespace spvtools { +namespace reduce { + +using opt::IRContext; +using opt::Instruction; + +std::vector> +ConditionalBranchToSimpleConditionalBranchOpportunityFinder:: + GetAvailableOpportunities(IRContext* context) const { + std::vector> result; + + // Find the opportunities for redirecting all false targets before the + // opportunities for redirecting all true targets because the former + // opportunities disable the latter, and vice versa, and the efficiency of the + // reducer is improved by avoiding contiguous opportunities that disable one + // another. + for (bool redirect_to_true : {true, false}) { + // Consider every function. + for (auto& function : *context->module()) { + // Consider every block in the function. + for (auto& block : function) { + // The terminator must be SpvOpBranchConditional. + Instruction* terminator = block.terminator(); + if (terminator->opcode() != SpvOpBranchConditional) { + continue; + } + + uint32_t true_block_id = + terminator->GetSingleWordInOperand(kTrueBranchOperandIndex); + uint32_t false_block_id = + terminator->GetSingleWordInOperand(kFalseBranchOperandIndex); + + // The conditional branch must not already be simplified. + if (true_block_id == false_block_id) { + continue; + } + + // The redirected target must not be a back-edge to a structured loop + // header. + uint32_t redirected_block_id = + redirect_to_true ? false_block_id : true_block_id; + uint32_t containing_loop_header = + context->GetStructuredCFGAnalysis()->ContainingLoop(block.id()); + // The structured CFG analysis does not include a loop header as part + // of the loop construct, but we want to include it, so handle this + // special case: + if (block.GetLoopMergeInst() != nullptr) { + containing_loop_header = block.id(); + } + if (redirected_block_id == containing_loop_header) { + continue; + } + + result.push_back( + MakeUnique< + ConditionalBranchToSimpleConditionalBranchReductionOpportunity>( + block.terminator(), redirect_to_true)); + } + } + } + return result; +} + +std::string +ConditionalBranchToSimpleConditionalBranchOpportunityFinder::GetName() const { + return "ConditionalBranchToSimpleConditionalBranchOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h b/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h new file mode 100644 index 000000000..c582a889e --- /dev/null +++ b/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h @@ -0,0 +1,37 @@ +// Copyright (c) 2019 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_REDUCE_SIMPLIFY_SELECTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_SIMPLIFY_SELECTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to simplify conditional branches into simple +// conditional branches (conditional branches with one target). +class ConditionalBranchToSimpleConditionalBranchOpportunityFinder + : public ReductionOpportunityFinder { + public: + std::vector> GetAvailableOpportunities( + opt::IRContext* context) const override; + + std::string GetName() const override; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_SIMPLIFY_SELECTION_OPPORTUNITY_FINDER_H_ diff --git a/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp b/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp new file mode 100644 index 000000000..92c621e08 --- /dev/null +++ b/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2019 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/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h" + +#include "source/reduce/reduction_util.h" + +namespace spvtools { +namespace reduce { + +using opt::Instruction; + +ConditionalBranchToSimpleConditionalBranchReductionOpportunity:: + ConditionalBranchToSimpleConditionalBranchReductionOpportunity( + Instruction* conditional_branch_instruction, bool redirect_to_true) + : conditional_branch_instruction_(conditional_branch_instruction), + redirect_to_true_(redirect_to_true) {} + +bool ConditionalBranchToSimpleConditionalBranchReductionOpportunity:: + PreconditionHolds() { + // Another opportunity may have already simplified this conditional branch, + // which should disable this opportunity. + return conditional_branch_instruction_->GetSingleWordInOperand( + kTrueBranchOperandIndex) != + conditional_branch_instruction_->GetSingleWordInOperand( + kFalseBranchOperandIndex); +} + +void ConditionalBranchToSimpleConditionalBranchReductionOpportunity::Apply() { + uint32_t operand_to_modify = + redirect_to_true_ ? kFalseBranchOperandIndex : kTrueBranchOperandIndex; + uint32_t operand_to_copy = + redirect_to_true_ ? kTrueBranchOperandIndex : kFalseBranchOperandIndex; + + // Do the branch redirection. + conditional_branch_instruction_->SetInOperand( + operand_to_modify, + {conditional_branch_instruction_->GetSingleWordInOperand( + operand_to_copy)}); +} + +} // namespace reduce +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h b/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h new file mode 100644 index 000000000..421906b44 --- /dev/null +++ b/3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h @@ -0,0 +1,52 @@ +// Copyright (c) 2019 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_REDUCE_SIMPLIFY_CONDITIONAL_BRANCH_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_SIMPLIFY_CONDITIONAL_BRANCH_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/basic_block.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to simplify a conditional branch to a simple conditional +// branch (a conditional branch with one target). +class ConditionalBranchToSimpleConditionalBranchReductionOpportunity + : public ReductionOpportunity { + public: + // Constructs an opportunity to simplify |conditional_branch_instruction|. If + // |redirect_to_true| is true, the false target will be changed to also point + // to the true target; otherwise, the true target will be changed to also + // point to the false target. + explicit ConditionalBranchToSimpleConditionalBranchReductionOpportunity( + opt::Instruction* conditional_branch_instruction, bool redirect_to_true); + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::Instruction* conditional_branch_instruction_; + + // If true, the false target will be changed to point to the true target; + // otherwise, the true target will be changed to point to the false target. + bool redirect_to_true_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_SIMPLIFY_CONDITIONAL_BRANCH_REDUCTION_OPPORTUNITY_H_ diff --git a/3rdparty/spirv-tools/source/reduce/reducer.cpp b/3rdparty/spirv-tools/source/reduce/reducer.cpp index e0fa84b7b..a677be35b 100644 --- a/3rdparty/spirv-tools/source/reduce/reducer.cpp +++ b/3rdparty/spirv-tools/source/reduce/reducer.cpp @@ -17,6 +17,7 @@ #include #include +#include "source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h" #include "source/reduce/merge_blocks_reduction_opportunity_finder.h" #include "source/reduce/operand_to_const_reduction_opportunity_finder.h" #include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h" @@ -26,6 +27,7 @@ #include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h" #include "source/reduce/remove_selection_reduction_opportunity_finder.h" #include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h" +#include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h" #include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h" #include "source/spirv_reducer_options.h" @@ -191,6 +193,11 @@ void Reducer::AddDefaultReductionPasses() { spvtools::MakeUnique()); AddReductionPass( spvtools::MakeUnique()); + AddReductionPass( + spvtools::MakeUnique< + ConditionalBranchToSimpleConditionalBranchOpportunityFinder>()); + AddReductionPass( + spvtools::MakeUnique()); } void Reducer::AddReductionPass( diff --git a/3rdparty/spirv-tools/source/reduce/reduction_util.cpp b/3rdparty/spirv-tools/source/reduce/reduction_util.cpp index 4a6e75a25..2b2b7e610 100644 --- a/3rdparty/spirv-tools/source/reduce/reduction_util.cpp +++ b/3rdparty/spirv-tools/source/reduce/reduction_util.cpp @@ -22,6 +22,9 @@ namespace reduce { using opt::IRContext; using opt::Instruction; +const uint32_t kTrueBranchOperandIndex = 1; +const uint32_t kFalseBranchOperandIndex = 2; + uint32_t FindOrCreateGlobalUndef(IRContext* context, uint32_t type_id) { for (auto& inst : context->module()->types_values()) { if (inst.opcode() != SpvOpUndef) { diff --git a/3rdparty/spirv-tools/source/reduce/reduction_util.h b/3rdparty/spirv-tools/source/reduce/reduction_util.h index d8efc9705..b8ffb6e46 100644 --- a/3rdparty/spirv-tools/source/reduce/reduction_util.h +++ b/3rdparty/spirv-tools/source/reduce/reduction_util.h @@ -23,6 +23,9 @@ namespace spvtools { namespace reduce { +extern const uint32_t kTrueBranchOperandIndex; +extern const uint32_t kFalseBranchOperandIndex; + // Returns an OpUndef id from the global value list that is of the given type, // adding one if it does not exist. uint32_t FindOrCreateGlobalUndef(opt::IRContext* context, uint32_t type_id); diff --git a/3rdparty/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.cpp b/3rdparty/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.cpp index 45cf26dd7..45821e2af 100644 --- a/3rdparty/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.cpp +++ b/3rdparty/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Google Inc. +// Copyright (c) 2019 Google LLC. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp b/3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp new file mode 100644 index 000000000..17a5c7e49 --- /dev/null +++ b/3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp @@ -0,0 +1,65 @@ +// Copyright (c) 2019 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/reduce/simple_conditional_branch_to_branch_opportunity_finder.h" + +#include "source/reduce/reduction_util.h" +#include "source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +using opt::IRContext; +using opt::Instruction; + +std::vector> +SimpleConditionalBranchToBranchOpportunityFinder::GetAvailableOpportunities( + IRContext* context) const { + std::vector> result; + + // Consider every function. + for (auto& function : *context->module()) { + // Consider every block in the function. + for (auto& block : function) { + // The terminator must be SpvOpBranchConditional. + Instruction* terminator = block.terminator(); + if (terminator->opcode() != SpvOpBranchConditional) { + continue; + } + // It must not be a selection header, as these cannot be followed by + // OpBranch. + if (block.GetMergeInst() && + block.GetMergeInst()->opcode() == SpvOpSelectionMerge) { + continue; + } + // The conditional branch must be simplified. + if (terminator->GetSingleWordInOperand(kTrueBranchOperandIndex) != + terminator->GetSingleWordInOperand(kFalseBranchOperandIndex)) { + continue; + } + + result.push_back( + MakeUnique( + block.terminator())); + } + } + return result; +} + +std::string SimpleConditionalBranchToBranchOpportunityFinder::GetName() const { + return "SimpleConditionalBranchToBranchOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h b/3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h new file mode 100644 index 000000000..10b9dce4a --- /dev/null +++ b/3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h @@ -0,0 +1,37 @@ +// Copyright (c) 2019 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_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to change simple conditional branches (conditional +// branches with one target) to an OpBranch. +class SimpleConditionalBranchToBranchOpportunityFinder + : public ReductionOpportunityFinder { + public: + std::vector> GetAvailableOpportunities( + opt::IRContext* context) const override; + + std::string GetName() const override; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_OPPORTUNITY_FINDER_H_ diff --git a/3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp b/3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp new file mode 100644 index 000000000..8968b9627 --- /dev/null +++ b/3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2019 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/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h" + +#include "source/reduce/reduction_util.h" + +namespace spvtools { +namespace reduce { + +using namespace opt; + +SimpleConditionalBranchToBranchReductionOpportunity:: + SimpleConditionalBranchToBranchReductionOpportunity( + Instruction* conditional_branch_instruction) + : conditional_branch_instruction_(conditional_branch_instruction) {} + +bool SimpleConditionalBranchToBranchReductionOpportunity::PreconditionHolds() { + // We find at most one opportunity per conditional branch and simplifying + // another branch cannot disable this opportunity. + return true; +} + +void SimpleConditionalBranchToBranchReductionOpportunity::Apply() { + assert(conditional_branch_instruction_->opcode() == SpvOpBranchConditional && + "SimpleConditionalBranchToBranchReductionOpportunity: branch was not " + "a conditional branch"); + + assert(conditional_branch_instruction_->GetSingleWordInOperand( + kTrueBranchOperandIndex) == + conditional_branch_instruction_->GetSingleWordInOperand( + kFalseBranchOperandIndex) && + "SimpleConditionalBranchToBranchReductionOpportunity: branch was not " + "simple"); + + // OpBranchConditional %condition %block_id %block_id ... + // -> + // OpBranch %block_id + + conditional_branch_instruction_->SetOpcode(SpvOpBranch); + conditional_branch_instruction_->ReplaceOperands( + {{SPV_OPERAND_TYPE_ID, + {conditional_branch_instruction_->GetSingleWordInOperand( + kTrueBranchOperandIndex)}}}); +} + +} // namespace reduce +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h b/3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h new file mode 100644 index 000000000..eddb4647e --- /dev/null +++ b/3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h @@ -0,0 +1,45 @@ +// Copyright (c) 2019 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_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/instruction.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to change simple conditional branches (conditional branches +// with one target) to an OpBranch. +class SimpleConditionalBranchToBranchReductionOpportunity + : public ReductionOpportunity { + public: + // Constructs an opportunity to simplify |conditional_branch_instruction|. + explicit SimpleConditionalBranchToBranchReductionOpportunity( + opt::Instruction* conditional_branch_instruction); + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::Instruction* conditional_branch_instruction_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_REDUCTION_OPPORTUNITY_H_ diff --git a/3rdparty/spirv-tools/source/val/validate.cpp b/3rdparty/spirv-tools/source/val/validate.cpp index b145b19d8..9e8d7fbfd 100644 --- a/3rdparty/spirv-tools/source/val/validate.cpp +++ b/3rdparty/spirv-tools/source/val/validate.cpp @@ -110,48 +110,6 @@ spv_result_t ProcessInstruction(void* user_data, return SPV_SUCCESS; } -void printDot(const ValidationState_t& _, const BasicBlock& other) { - std::string block_string; - if (other.successors()->empty()) { - block_string += "end "; - } else { - for (auto block : *other.successors()) { - block_string += _.getIdName(block->id()) + " "; - } - } - printf("%10s -> {%s\b}\n", _.getIdName(other.id()).c_str(), - block_string.c_str()); -} - -void PrintBlocks(ValidationState_t& _, Function func) { - assert(func.first_block()); - - printf("%10s -> %s\n", _.getIdName(func.id()).c_str(), - _.getIdName(func.first_block()->id()).c_str()); - for (const auto& block : func.ordered_blocks()) { - printDot(_, *block); - } -} - -#ifdef __clang__ -#define UNUSED(func) [[gnu::unused]] func -#elif defined(__GNUC__) -#define UNUSED(func) \ - func __attribute__((unused)); \ - func -#elif defined(_MSC_VER) -#define UNUSED(func) func -#endif - -UNUSED(void PrintDotGraph(ValidationState_t& _, Function func)) { - if (func.first_block()) { - std::string func_name(_.getIdName(func.id())); - printf("digraph %s {\n", func_name.c_str()); - PrintBlocks(_, func); - printf("}\n"); - } -} - spv_result_t ValidateForwardDecls(ValidationState_t& _) { if (_.unresolved_forward_id_count() == 0) return SPV_SUCCESS; @@ -281,6 +239,12 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( << "Invalid SPIR-V magic number."; } + if (spvIsWebGPUEnv(context.target_env) && endian != SPV_ENDIANNESS_LITTLE) { + return DiagnosticStream(position, context.consumer, "", + SPV_ERROR_INVALID_BINARY) + << "WebGPU requires SPIR-V to be little endian."; + } + spv_header_t header; if (spvBinaryHeaderGet(binary.get(), endian, &header)) { return DiagnosticStream(position, context.consumer, "", diff --git a/3rdparty/spirv-tools/source/val/validate_cfg.cpp b/3rdparty/spirv-tools/source/val/validate_cfg.cpp index 1d6f920d1..6e7acbd6a 100644 --- a/3rdparty/spirv-tools/source/val/validate_cfg.cpp +++ b/3rdparty/spirv-tools/source/val/validate_cfg.cpp @@ -725,36 +725,6 @@ spv_result_t PerformWebGPUCfgChecks(ValidationState_t& _, Function* function) { return SPV_SUCCESS; } -// Checks that there are no OpUnreachable instructions reachable from the entry -// block of |function|. -spv_result_t ValidateUnreachableBlocks(ValidationState_t& _, - Function& function) { - auto* entry_block = function.first_block(); - std::vector stack; - std::unordered_set seen; - if (entry_block) stack.push_back(entry_block); - - while (!stack.empty()) { - auto* block = stack.back(); - stack.pop_back(); - - if (!seen.insert(block).second) continue; - - auto* terminator = block->terminator(); - if (terminator->opcode() == SpvOpUnreachable) { - return _.diag(SPV_ERROR_INVALID_CFG, block->label()) - << "Statically reachable blocks cannot be terminated by " - "OpUnreachable"; - } - - for (auto* succ : *block->successors()) { - stack.push_back(succ); - } - } - - return SPV_SUCCESS; -} - spv_result_t PerformCfgChecks(ValidationState_t& _) { for (auto& function : _.functions()) { // Check all referenced blocks are defined within a function @@ -857,8 +827,6 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) { } } - if (auto error = ValidateUnreachableBlocks(_, function)) return error; - /// Structured control flow checks are only required for shader capabilities if (_.HasCapability(SpvCapabilityShader)) { if (auto error = StructuredControlFlowChecks(_, &function, back_edges)) diff --git a/3rdparty/spirv-tools/source/val/validate_memory_semantics.cpp b/3rdparty/spirv-tools/source/val/validate_memory_semantics.cpp index f90b5e4cd..5dece3f65 100644 --- a/3rdparty/spirv-tools/source/val/validate_memory_semantics.cpp +++ b/3rdparty/spirv-tools/source/val/validate_memory_semantics.cpp @@ -48,21 +48,31 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _, } if (spvIsWebGPUEnv(_.context()->target_env)) { - uint32_t valid_bits = SpvMemorySemanticsAcquireMask | - SpvMemorySemanticsReleaseMask | - SpvMemorySemanticsAcquireReleaseMask | - SpvMemorySemanticsUniformMemoryMask | + uint32_t valid_bits = SpvMemorySemanticsUniformMemoryMask | SpvMemorySemanticsWorkgroupMemoryMask | SpvMemorySemanticsImageMemoryMask | SpvMemorySemanticsOutputMemoryKHRMask | SpvMemorySemanticsMakeAvailableKHRMask | SpvMemorySemanticsMakeVisibleKHRMask; + if (!spvOpcodeIsAtomicOp(inst->opcode())) { + valid_bits |= SpvMemorySemanticsAcquireMask | + SpvMemorySemanticsReleaseMask | + SpvMemorySemanticsAcquireReleaseMask; + } + if (value & ~valid_bits) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "WebGPU spec disallows any bit masks in Memory Semantics that " - "are not Acquire, Release, AcquireRelease, UniformMemory, " - "WorkgroupMemory, ImageMemory, OutputMemoryKHR, " - "MakeAvailableKHR, or MakeVisibleKHR"; + if (spvOpcodeIsAtomicOp(inst->opcode())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "WebGPU spec disallows, for OpAtomic*, any bit masks in " + "Memory Semantics that are not UniformMemory, " + "WorkgroupMemory, ImageMemory, or OutputMemoryKHR"; + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "WebGPU spec disallows any bit masks in Memory Semantics " + "that are not Acquire, Release, AcquireRelease, " + "UniformMemory, WorkgroupMemory, ImageMemory, " + "OutputMemoryKHR, MakeAvailableKHR, or MakeVisibleKHR"; + } } } diff --git a/3rdparty/spirv-tools/test/opt/CMakeLists.txt b/3rdparty/spirv-tools/test/opt/CMakeLists.txt index f67f9b643..c1b1b2745 100644 --- a/3rdparty/spirv-tools/test/opt/CMakeLists.txt +++ b/3rdparty/spirv-tools/test/opt/CMakeLists.txt @@ -30,6 +30,7 @@ add_spvtools_unittest(TARGET opt dead_branch_elim_test.cpp dead_insert_elim_test.cpp dead_variable_elim_test.cpp + decompose_initialized_variables_test.cpp decoration_manager_test.cpp def_use_test.cpp eliminate_dead_const_test.cpp diff --git a/3rdparty/spirv-tools/test/opt/decompose_initialized_variables_test.cpp b/3rdparty/spirv-tools/test/opt/decompose_initialized_variables_test.cpp new file mode 100644 index 000000000..188e8b232 --- /dev/null +++ b/3rdparty/spirv-tools/test/opt/decompose_initialized_variables_test.cpp @@ -0,0 +1,85 @@ +// Copyright (c) 2019 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 + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using DecomposeInitializedVariablesTest = PassTest<::testing::Test>; + +void operator+=(std::vector& lhs, + const std::vector rhs) { + for (auto elem : rhs) lhs.push_back(elem); +} + +std::vector header = { + "OpCapability Shader", + "OpCapability VulkanMemoryModelKHR", + "OpExtension \"SPV_KHR_vulkan_memory_model\"", + "OpMemoryModel Logical VulkanKHR", + "OpEntryPoint Vertex %1 \"shader\"", + "%uint = OpTypeInt 32 0", + "%uint_1 = OpConstant %uint 1", + "%4 = OpConstantNull %uint", + "%void = OpTypeVoid", + "%6 = OpTypeFunction %void"}; + +std::string GetFunctionTest(std::vector body) { + auto result = header; + result += {"%_ptr_Function_uint = OpTypePointer Function %uint", + "%1 = OpFunction %void None %6", "%8 = OpLabel"}; + result += body; + result += {"OpReturn", "OpFunctionEnd"}; + return JoinAllInsts(result); +} + +TEST_F(DecomposeInitializedVariablesTest, FunctionChanged) { + std::string input = + GetFunctionTest({"%9 = OpVariable %_ptr_Function_uint Function %uint_1"}); + std::string expected = GetFunctionTest( + {"%9 = OpVariable %_ptr_Function_uint Function", "OpStore %9 %uint_1"}); + + SinglePassRunAndCheck( + input, expected, /* skip_nop = */ false); +} + +TEST_F(DecomposeInitializedVariablesTest, FunctionUnchanged) { + std::string input = + GetFunctionTest({"%9 = OpVariable %_ptr_Function_uint Function"}); + + SinglePassRunAndCheck( + input, input, /* skip_nop = */ false); +} + +TEST_F(DecomposeInitializedVariablesTest, FunctionMultipleVariables) { + std::string input = + GetFunctionTest({"%9 = OpVariable %_ptr_Function_uint Function %uint_1", + "%10 = OpVariable %_ptr_Function_uint Function %4"}); + std::string expected = + GetFunctionTest({"%9 = OpVariable %_ptr_Function_uint Function", + "%10 = OpVariable %_ptr_Function_uint Function", + "OpStore %9 %uint_1", "OpStore %10 %4"}); + + SinglePassRunAndCheck( + input, expected, /* skip_nop = */ false); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/opt/generate_webgpu_initializers_test.cpp b/3rdparty/spirv-tools/test/opt/generate_webgpu_initializers_test.cpp index 3e4be55f9..f35cf56d4 100644 --- a/3rdparty/spirv-tools/test/opt/generate_webgpu_initializers_test.cpp +++ b/3rdparty/spirv-tools/test/opt/generate_webgpu_initializers_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Google Inc. +// Copyright (c) 2019 Google LLC. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/3rdparty/spirv-tools/test/opt/legalize_vector_shuffle_test.cpp b/3rdparty/spirv-tools/test/opt/legalize_vector_shuffle_test.cpp index 4d3d7a2ad..8b9695b9a 100644 --- a/3rdparty/spirv-tools/test/opt/legalize_vector_shuffle_test.cpp +++ b/3rdparty/spirv-tools/test/opt/legalize_vector_shuffle_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Google Inc. +// Copyright (c) 2019 Google LLC. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/3rdparty/spirv-tools/test/opt/optimizer_test.cpp b/3rdparty/spirv-tools/test/opt/optimizer_test.cpp index a3f695733..549335d88 100644 --- a/3rdparty/spirv-tools/test/opt/optimizer_test.cpp +++ b/3rdparty/spirv-tools/test/opt/optimizer_test.cpp @@ -222,7 +222,7 @@ TEST(Optimizer, CanRegisterPassesFromFlags) { EXPECT_EQ(msg_level, SPV_MSG_ERROR); } -TEST(Optimizer, VulkanToWebGPUModeSetsCorrectPasses) { +TEST(Optimizer, VulkanToWebGPUSetsCorrectPasses) { Optimizer opt(SPV_ENV_WEBGPU_0); opt.RegisterVulkanToWebGPUPasses(); std::vector pass_names = opt.GetPassNames(); @@ -260,9 +260,11 @@ using VulkanToWebGPUPassTest = PassTest<::testing::TestWithParam>; TEST_P(VulkanToWebGPUPassTest, Ran) { - SpirvTools tools(SPV_ENV_WEBGPU_0); std::vector binary; - tools.Assemble(GetParam().input, &binary); + { + SpirvTools tools(SPV_ENV_VULKAN_1_1); + tools.Assemble(GetParam().input, &binary); + } Optimizer opt(SPV_ENV_WEBGPU_0); opt.RegisterVulkanToWebGPUPasses(); @@ -272,7 +274,10 @@ TEST_P(VulkanToWebGPUPassTest, Ran) { ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized, validator_options, true)); std::string disassembly; - tools.Disassemble(optimized.data(), optimized.size(), &disassembly); + { + SpirvTools tools(SPV_ENV_WEBGPU_0); + tools.Disassemble(optimized.data(), optimized.size(), &disassembly); + } EXPECT_EQ(GetParam().expected, disassembly) << "Was expecting pass '" << GetParam().pass << "' to have been run.\n"; @@ -521,6 +526,101 @@ INSTANTIATE_TEST_SUITE_P( // pass "legalize-vector-shuffle"}})); +TEST(Optimizer, WebGPUToVulkanSetsCorrectPasses) { + Optimizer opt(SPV_ENV_VULKAN_1_1); + opt.RegisterWebGPUToVulkanPasses(); + std::vector pass_names = opt.GetPassNames(); + + std::vector registered_passes; + for (auto name = pass_names.begin(); name != pass_names.end(); ++name) + registered_passes.push_back(*name); + + std::vector expected_passes = { + "decompose-initialized-variables"}; + std::sort(registered_passes.begin(), registered_passes.end()); + std::sort(expected_passes.begin(), expected_passes.end()); + + ASSERT_EQ(registered_passes.size(), expected_passes.size()); + for (size_t i = 0; i < registered_passes.size(); i++) + EXPECT_EQ(registered_passes[i], expected_passes[i]); +} + +struct WebGPUToVulkanPassCase { + // Input SPIR-V + std::string input; + // Expected result SPIR-V + std::string expected; + // Specific pass under test, used for logging messages. + std::string pass; +}; + +using WebGPUToVulkanPassTest = + PassTest<::testing::TestWithParam>; + +TEST_P(WebGPUToVulkanPassTest, Ran) { + std::vector binary; + { + SpirvTools tools(SPV_ENV_WEBGPU_0); + tools.Assemble(GetParam().input, &binary); + } + + Optimizer opt(SPV_ENV_VULKAN_1_1); + opt.RegisterWebGPUToVulkanPasses(); + + std::vector optimized; + class ValidatorOptions validator_options; + ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized, + validator_options, true)); + std::string disassembly; + { + SpirvTools tools(SPV_ENV_VULKAN_1_1); + tools.Disassemble(optimized.data(), optimized.size(), &disassembly); + } + + EXPECT_EQ(GetParam().expected, disassembly) + << "Was expecting pass '" << GetParam().pass << "' to have been run.\n"; +} + +INSTANTIATE_TEST_SUITE_P( + Optimizer, WebGPUToVulkanPassTest, + ::testing::ValuesIn(std::vector{ + // Decompose Initialized Variables + {// input + "OpCapability Shader\n" + "OpCapability VulkanMemoryModelKHR\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical VulkanKHR\n" + "OpEntryPoint Vertex %1 \"shader\"\n" + "%uint = OpTypeInt 32 0\n" + "%_ptr_Function_uint = OpTypePointer Function %uint\n" + "%4 = OpConstantNull %uint\n" + "%void = OpTypeVoid\n" + "%6 = OpTypeFunction %void\n" + "%1 = OpFunction %void None %6\n" + "%7 = OpLabel\n" + "%8 = OpVariable %_ptr_Function_uint Function %4\n" + "OpReturn\n" + "OpFunctionEnd\n", + // expected + "OpCapability Shader\n" + "OpCapability VulkanMemoryModelKHR\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical VulkanKHR\n" + "OpEntryPoint Vertex %1 \"shader\"\n" + "%uint = OpTypeInt 32 0\n" + "%_ptr_Function_uint = OpTypePointer Function %uint\n" + "%4 = OpConstantNull %uint\n" + "%void = OpTypeVoid\n" + "%6 = OpTypeFunction %void\n" + "%1 = OpFunction %void None %6\n" + "%7 = OpLabel\n" + "%8 = OpVariable %_ptr_Function_uint Function\n" + "OpStore %8 %4\n" + "OpReturn\n" + "OpFunctionEnd\n", + // pass + "decompose-initialized-variables"}})); + } // namespace } // namespace opt } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/opt/strip_atomic_counter_memory_test.cpp b/3rdparty/spirv-tools/test/opt/strip_atomic_counter_memory_test.cpp index 8756e815a..6287a13b0 100644 --- a/3rdparty/spirv-tools/test/opt/strip_atomic_counter_memory_test.cpp +++ b/3rdparty/spirv-tools/test/opt/strip_atomic_counter_memory_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Google Inc. +// Copyright (c) 2019 Google LLC. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/3rdparty/spirv-tools/test/reduce/CMakeLists.txt b/3rdparty/spirv-tools/test/reduce/CMakeLists.txt index ce8c0034b..964abdd5e 100644 --- a/3rdparty/spirv-tools/test/reduce/CMakeLists.txt +++ b/3rdparty/spirv-tools/test/reduce/CMakeLists.txt @@ -13,7 +13,8 @@ # limitations under the License. add_spvtools_unittest(TARGET reduce - SRCS merge_blocks_test.cpp + SRCS + merge_blocks_test.cpp operand_to_constant_test.cpp operand_to_undef_test.cpp operand_to_dominating_id_test.cpp @@ -27,6 +28,8 @@ add_spvtools_unittest(TARGET reduce remove_unreferenced_instruction_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/3rdparty/spirv-tools/test/reduce/conditional_branch_to_simple_conditional_branch_test.cpp b/3rdparty/spirv-tools/test/reduce/conditional_branch_to_simple_conditional_branch_test.cpp new file mode 100644 index 000000000..69ef1f48b --- /dev/null +++ b/3rdparty/spirv-tools/test/reduce/conditional_branch_to_simple_conditional_branch_test.cpp @@ -0,0 +1,501 @@ +// Copyright (c) 2019 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/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "source/reduce/reduction_pass.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3; + +TEST(ConditionalBranchToSimpleConditionalBranchTest, Diamond) { + // A test with the following structure. + // + // selection header + // OpBranchConditional + // | | + // b b + // | | + // selection merge + // + // There should be two opportunities for redirecting the OpBranchConditional + // targets: redirecting the true to false, and vice-versa. E.g. false to true: + // + // selection header + // OpBranchConditional + // || + // b b + // | | + // selection merge + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %8 %12 %13 + %12 = OpLabel + OpBranch %11 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + + )"; + + auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + + ASSERT_EQ(2, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[0]->TryToApply(); + // The other opportunity should now be disabled. + ASSERT_FALSE(ops[1]->PreconditionHolds()); + + CheckValid(kEnv, context.get()); + + { + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %8 %12 %12 + %12 = OpLabel + OpBranch %11 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(kEnv, after, context.get()); + } + + ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + ASSERT_EQ(0, ops.size()); + + // Start again, and apply the other op. + context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + ASSERT_EQ(2, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + // The other opportunity should now be disabled. + ASSERT_FALSE(ops[0]->PreconditionHolds()); + + CheckValid(kEnv, context.get()); + + { + std::string after2 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %8 %13 %13 + %12 = OpLabel + OpBranch %11 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(kEnv, after2, context.get()); + } +} + +TEST(ConditionalBranchToSimpleConditionalBranchTest, AlreadySimplified) { + // A test with the following structure. + // + // selection header + // OpBranchConditional + // || + // b b + // | | + // selection merge + // + // There should be no opportunities for redirecting the OpBranchConditional + // as it is already simplified. + // + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %8 %12 %12 + %12 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + + )"; + + auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + + ASSERT_EQ(0, ops.size()); +} + +TEST(ConditionalBranchToSimpleConditionalBranchTest, DontRemoveBackEdge) { + // A test with the following structure. The loop has a continue construct that + // ends with OpBranchConditional. The OpBranchConditional can be simplified, + // but only to point to the loop header, otherwise we have removed the + // back-edge. Thus, there should be one opportunity instead of two. + // + // loop header + // | + // loop continue target and back-edge block + // OpBranchConditional + // | | + // loop merge (to loop header^) + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %8 %11 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto context = + BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(kEnv, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %8 %10 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(kEnv, after, context.get()); + + ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + ASSERT_EQ(0, ops.size()); +} + +TEST(ConditionalBranchToSimpleConditionalBranchTest, + DontRemoveBackEdgeCombinedHeaderContinue) { + // A test with the following structure. + // + // loop header and continue target and back-edge block + // OpBranchConditional + // | | + // loop merge (to loop header^) + // + // The OpBranchConditional-to-header edge must not be removed, so there should + // only be one opportunity. It should change both targets to be to the loop + // header. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %10 None + OpBranchConditional %8 %11 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto context = + BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(kEnv, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %10 None + OpBranchConditional %8 %10 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(kEnv, after, context.get()); + + ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + ASSERT_EQ(0, ops.size()); +} + +TEST(ConditionalBranchToSimpleConditionalBranchTest, BackEdgeUnreachable) { + // A test with the following structure. I.e. a loop with an unreachable + // continue construct that ends with OpBranchConditional. + // + // loop header + // | + // | loop continue target (unreachable) + // | | + // | back-edge block (unreachable) + // | OpBranchConditional + // | | | + // loop merge (to loop header^) + // + // The branch to the loop header must not be removed, even though the continue + // construct is unreachable. So there should only be one opportunity to make + // the true and false targets of the OpBranchConditional to point to the loop + // header. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %11 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranchConditional %8 %11 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto context = + BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(kEnv, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %11 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranchConditional %8 %10 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(kEnv, after, context.get()); + + ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + ASSERT_EQ(0, ops.size()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/reduce/simple_conditional_branch_to_branch_test.cpp b/3rdparty/spirv-tools/test/reduce/simple_conditional_branch_to_branch_test.cpp new file mode 100644 index 000000000..d55e69103 --- /dev/null +++ b/3rdparty/spirv-tools/test/reduce/simple_conditional_branch_to_branch_test.cpp @@ -0,0 +1,486 @@ +// Copyright (c) 2019 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/reduce/simple_conditional_branch_to_branch_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "source/reduce/reduction_pass.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3; + +TEST(SimpleConditionalBranchToBranchTest, Diamond) { + // A test with the following structure. + // + // selection header + // OpBranchConditional + // || + // b b + // | | + // selection merge + // + // The conditional branch cannot be simplified because selection headers + // cannot end with OpBranch. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %8 %12 %12 + %12 = OpLabel + OpBranch %11 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + + )"; + + auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + + ASSERT_EQ(0, ops.size()); +} + +TEST(SimpleConditionalBranchToBranchTest, DiamondNoSelection) { + // A test with the following structure. + // + // OpBranchConditional + // || + // b b + // | / + // b + // + // The conditional branch can be simplified. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranchConditional %8 %12 %12 + %12 = OpLabel + OpBranch %11 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(kEnv, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranch %12 + %12 = OpLabel + OpBranch %11 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, after, context.get()); + + ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + ASSERT_EQ(0, ops.size()); +} + +TEST(SimpleConditionalBranchToBranchTest, ConditionalBranchesButNotSimple) { + // A test with the following structure. + // + // selection header + // OpBranchConditional + // | | + // b OpBranchConditional + // | | | + // | b | + // | | | + // selection merge + // + // None of the conditional branches can be simplified; the first is not simple + // AND part of a selection header; the second is just not simple (where + // "simple" means it only has one target). + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %8 %12 %13 + %12 = OpLabel + OpBranch %11 + %13 = OpLabel + OpBranchConditional %8 %14 %11 + %14 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + + ASSERT_EQ(0, ops.size()); +} + +TEST(SimpleConditionalBranchToBranchTest, SimplifyBackEdge) { + // A test with the following structure. The loop has a continue construct that + // ends with OpBranchConditional. The OpBranchConditional can be simplified. + // + // loop header + // | + // loop continue target and back-edge block + // OpBranchConditional + // || + // loop merge (to loop header^) + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %8 %10 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto context = + BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(kEnv, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %12 + %12 = OpLabel + OpBranch %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(kEnv, after, context.get()); + + ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + ASSERT_EQ(0, ops.size()); +} + +TEST(SimpleConditionalBranchToBranchTest, + DontRemoveBackEdgeCombinedHeaderContinue) { + // A test with the following structure. + // + // loop header and continue target and back-edge block + // OpBranchConditional + // || + // loop merge (to loop header^) + // + // The conditional branch can be simplified. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %10 None + OpBranchConditional %8 %10 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto context = + BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(kEnv, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %10 None + OpBranch %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(kEnv, after, context.get()); + + ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + ASSERT_EQ(0, ops.size()); +} + +TEST(SimpleConditionalBranchToBranchTest, BackEdgeUnreachable) { + // A test with the following structure. I.e. a loop with an unreachable + // continue construct that ends with OpBranchConditional. + // + // loop header + // | + // | loop continue target (unreachable) + // | | + // | back-edge block (unreachable) + // | OpBranchConditional + // | || + // loop merge (to loop header^) + // + // The conditional branch can be simplified. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %11 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranchConditional %8 %10 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto context = + BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(kEnv, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %11 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranch %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(kEnv, after, context.get()); + + ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get()); + ASSERT_EQ(0, ops.size()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/val/val_atomics_test.cpp b/3rdparty/spirv-tools/test/val/val_atomics_test.cpp index 2090c1324..1be19c560 100644 --- a/3rdparty/spirv-tools/test/val/val_atomics_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_atomics_test.cpp @@ -373,30 +373,37 @@ TEST_F(ValidateAtomics, AtomicLoadVulkanInt64) { "AtomicLoad: 64-bit atomics require the Int64Atomics capability")); } -TEST_F(ValidateAtomics, AtomicLoadWebGPUShaderSuccess) { +TEST_F(ValidateAtomics, AtomicLoadWebGPUSuccess) { const std::string body = R"( %val1 = OpAtomicLoad %u32 %u32_var %queuefamily %relaxed -%val2 = OpAtomicLoad %u32 %u32_var %workgroup %acquire +%val2 = OpAtomicLoad %u32 %u32_var %workgroup %relaxed )"; CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } -TEST_F(ValidateAtomics, AtomicLoadWebGPUShaderSequentiallyConsistentFailure) { +TEST_F(ValidateAtomics, AtomicLoadWebGPUNonRelaxedFailure) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %queuefamily %acquire +%val2 = OpAtomicLoad %u32 %u32_var %workgroup %release +)"; + + CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("WebGPU spec disallows, for OpAtomic*, any bit masks")); +} + +TEST_F(ValidateAtomics, AtomicLoadWebGPUSequentiallyConsistentFailure) { const std::string body = R"( %val3 = OpAtomicLoad %u32 %u32_var %subgroup %sequentially_consistent )"; CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "WebGPU spec disallows any bit masks in Memory Semantics that are " - "not Acquire, Release, AcquireRelease, UniformMemory, " - "WorkgroupMemory, ImageMemory, OutputMemoryKHR, MakeAvailableKHR, or " - "MakeVisibleKHR\n %34 = OpAtomicLoad %uint %29 %uint_3 %uint_16\n")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("WebGPU spec disallows, for OpAtomic*, any bit masks")); } TEST_F(ValidateAtomics, VK_KHR_shader_atomic_int64Success) { @@ -579,13 +586,24 @@ OpAtomicStore %u32_var %device %sequentially_consistent %u32_1 TEST_F(ValidateAtomics, AtomicStoreWebGPUSuccess) { const std::string body = R"( -OpAtomicStore %u32_var %queuefamily %release %u32_1 +OpAtomicStore %u32_var %queuefamily %relaxed %u32_1 )"; CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } +TEST_F(ValidateAtomics, AtomicStoreWebGPUNonRelaxedFailure) { + const std::string body = R"( +OpAtomicStore %u32_var %queuefamily %release %u32_1 +)"; + + CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("WebGPU spec disallows, for OpAtomic*, any bit masks")); +} + TEST_F(ValidateAtomics, AtomicStoreWebGPUSequentiallyConsistent) { const std::string body = R"( OpAtomicStore %u32_var %queuefamily %sequentially_consistent %u32_1 @@ -593,14 +611,8 @@ OpAtomicStore %u32_var %queuefamily %sequentially_consistent %u32_1 CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "WebGPU spec disallows any bit masks in Memory Semantics that are " - "not Acquire, Release, AcquireRelease, UniformMemory, " - "WorkgroupMemory, ImageMemory, OutputMemoryKHR, MakeAvailableKHR, or " - "MakeVisibleKHR\n" - " OpAtomicStore %29 %uint_5 %uint_16 %uint_1\n")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("WebGPU spec disallows, for OpAtomic*, any bit masks")); } TEST_F(ValidateAtomics, AtomicStoreWrongPointerType) { diff --git a/3rdparty/spirv-tools/test/val/val_cfg_test.cpp b/3rdparty/spirv-tools/test/val/val_cfg_test.cpp index 8f39a00c0..72bfa42c5 100644 --- a/3rdparty/spirv-tools/test/val/val_cfg_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_cfg_test.cpp @@ -1203,11 +1203,7 @@ std::string GetUnreachableMergeWithBranchUse(SpvCapability cap, TEST_P(ValidateCFG, UnreachableMergeWithBranchUse) { CompileSuccessfully( GetUnreachableMergeWithBranchUse(GetParam(), SPV_ENV_UNIVERSAL_1_0)); - EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "Statically reachable blocks cannot be terminated by OpUnreachable")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateCFG, WebGPUUnreachableMergeWithBranchUse) { @@ -1942,10 +1938,7 @@ OpDecorate %id BuiltIn GlobalInvocationId str += "OpFunctionEnd"; CompileSuccessfully(str); - EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Statically reachable blocks cannot be terminated by " - "OpUnreachable")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateCFG, LoopWithZeroBackEdgesBad) { @@ -2873,11 +2866,7 @@ OpFunctionEnd )"; CompileSuccessfully(text); - EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "Statically reachable blocks cannot be terminated by OpUnreachable")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateCFG, ReachableOpUnreachableOpBranch) { @@ -2896,11 +2885,7 @@ OpFunctionEnd )"; CompileSuccessfully(text); - EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "Statically reachable blocks cannot be terminated by OpUnreachable")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateCFG, ReachableOpUnreachableOpBranchConditional) { @@ -2923,11 +2908,7 @@ OpFunctionEnd )"; CompileSuccessfully(text); - EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "Statically reachable blocks cannot be terminated by OpUnreachable")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateCFG, ReachableOpUnreachableOpSwitch) { @@ -2952,11 +2933,7 @@ OpFunctionEnd )"; CompileSuccessfully(text); - EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "Statically reachable blocks cannot be terminated by OpUnreachable")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateCFG, ReachableOpUnreachableLoop) { @@ -2980,11 +2957,7 @@ OpFunctionEnd )"; CompileSuccessfully(text); - EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "Statically reachable blocks cannot be terminated by OpUnreachable")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateCFG, UnreachableLoopBadBackedge) { diff --git a/3rdparty/spirv-tools/test/val/val_webgpu_test.cpp b/3rdparty/spirv-tools/test/val/val_webgpu_test.cpp index 48ea21db2..c55f9276d 100644 --- a/3rdparty/spirv-tools/test/val/val_webgpu_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_webgpu_test.cpp @@ -276,6 +276,63 @@ TEST_F(ValidateWebGPU, NonVulkanKHRMemoryModelExtensionBad) { "\"SPV_KHR_8bit_storage\"\n")); } +spv_binary GenerateTrivialBinary(bool need_little_endian) { + // Smallest possible valid WebGPU SPIR-V binary in little endian. Contains all + // the required boilerplate and a trivial entry point function. + static const uint8_t binary_bytes[] = { + // clang-format off + 0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0xE1, 0x14, 0x00, 0x00, + 0x0A, 0x00, 0x08, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x4B, 0x48, 0x52, 0x5F, + 0x76, 0x75, 0x6C, 0x6B, 0x61, 0x6E, 0x5F, 0x6D, 0x65, 0x6D, 0x6F, 0x72, + 0x79, 0x5F, 0x6D, 0x6F, 0x64, 0x65, 0x6C, 0x00, 0x0E, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x73, 0x68, 0x61, 0x64, + 0x65, 0x72, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 + // clang-format on + }; + static const size_t word_count = sizeof(binary_bytes) / sizeof(uint32_t); + std::unique_ptr result(new spv_binary_t); + if (!result) return nullptr; + + result->wordCount = word_count; + result->code = new uint32_t[word_count]; + if (!result->code) return nullptr; + + if (need_little_endian) { + memcpy(result->code, binary_bytes, sizeof(binary_bytes)); + } else { + uint8_t* code_bytes = reinterpret_cast(result->code); + for (size_t word = 0; word < word_count; ++word) { + code_bytes[4 * word] = binary_bytes[4 * word + 3]; + code_bytes[4 * word + 1] = binary_bytes[4 * word + 2]; + code_bytes[4 * word + 2] = binary_bytes[4 * word + 1]; + code_bytes[4 * word + 3] = binary_bytes[4 * word]; + } + } + + return result.release(); +} + +TEST_F(ValidateWebGPU, LittleEndianGood) { + DestroyBinary(); + binary_ = GenerateTrivialBinary(true); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, BigEndianBad) { + DestroyBinary(); + binary_ = GenerateTrivialBinary(false); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("WebGPU requires SPIR-V to be little endian.")); +} + } // namespace } // namespace val } // namespace spvtools