From a91c5a877ef772f14be87b5a775107f302640c1d 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, 9 Feb 2019 12:43:10 -0800 Subject: [PATCH] Updated spirv-tools. --- 3rdparty/spirv-tools/Android.mk | 1 + 3rdparty/spirv-tools/BUILD.gn | 2 + 3rdparty/spirv-tools/README.md | 12 + .../include/generated/build-version.inc | 2 +- .../include/spirv-tools/instrument.hpp | 39 +- .../include/spirv-tools/libspirv.h | 4 + .../include/spirv-tools/optimizer.hpp | 6 +- .../kokoro/shaderc-smoketest/build.sh | 2 +- .../spirv-tools/source/opt/CMakeLists.txt | 2 + .../opt/eliminate_dead_functions_pass.cpp | 10 +- .../opt/eliminate_dead_functions_util.cpp | 32 ++ .../opt/eliminate_dead_functions_util.h | 36 ++ .../source/opt/inst_bindless_check_pass.cpp | 56 +- .../source/opt/inst_bindless_check_pass.h | 23 +- .../source/opt/instrument_pass.cpp | 148 ++++- .../spirv-tools/source/opt/instrument_pass.h | 37 +- 3rdparty/spirv-tools/source/opt/optimizer.cpp | 16 +- .../spirv-tools/source/reduce/CMakeLists.txt | 4 + .../remove_function_reduction_opportunity.cpp | 40 ++ .../remove_function_reduction_opportunity.h | 49 ++ ..._function_reduction_opportunity_finder.cpp | 42 ++ ...ve_function_reduction_opportunity_finder.h | 42 ++ .../spirv-tools/source/spirv_target_env.h | 4 - .../source/val/validate_function.cpp | 43 ++ .../spirv-tools/source/val/validate_type.cpp | 16 +- .../test/opt/inst_bindless_check_test.cpp | 523 +++++++++--------- .../spirv-tools/test/opt/optimizer_test.cpp | 127 +++++ .../spirv-tools/test/reduce/CMakeLists.txt | 1 + .../test/reduce/remove_function_test.cpp | 294 ++++++++++ 3rdparty/spirv-tools/test/val/CMakeLists.txt | 3 +- .../test/val/val_builtins_test.cpp | 2 +- .../test/val/val_decoration_test.cpp | 24 + 3rdparty/spirv-tools/test/val/val_fixtures.h | 7 + .../test/val/val_function_test.cpp | 424 ++++++++++++++ 3rdparty/spirv-tools/test/val/val_id_test.cpp | 7 +- 3rdparty/spirv-tools/tools/CMakeLists.txt | 9 +- 3rdparty/spirv-tools/tools/reduce/reduce.cpp | 3 + 37 files changed, 1741 insertions(+), 351 deletions(-) create mode 100644 3rdparty/spirv-tools/source/opt/eliminate_dead_functions_util.cpp create mode 100644 3rdparty/spirv-tools/source/opt/eliminate_dead_functions_util.h create mode 100644 3rdparty/spirv-tools/source/reduce/remove_function_reduction_opportunity.cpp create mode 100644 3rdparty/spirv-tools/source/reduce/remove_function_reduction_opportunity.h create mode 100644 3rdparty/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.cpp create mode 100644 3rdparty/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.h create mode 100644 3rdparty/spirv-tools/test/reduce/remove_function_test.cpp create mode 100644 3rdparty/spirv-tools/test/val/val_function_test.cpp diff --git a/3rdparty/spirv-tools/Android.mk b/3rdparty/spirv-tools/Android.mk index 67d4fc3f3..986f39cad 100644 --- a/3rdparty/spirv-tools/Android.mk +++ b/3rdparty/spirv-tools/Android.mk @@ -98,6 +98,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/dominator_tree.cpp \ source/opt/eliminate_dead_constant_pass.cpp \ source/opt/eliminate_dead_functions_pass.cpp \ + source/opt/eliminate_dead_functions_util.cpp \ source/opt/feature_manager.cpp \ source/opt/flatten_decoration_pass.cpp \ source/opt/fold.cpp \ diff --git a/3rdparty/spirv-tools/BUILD.gn b/3rdparty/spirv-tools/BUILD.gn index 01044bf7a..0df5e138c 100644 --- a/3rdparty/spirv-tools/BUILD.gn +++ b/3rdparty/spirv-tools/BUILD.gn @@ -496,6 +496,8 @@ static_library("spvtools_opt") { "source/opt/eliminate_dead_constant_pass.h", "source/opt/eliminate_dead_functions_pass.cpp", "source/opt/eliminate_dead_functions_pass.h", + "source/opt/eliminate_dead_functions_util.cpp", + "source/opt/eliminate_dead_functions_util.h", "source/opt/feature_manager.cpp", "source/opt/feature_manager.h", "source/opt/flatten_decoration_pass.cpp", diff --git a/3rdparty/spirv-tools/README.md b/3rdparty/spirv-tools/README.md index c8e1cb6e9..52c382ed0 100644 --- a/3rdparty/spirv-tools/README.md +++ b/3rdparty/spirv-tools/README.md @@ -432,6 +432,18 @@ The validator operates on the binary form. * `spirv-val` - the standalone validator * `/tools/val` +### Reducer tool + +The reducer shrinks a SPIR-V binary module, guided by a user-supplied +*interestingness test*. + +This is a work in progress, with initially only shrinks a module in a few ways. + +* `spirv-reduce` - the standalone reducer + * `/tools/reduce` + +Run `spirv-reduce --help` to see how to specify interestingness. + ### Control flow dumper tool The control flow dumper prints the control flow graph for a SPIR-V module as a diff --git a/3rdparty/spirv-tools/include/generated/build-version.inc b/3rdparty/spirv-tools/include/generated/build-version.inc index b5470c994..86995c085 100644 --- a/3rdparty/spirv-tools/include/generated/build-version.inc +++ b/3rdparty/spirv-tools/include/generated/build-version.inc @@ -1 +1 @@ -"v2019.2-dev", "SPIRV-Tools v2019.2-dev 6bc0c3fb0a18b013fc222ac6173abaff22f65a39" +"v2019.2-dev", "SPIRV-Tools v2019.2-dev c3405c13ff287fcc881a3244dafd761d40edaa50" diff --git a/3rdparty/spirv-tools/include/spirv-tools/instrument.hpp b/3rdparty/spirv-tools/include/spirv-tools/instrument.hpp index f8068099c..01c7c6d1e 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/instrument.hpp +++ b/3rdparty/spirv-tools/include/spirv-tools/instrument.hpp @@ -30,17 +30,17 @@ namespace spvtools { // Stream Output Buffer Offsets // -// The following values provide 32-bit word offsets into the output buffer +// The following values provide offsets into the output buffer struct // generated by InstrumentPass::GenDebugStreamWrite. This method is utilized // by InstBindlessCheckPass. // -// The first word of the debug output buffer contains the next available word +// The first member of the debug output buffer contains the next available word // in the data stream to be written. Shaders will atomically read and update // this value so as not to overwrite each others records. This value must be // initialized to zero static const int kDebugOutputSizeOffset = 0; -// The second word of the output buffer is the start of the stream of records +// The second member of the output buffer is the start of the stream of records // written by the instrumented shaders. Each record represents a validation // error. The format of the records is documented below. static const int kDebugOutputDataOffset = 1; @@ -122,14 +122,45 @@ static const int kInstMaxOutCnt = kInstStageOutCnt + 3; // These are the possible validation error codes. static const int kInstErrorBindlessBounds = 0; +// Direct Input Buffer Offsets +// +// The following values provide member offsets into the input buffers +// consumed by InstrumentPass::GenDebugDirectRead(). This method is utilized +// by InstBindlessCheckPass. +// +// The only object in an input buffer is a runtime array of unsigned +// integers. Each validation will have its own formatting of this array. +static const int kDebugInputDataOffset = 0; + // Debug Buffer Bindings // // These are the bindings for the different buffers which are // read or written by the instrumentation passes. // -// This is the output buffer written by InstBindlessCheckPass. +// This is the output buffer written by InstBindlessCheckPass +// and possibly other future validations. static const int kDebugOutputBindingStream = 0; +// The binding for the input buffer for InstBindlessCheckPass. The input +// buffer needs only be created if the shaders being validated contain a +// descriptor array of runtime size, and validation of runtime size descriptor +// arrays have been enabled at the time of the bindless validation pass +// creation. +static const int kDebugInputBindingBindless = 1; + +// Bindless Validation Input Buffer Format +// +// An input buffer for bindless validation consists of a single array of +// unsigned integers we will call Data[]. This array is formatted as follows. +// +// At the beginning of the array is a single uint reserved for future use. +static const int kDebugInputBindlessOffsetReserved = 0; + +// Following the reserved uint is some number of uints such that the following +// is true: the number of descriptors at (set=s, binding=b) is: +// Data[ Data[ s + kDebugInputBindlessOffsetLengths ] + b ] +static const int kDebugInputBindlessOffsetLengths = 1; + } // namespace spvtools #endif // INCLUDE_SPIRV_TOOLS_INSTRUMENT_HPP_ diff --git a/3rdparty/spirv-tools/include/spirv-tools/libspirv.h b/3rdparty/spirv-tools/include/spirv-tools/libspirv.h index ff7eb6b6c..13bd959a8 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/libspirv.h +++ b/3rdparty/spirv-tools/include/spirv-tools/libspirv.h @@ -445,6 +445,10 @@ typedef enum { // Returns a string describing the given SPIR-V target environment. SPIRV_TOOLS_EXPORT const char* spvTargetEnvDescription(spv_target_env env); +// Parses s into *env and returns true if successful. If unparsable, returns +// false and sets *env to SPV_ENV_UNIVERSAL_1_0. +SPIRV_TOOLS_EXPORT bool spvParseTargetEnv(const char* s, spv_target_env* env); + // Creates a context object. Returns null if env is invalid. SPIRV_TOOLS_EXPORT spv_context spvContextCreate(spv_target_env env); diff --git a/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp b/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp index b59329db4..a496f9daf 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp +++ b/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp @@ -711,10 +711,12 @@ Optimizer::PassToken CreateCombineAccessChainsPass(); // The instrumentation will read and write buffers in debug // descriptor set |desc_set|. It will write |shader_id| in each output record // to identify the shader module which generated the record. +// |runtime_array_enable| controls instrumentation of runtime arrays which +// require input buffer support. // // TODO(greg-lunarg): Add support for vk_ext_descriptor_indexing. -Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set, - uint32_t shader_id); +Optimizer::PassToken CreateInstBindlessCheckPass( + uint32_t desc_set, uint32_t shader_id, bool runtime_array_enable = false); // Create a pass to upgrade to the VulkanKHR memory model. // This pass upgrades the Logical GLSL450 memory model to Logical VulkanKHR. diff --git a/3rdparty/spirv-tools/kokoro/shaderc-smoketest/build.sh b/3rdparty/spirv-tools/kokoro/shaderc-smoketest/build.sh index 638ca8c61..0856c9b25 100644 --- a/3rdparty/spirv-tools/kokoro/shaderc-smoketest/build.sh +++ b/3rdparty/spirv-tools/kokoro/shaderc-smoketest/build.sh @@ -37,7 +37,7 @@ cd $SHADERC_DIR/third_party # Get shaderc dependencies. Link the appropriate SPIRV-Tools. git clone https://github.com/google/googletest.git -git clone https://github.com/google/glslang.git +git clone https://github.com/KhronosGroup/glslang.git ln -s $GITHUB_DIR/SPIRV-Tools spirv-tools git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-headers git clone https://github.com/google/re2 diff --git a/3rdparty/spirv-tools/source/opt/CMakeLists.txt b/3rdparty/spirv-tools/source/opt/CMakeLists.txt index 444379855..e1c26f089 100644 --- a/3rdparty/spirv-tools/source/opt/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/opt/CMakeLists.txt @@ -37,6 +37,7 @@ set(SPIRV_TOOLS_OPT_SOURCES dominator_tree.h eliminate_dead_constant_pass.h eliminate_dead_functions_pass.h + eliminate_dead_functions_util.h feature_manager.h flatten_decoration_pass.h fold.h @@ -131,6 +132,7 @@ set(SPIRV_TOOLS_OPT_SOURCES dominator_tree.cpp eliminate_dead_constant_pass.cpp eliminate_dead_functions_pass.cpp + eliminate_dead_functions_util.cpp feature_manager.cpp flatten_decoration_pass.cpp fold.cpp diff --git a/3rdparty/spirv-tools/source/opt/eliminate_dead_functions_pass.cpp b/3rdparty/spirv-tools/source/opt/eliminate_dead_functions_pass.cpp index f067be5f9..a46552193 100644 --- a/3rdparty/spirv-tools/source/opt/eliminate_dead_functions_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/eliminate_dead_functions_pass.cpp @@ -13,6 +13,7 @@ // limitations under the License. #include "source/opt/eliminate_dead_functions_pass.h" +#include "source/opt/eliminate_dead_functions_util.h" #include @@ -36,8 +37,8 @@ Pass::Status EliminateDeadFunctionsPass::Process() { funcIter != get_module()->end();) { if (live_function_set.count(&*funcIter) == 0) { modified = true; - EliminateFunction(&*funcIter); - funcIter = funcIter.Erase(); + funcIter = + eliminatedeadfunctionsutil::EliminateFunction(context(), &funcIter); } else { ++funcIter; } @@ -47,10 +48,5 @@ Pass::Status EliminateDeadFunctionsPass::Process() { : Pass::Status::SuccessWithoutChange; } -void EliminateDeadFunctionsPass::EliminateFunction(Function* func) { - // Remove all of the instruction in the function body - func->ForEachInst([this](Instruction* inst) { context()->KillInst(inst); }, - true); -} } // namespace opt } // namespace spvtools diff --git a/3rdparty/spirv-tools/source/opt/eliminate_dead_functions_util.cpp b/3rdparty/spirv-tools/source/opt/eliminate_dead_functions_util.cpp new file mode 100644 index 000000000..8a3895931 --- /dev/null +++ b/3rdparty/spirv-tools/source/opt/eliminate_dead_functions_util.cpp @@ -0,0 +1,32 @@ +// 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 "eliminate_dead_functions_util.h" + +namespace spvtools { +namespace opt { + +namespace eliminatedeadfunctionsutil { + +Module::iterator EliminateFunction(IRContext* context, + Module::iterator* func_iter) { + (*func_iter) + ->ForEachInst([context](Instruction* inst) { context->KillInst(inst); }, + true); + return func_iter->Erase(); +} + +} // namespace eliminatedeadfunctionsutil +} // namespace opt +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/opt/eliminate_dead_functions_util.h b/3rdparty/spirv-tools/source/opt/eliminate_dead_functions_util.h new file mode 100644 index 000000000..9fcce956f --- /dev/null +++ b/3rdparty/spirv-tools/source/opt/eliminate_dead_functions_util.h @@ -0,0 +1,36 @@ +// 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_ELIMINATE_DEAD_FUNCTIONS_UTIL_H_ +#define SOURCE_OPT_ELIMINATE_DEAD_FUNCTIONS_UTIL_H_ + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +// Provides functionality for eliminating functions that are not needed, for use +// by various analyses and passes. +namespace eliminatedeadfunctionsutil { + +// Removes all of the function's instructions, removes the function from the +// module, and returns the next iterator. +Module::iterator EliminateFunction(IRContext* context, + Module::iterator* func_iter); + +} // namespace eliminatedeadfunctionsutil +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_ELIMINATE_DEAD_FUNCTIONS_UTIL_H_ diff --git a/3rdparty/spirv-tools/source/opt/inst_bindless_check_pass.cpp b/3rdparty/spirv-tools/source/opt/inst_bindless_check_pass.cpp index 1901f763b..d785f0c14 100644 --- a/3rdparty/spirv-tools/source/opt/inst_bindless_check_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/inst_bindless_check_pass.cpp @@ -35,6 +35,18 @@ static const int kSpvConstantValueInIdx = 0; namespace spvtools { namespace opt { +uint32_t InstBindlessCheckPass::GenDebugReadLength( + uint32_t image_id, InstructionBuilder* builder) { + uint32_t desc_set_idx = + var2desc_set_[image_id] + kDebugInputBindlessOffsetLengths; + uint32_t desc_set_idx_id = builder->GetUintConstantId(desc_set_idx); + uint32_t desc_set_offset_id = GenDebugDirectRead(desc_set_idx_id, builder); + Instruction* binding_idx_inst = + builder->AddBinaryOp(GetUintId(), SpvOpIAdd, desc_set_offset_id, + builder->GetUintConstantId(var2binding_[image_id])); + return GenDebugDirectRead(binding_idx_inst->result_id(), builder); +} + void InstBindlessCheckPass::GenBindlessCheckCode( BasicBlock::iterator ref_inst_itr, UptrVectorIterator ref_block_itr, uint32_t instruction_idx, @@ -119,20 +131,23 @@ void InstBindlessCheckPass::GenBindlessCheckCode( uint32_t ptr_type_id = var_type_inst->GetSingleWordInOperand(kSpvTypePointerTypeIdInIdx); Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id); - // TODO(greg-lunarg): Handle RuntimeArray. Will need to pull length - // out of debug input buffer. - if (ptr_type_inst->opcode() != SpvOpTypeArray) return; // If index and bound both compile-time constants and index < bound, // return without changing - uint32_t length_id = - ptr_type_inst->GetSingleWordInOperand(kSpvTypeArrayLengthIdInIdx); - Instruction* index_inst = get_def_use_mgr()->GetDef(index_id); - Instruction* length_inst = get_def_use_mgr()->GetDef(length_id); - if (index_inst->opcode() == SpvOpConstant && - length_inst->opcode() == SpvOpConstant && - index_inst->GetSingleWordInOperand(kSpvConstantValueInIdx) < - length_inst->GetSingleWordInOperand(kSpvConstantValueInIdx)) + uint32_t length_id = 0; + if (ptr_type_inst->opcode() == SpvOpTypeArray) { + length_id = + ptr_type_inst->GetSingleWordInOperand(kSpvTypeArrayLengthIdInIdx); + Instruction* index_inst = get_def_use_mgr()->GetDef(index_id); + Instruction* length_inst = get_def_use_mgr()->GetDef(length_id); + if (index_inst->opcode() == SpvOpConstant && + length_inst->opcode() == SpvOpConstant && + index_inst->GetSingleWordInOperand(kSpvConstantValueInIdx) < + length_inst->GetSingleWordInOperand(kSpvConstantValueInIdx)) + return; + } else if (!runtime_array_enabled_ || + ptr_type_inst->opcode() != SpvOpTypeRuntimeArray) { return; + } // Generate full runtime bounds test code with true branch // being full reference and false branch being debug output and zero // for the referenced value. @@ -141,6 +156,13 @@ void InstBindlessCheckPass::GenBindlessCheckCode( context(), &*new_blk_ptr, IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); uint32_t error_id = builder.GetUintConstantId(kInstErrorBindlessBounds); + // If length id not yet set, descriptor array is runtime size so + // generate load of length from stage's debug input buffer. + if (length_id == 0) { + assert(ptr_type_inst->opcode() == SpvOpTypeRuntimeArray && + "unexpected bindless type"); + length_id = GenDebugReadLength(image_id, &builder); + } Instruction* ult_inst = builder.AddBinaryOp(GetBoolId(), SpvOpULessThan, index_id, length_id); uint32_t merge_blk_id = TakeNextId(); @@ -236,6 +258,18 @@ void InstBindlessCheckPass::InitializeInstBindlessCheck() { break; } } + // If descriptor indexing extension and runtime array support enabled, + // create variable to descriptor set mapping. + if (ext_descriptor_indexing_defined_ && runtime_array_enabled_) + for (auto& anno : get_module()->annotations()) + if (anno.opcode() == SpvOpDecorate) { + if (anno.GetSingleWordInOperand(1u) == SpvDecorationDescriptorSet) + var2desc_set_[anno.GetSingleWordInOperand(0u)] = + anno.GetSingleWordInOperand(2u); + else if (anno.GetSingleWordInOperand(1u) == SpvDecorationBinding) + var2binding_[anno.GetSingleWordInOperand(0u)] = + anno.GetSingleWordInOperand(2u); + } } Pass::Status InstBindlessCheckPass::ProcessImpl() { diff --git a/3rdparty/spirv-tools/source/opt/inst_bindless_check_pass.h b/3rdparty/spirv-tools/source/opt/inst_bindless_check_pass.h index 3ab5ab7cf..cc405f0c3 100644 --- a/3rdparty/spirv-tools/source/opt/inst_bindless_check_pass.h +++ b/3rdparty/spirv-tools/source/opt/inst_bindless_check_pass.h @@ -29,10 +29,14 @@ namespace opt { class InstBindlessCheckPass : public InstrumentPass { public: // For test harness only - InstBindlessCheckPass() : InstrumentPass(7, 23, kInstValidationIdBindless) {} + InstBindlessCheckPass() + : InstrumentPass(7, 23, kInstValidationIdBindless), + runtime_array_enabled_(true) {} // For all other interfaces - InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id) - : InstrumentPass(desc_set, shader_id, kInstValidationIdBindless) {} + InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id, + bool runtime_array_enable) + : InstrumentPass(desc_set, shader_id, kInstValidationIdBindless), + runtime_array_enabled_(runtime_array_enable) {} ~InstBindlessCheckPass() override = default; @@ -42,6 +46,10 @@ class InstBindlessCheckPass : public InstrumentPass { const char* name() const override { return "inst-bindless-check-pass"; } private: + // Generate instructions into |builder| to read length of runtime descriptor + // array |image_id| from debug input buffer and return id of value. + uint32_t GenDebugReadLength(uint32_t image_id, InstructionBuilder* builder); + // Initialize state for instrumenting bindless checking void InitializeInstBindlessCheck(); @@ -85,6 +93,15 @@ class InstBindlessCheckPass : public InstrumentPass { // True if VK_EXT_descriptor_indexing is defined bool ext_descriptor_indexing_defined_; + + // Enable instrumentation of runtime arrays + bool runtime_array_enabled_; + + // Mapping from variable to descriptor set + std::unordered_map var2desc_set_; + + // Mapping from variable to binding + std::unordered_map var2binding_; }; } // namespace opt diff --git a/3rdparty/spirv-tools/source/opt/instrument_pass.cpp b/3rdparty/spirv-tools/source/opt/instrument_pass.cpp index 6935a43dc..58bfd27fe 100644 --- a/3rdparty/spirv-tools/source/opt/instrument_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/instrument_pass.cpp @@ -107,7 +107,7 @@ void InstrumentPass::GenDebugOutputFieldCode(uint32_t base_offset_id, builder->AddBinaryOp(GetUintId(), SpvOpIAdd, base_offset_id, builder->GetUintConstantId(field_offset)); uint32_t buf_id = GetOutputBufferId(); - uint32_t buf_uint_ptr_id = GetOutputBufferUintPtrId(); + uint32_t buf_uint_ptr_id = GetBufferUintPtrId(); Instruction* achain_inst = builder->AddTernaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id, builder->GetUintConstantId(kDebugOutputDataOffset), @@ -222,6 +222,18 @@ void InstrumentPass::GenDebugStreamWrite( (void)builder->AddNaryOp(GetVoidId(), SpvOpFunctionCall, args); } +uint32_t InstrumentPass::GenDebugDirectRead(uint32_t idx_id, + InstructionBuilder* builder) { + uint32_t input_buf_id = GetInputBufferId(); + uint32_t buf_uint_ptr_id = GetBufferUintPtrId(); + Instruction* ibuf_ac_inst = builder->AddTernaryOp( + buf_uint_ptr_id, SpvOpAccessChain, input_buf_id, + builder->GetUintConstantId(kDebugInputDataOffset), idx_id); + Instruction* load_inst = + builder->AddUnaryOp(GetUintId(), SpvOpLoad, ibuf_ac_inst->result_id()); + return load_inst->result_id(); +} + bool InstrumentPass::IsSameBlockOp(const Instruction* inst) const { return inst->opcode() == SpvOpSampledImage || inst->opcode() == SpvOpImage; } @@ -280,12 +292,12 @@ void InstrumentPass::UpdateSucceedingPhis( } // Return id for output buffer uint ptr type -uint32_t InstrumentPass::GetOutputBufferUintPtrId() { - if (output_buffer_uint_ptr_id_ == 0) { - output_buffer_uint_ptr_id_ = context()->get_type_mgr()->FindPointerToType( +uint32_t InstrumentPass::GetBufferUintPtrId() { + if (buffer_uint_ptr_id_ == 0) { + buffer_uint_ptr_id_ = context()->get_type_mgr()->FindPointerToType( GetUintId(), SpvStorageClassStorageBuffer); } - return output_buffer_uint_ptr_id_; + return buffer_uint_ptr_id_; } uint32_t InstrumentPass::GetOutputBufferBinding() { @@ -298,22 +310,74 @@ uint32_t InstrumentPass::GetOutputBufferBinding() { return 0; } +uint32_t InstrumentPass::GetInputBufferBinding() { + switch (validation_id_) { + case kInstValidationIdBindless: + return kDebugInputBindingBindless; + default: + assert(false && "unexpected validation id"); + } + return 0; +} + +analysis::Type* InstrumentPass::GetUintRuntimeArrayType( + analysis::DecorationManager* deco_mgr, analysis::TypeManager* type_mgr) { + if (uint_rarr_ty_ == nullptr) { + analysis::Integer uint_ty(32, false); + analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); + analysis::RuntimeArray uint_rarr_ty_tmp(reg_uint_ty); + uint_rarr_ty_ = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp); + uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(uint_rarr_ty_); + // By the Vulkan spec, a pre-existing RuntimeArray of uint must be part of + // a block, and will therefore be decorated with an ArrayStride. Therefore + // the undecorated type returned here will not be pre-existing and can + // safely be decorated. Since this type is now decorated, it is out of + // sync with the TypeManager and therefore the TypeManager must be + // invalidated after this pass. + assert(context()->get_def_use_mgr()->NumUses(uint_arr_ty_id) == 0 && + "used RuntimeArray type returned"); + deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride, 4u); + } + return uint_rarr_ty_; +} + +void InstrumentPass::AddStorageBufferExt() { + if (storage_buffer_ext_defined_) return; + if (!get_feature_mgr()->HasExtension(kSPV_KHR_storage_buffer_storage_class)) { + const std::string ext_name("SPV_KHR_storage_buffer_storage_class"); + const auto num_chars = ext_name.size(); + // Compute num words, accommodate the terminating null character. + const auto num_words = (num_chars + 1 + 3) / 4; + std::vector ext_words(num_words, 0u); + std::memcpy(ext_words.data(), ext_name.data(), num_chars); + context()->AddExtension(std::unique_ptr( + new Instruction(context(), SpvOpExtension, 0u, 0u, + {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}))); + } + storage_buffer_ext_defined_ = true; +} + // Return id for output buffer uint32_t InstrumentPass::GetOutputBufferId() { if (output_buffer_id_ == 0) { // If not created yet, create one analysis::DecorationManager* deco_mgr = get_decoration_mgr(); analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Type* reg_uint_rarr_ty = + GetUintRuntimeArrayType(deco_mgr, type_mgr); analysis::Integer uint_ty(32, false); analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); - analysis::RuntimeArray uint_rarr_ty(reg_uint_ty); - analysis::Type* reg_uint_rarr_ty = - type_mgr->GetRegisteredType(&uint_rarr_ty); - uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(reg_uint_rarr_ty); - deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride, 4u); - analysis::Struct obuf_ty({reg_uint_ty, reg_uint_rarr_ty}); - analysis::Type* reg_obuf_ty = type_mgr->GetRegisteredType(&obuf_ty); - uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_obuf_ty); + analysis::Struct buf_ty({reg_uint_ty, reg_uint_rarr_ty}); + analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty); + uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_buf_ty); + // By the Vulkan spec, a pre-existing struct containing a RuntimeArray + // must be a block, and will therefore be decorated with Block. Therefore + // the undecorated type returned here will not be pre-existing and can + // safely be decorated. Since this type is now decorated, it is out of + // sync with the TypeManager and therefore the TypeManager must be + // invalidated after this pass. + assert(context()->get_def_use_mgr()->NumUses(obufTyId) == 0 && + "used struct type returned"); deco_mgr->AddDecoration(obufTyId, SpvDecorationBlock); deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputSizeOffset, SpvDecorationOffset, 0); @@ -331,23 +395,48 @@ uint32_t InstrumentPass::GetOutputBufferId() { desc_set_); deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationBinding, GetOutputBufferBinding()); - // Look for storage buffer extension. If none, create one. - if (!get_feature_mgr()->HasExtension( - kSPV_KHR_storage_buffer_storage_class)) { - const std::string ext_name("SPV_KHR_storage_buffer_storage_class"); - const auto num_chars = ext_name.size(); - // Compute num words, accommodate the terminating null character. - const auto num_words = (num_chars + 1 + 3) / 4; - std::vector ext_words(num_words, 0u); - std::memcpy(ext_words.data(), ext_name.data(), num_chars); - context()->AddExtension(std::unique_ptr( - new Instruction(context(), SpvOpExtension, 0u, 0u, - {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}))); - } + AddStorageBufferExt(); } return output_buffer_id_; } +uint32_t InstrumentPass::GetInputBufferId() { + if (input_buffer_id_ == 0) { + // If not created yet, create one + analysis::DecorationManager* deco_mgr = get_decoration_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Type* reg_uint_rarr_ty = + GetUintRuntimeArrayType(deco_mgr, type_mgr); + analysis::Struct buf_ty({reg_uint_rarr_ty}); + analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty); + uint32_t ibufTyId = type_mgr->GetTypeInstruction(reg_buf_ty); + // By the Vulkan spec, a pre-existing struct containing a RuntimeArray + // must be a block, and will therefore be decorated with Block. Therefore + // the undecorated type returned here will not be pre-existing and can + // safely be decorated. Since this type is now decorated, it is out of + // sync with the TypeManager and therefore the TypeManager must be + // invalidated after this pass. + assert(context()->get_def_use_mgr()->NumUses(ibufTyId) == 0 && + "used struct type returned"); + deco_mgr->AddDecoration(ibufTyId, SpvDecorationBlock); + deco_mgr->AddMemberDecoration(ibufTyId, 0, SpvDecorationOffset, 0); + uint32_t ibufTyPtrId_ = + type_mgr->FindPointerToType(ibufTyId, SpvStorageClassStorageBuffer); + input_buffer_id_ = TakeNextId(); + std::unique_ptr newVarOp(new Instruction( + context(), SpvOpVariable, ibufTyPtrId_, input_buffer_id_, + {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvStorageClassStorageBuffer}}})); + context()->AddGlobalValue(std::move(newVarOp)); + deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationDescriptorSet, + desc_set_); + deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationBinding, + GetInputBufferBinding()); + AddStorageBufferExt(); + } + return input_buffer_id_; +} + uint32_t InstrumentPass::GetVec4FloatId() { if (v4float_id_ == 0) { analysis::TypeManager* type_mgr = context()->get_type_mgr(); @@ -447,7 +536,7 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx, // Gen test if debug output buffer size will not be exceeded. uint32_t obuf_record_sz = kInstStageOutCnt + val_spec_param_cnt; uint32_t buf_id = GetOutputBufferId(); - uint32_t buf_uint_ptr_id = GetOutputBufferUintPtrId(); + uint32_t buf_uint_ptr_id = GetBufferUintPtrId(); Instruction* obuf_curr_sz_ac_inst = builder.AddBinaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id, builder.GetUintConstantId(kDebugOutputSizeOffset)); @@ -620,14 +709,17 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) { void InstrumentPass::InitializeInstrument() { output_buffer_id_ = 0; - output_buffer_uint_ptr_id_ = 0; + buffer_uint_ptr_id_ = 0; output_func_id_ = 0; output_func_param_cnt_ = 0; + input_buffer_id_ = 0; v4float_id_ = 0; uint_id_ = 0; v4uint_id_ = 0; bool_id_ = 0; void_id_ = 0; + storage_buffer_ext_defined_ = false; + uint_rarr_ty_ = nullptr; // clear collections id2function_.clear(); diff --git a/3rdparty/spirv-tools/source/opt/instrument_pass.h b/3rdparty/spirv-tools/source/opt/instrument_pass.h index cfa76b149..003e64742 100644 --- a/3rdparty/spirv-tools/source/opt/instrument_pass.h +++ b/3rdparty/spirv-tools/source/opt/instrument_pass.h @@ -76,7 +76,7 @@ class InstrumentPass : public Pass { IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | IRContext::kAnalysisNameMap | IRContext::kAnalysisBuiltinVarId | - IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + IRContext::kAnalysisConstants; } protected: @@ -195,6 +195,13 @@ class InstrumentPass : public Pass { const std::vector& validation_ids, InstructionBuilder* builder); + // Generate in |builder| instructions to read the unsigned integer from the + // input buffer at offset |idx_id|. Return the result id. + // + // The binding and the format of the input buffer is determined by each + // specific validation, which is specified at the creation of the pass. + uint32_t GenDebugDirectRead(uint32_t idx_id, InstructionBuilder* builder); + // Generate code to cast |value_id| to unsigned, if needed. Return // an id to the unsigned equivalent. uint32_t GenUintCastCode(uint32_t value_id, InstructionBuilder* builder); @@ -211,15 +218,28 @@ class InstrumentPass : public Pass { // Return id for void type uint32_t GetVoidId(); - // Return id for output buffer uint type - uint32_t GetOutputBufferUintPtrId(); + // Return pointer to type for runtime array of uint + analysis::Type* GetUintRuntimeArrayType(analysis::DecorationManager* deco_mgr, + analysis::TypeManager* type_mgr); + + // Return id for buffer uint type + uint32_t GetBufferUintPtrId(); // Return binding for output buffer for current validation. uint32_t GetOutputBufferBinding(); + // Return binding for input buffer for current validation. + uint32_t GetInputBufferBinding(); + + // Add storage buffer extension if needed + void AddStorageBufferExt(); + // Return id for debug output buffer uint32_t GetOutputBufferId(); + // Return id for debug input buffer + uint32_t GetInputBufferId(); + // Return id for v4float type uint32_t GetVec4FloatId(); @@ -321,7 +341,7 @@ class InstrumentPass : public Pass { uint32_t output_buffer_id_; // type id for output buffer element - uint32_t output_buffer_uint_ptr_id_; + uint32_t buffer_uint_ptr_id_; // id for debug output function uint32_t output_func_id_; @@ -329,6 +349,9 @@ class InstrumentPass : public Pass { // param count for output function uint32_t output_func_param_cnt_; + // id for input buffer variable + uint32_t input_buffer_id_; + // id for v4float type uint32_t v4float_id_; @@ -344,6 +367,12 @@ class InstrumentPass : public Pass { // id for void type uint32_t void_id_; + // boolean to remember storage buffer extension + bool storage_buffer_ext_defined_; + + // runtime array of uint type + analysis::Type* uint_rarr_ty_; + // Pre-instrumentation same-block insts std::unordered_map same_block_pre_; diff --git a/3rdparty/spirv-tools/source/opt/optimizer.cpp b/3rdparty/spirv-tools/source/opt/optimizer.cpp index 4eb0c4006..9d3cad7df 100644 --- a/3rdparty/spirv-tools/source/opt/optimizer.cpp +++ b/3rdparty/spirv-tools/source/opt/optimizer.cpp @@ -57,8 +57,8 @@ Optimizer::PassToken::~PassToken() {} struct Optimizer::Impl { explicit Impl(spv_target_env env) : target_env(env), pass_manager() {} - spv_target_env target_env; // Target environment. - opt::PassManager pass_manager; // Internal implementation pass manager. + spv_target_env target_env; // Target environment. + opt::PassManager pass_manager; // Internal implementation pass manager. }; Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {} @@ -218,7 +218,9 @@ Optimizer& Optimizer::RegisterSizePasses() { } Optimizer& Optimizer::RegisterWebGPUPasses() { - return RegisterPass(CreateAggressiveDCEPass()) + return RegisterPass(CreateStripDebugInfoPass()) + .RegisterPass(CreateFlattenDecorationPass()) + .RegisterPass(CreateAggressiveDCEPass()) .RegisterPass(CreateDeadBranchElimPass()); } @@ -379,7 +381,7 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { } else if (pass_name == "replace-invalid-opcode") { RegisterPass(CreateReplaceInvalidOpcodePass()); } else if (pass_name == "inst-bindless-check") { - RegisterPass(CreateInstBindlessCheckPass(7, 23)); + RegisterPass(CreateInstBindlessCheckPass(7, 23, true)); RegisterPass(CreateSimplificationPass()); RegisterPass(CreateDeadBranchElimPass()); RegisterPass(CreateBlockMergePass()); @@ -788,9 +790,11 @@ Optimizer::PassToken CreateUpgradeMemoryModelPass() { } Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set, - uint32_t shader_id) { + uint32_t shader_id, + bool runtime_array_enable) { return MakeUnique( - MakeUnique(desc_set, shader_id)); + MakeUnique(desc_set, shader_id, + runtime_array_enable)); } Optimizer::PassToken CreateCodeSinkingPass() { diff --git a/3rdparty/spirv-tools/source/reduce/CMakeLists.txt b/3rdparty/spirv-tools/source/reduce/CMakeLists.txt index 309e89b8a..1a6ead479 100644 --- a/3rdparty/spirv-tools/source/reduce/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/reduce/CMakeLists.txt @@ -25,6 +25,8 @@ set(SPIRV_TOOLS_REDUCE_SOURCES reduction_pass.h reduction_util.h remove_instruction_reduction_opportunity.h + remove_function_reduction_opportunity.h + remove_function_reduction_opportunity_finder.h remove_opname_instruction_reduction_opportunity_finder.h remove_unreferenced_instruction_reduction_opportunity_finder.h structured_loop_to_selection_reduction_opportunity.h @@ -41,6 +43,8 @@ set(SPIRV_TOOLS_REDUCE_SOURCES reduction_opportunity.cpp reduction_pass.cpp reduction_util.cpp + remove_function_reduction_opportunity.cpp + remove_function_reduction_opportunity_finder.cpp remove_instruction_reduction_opportunity.cpp remove_unreferenced_instruction_reduction_opportunity_finder.cpp remove_opname_instruction_reduction_opportunity_finder.cpp diff --git a/3rdparty/spirv-tools/source/reduce/remove_function_reduction_opportunity.cpp b/3rdparty/spirv-tools/source/reduce/remove_function_reduction_opportunity.cpp new file mode 100644 index 000000000..6ecf61bd4 --- /dev/null +++ b/3rdparty/spirv-tools/source/reduce/remove_function_reduction_opportunity.cpp @@ -0,0 +1,40 @@ +// 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 "remove_function_reduction_opportunity.h" +#include "source/opt/eliminate_dead_functions_util.h" + +namespace spvtools { +namespace reduce { + +bool RemoveFunctionReductionOpportunity::PreconditionHolds() { + // Removing one function cannot influence whether another function can be + // removed. + return true; +} + +void RemoveFunctionReductionOpportunity::Apply() { + for (opt::Module::iterator function_it = context_->module()->begin(); + function_it != context_->module()->end(); ++function_it) { + if (&*function_it == function_) { + opt::eliminatedeadfunctionsutil::EliminateFunction(context_, + &function_it); + return; + } + } + assert(0 && "Function to be removed was not found."); +} + +} // namespace reduce +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/reduce/remove_function_reduction_opportunity.h b/3rdparty/spirv-tools/source/reduce/remove_function_reduction_opportunity.h new file mode 100644 index 000000000..483453d63 --- /dev/null +++ b/3rdparty/spirv-tools/source/reduce/remove_function_reduction_opportunity.h @@ -0,0 +1,49 @@ +// 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_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_H_ + +#include "reduction_opportunity.h" +#include "source/opt/function.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to remove an unreferenced function. +class RemoveFunctionReductionOpportunity : public ReductionOpportunity { + public: + // Creates an opportunity to remove |function| from the module represented by + // |context|. + RemoveFunctionReductionOpportunity(opt::IRContext* context, + opt::Function* function) + : context_(context), function_(function) {} + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + // The IR context for the module under analysis. + opt::IRContext* context_; + + // The function that can be removed. + opt::Function* function_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_H_ diff --git a/3rdparty/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.cpp b/3rdparty/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.cpp new file mode 100644 index 000000000..f0206fbcf --- /dev/null +++ b/3rdparty/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.cpp @@ -0,0 +1,42 @@ +// 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 "remove_function_reduction_opportunity_finder.h" +#include "remove_function_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +RemoveFunctionReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context) const { + std::vector> result; + // Consider each function. + for (auto& function : *context->module()) { + if (context->get_def_use_mgr()->NumUses(function.result_id()) > 0) { + // If the function is referenced, ignore it. + continue; + } + result.push_back( + MakeUnique(context, &function)); + } + return result; +} + +std::string RemoveFunctionReductionOpportunityFinder::GetName() const { + return "RemoveFunctionReductionOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.h b/3rdparty/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.h new file mode 100644 index 000000000..7952a2296 --- /dev/null +++ b/3rdparty/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.h @@ -0,0 +1,42 @@ +// 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_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder of opportunities to remove unreferenced functions. +class RemoveFunctionReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + RemoveFunctionReductionOpportunityFinder() = default; + + ~RemoveFunctionReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context) const final; + + private: +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/3rdparty/spirv-tools/source/spirv_target_env.h b/3rdparty/spirv-tools/source/spirv_target_env.h index d4635705f..c19d16921 100644 --- a/3rdparty/spirv-tools/source/spirv_target_env.h +++ b/3rdparty/spirv-tools/source/spirv_target_env.h @@ -19,10 +19,6 @@ #include "spirv-tools/libspirv.h" -// Parses s into *env and returns true if successful. If unparsable, returns -// false and sets *env to SPV_ENV_UNIVERSAL_1_0. -bool spvParseTargetEnv(const char* s, spv_target_env* env); - // Returns true if |env| is a VULKAN environment, false otherwise. bool spvIsVulkanEnv(spv_target_env env); diff --git a/3rdparty/spirv-tools/source/val/validate_function.cpp b/3rdparty/spirv-tools/source/val/validate_function.cpp index de41b27c3..96c477698 100644 --- a/3rdparty/spirv-tools/source/val/validate_function.cpp +++ b/3rdparty/spirv-tools/source/val/validate_function.cpp @@ -247,6 +247,49 @@ spv_result_t ValidateFunctionCall(ValidationState_t& _, << "'s type does not match Function '" << _.getIdName(parameter_type_id) << "'s parameter type."; } + + if (_.addressing_model() == SpvAddressingModelLogical) { + if (parameter_type->opcode() == SpvOpTypePointer) { + SpvStorageClass sc = parameter_type->GetOperandAs(1u); + // Validate which storage classes can be pointer operands. + switch (sc) { + case SpvStorageClassUniformConstant: + case SpvStorageClassFunction: + case SpvStorageClassPrivate: + case SpvStorageClassWorkgroup: + case SpvStorageClassAtomicCounter: + // These are always allowed. + break; + case SpvStorageClassStorageBuffer: + if (!_.features().variable_pointers_storage_buffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "StorageBuffer pointer operand " + << _.getIdName(argument_id) + << " requires a variable pointers capability"; + } + break; + default: + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Invalid storage class for pointer operand " + << _.getIdName(argument_id); + } + + // Validate memory object declaration requirements. + if (argument->opcode() != SpvOpVariable && + argument->opcode() != SpvOpFunctionParameter) { + const bool ssbo_vptr = + _.features().variable_pointers_storage_buffer && + sc == SpvStorageClassStorageBuffer; + const bool wg_vptr = + _.features().variable_pointers && sc == SpvStorageClassWorkgroup; + if (!ssbo_vptr && !wg_vptr) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Pointer operand " << _.getIdName(argument_id) + << " must be a memory object declaration"; + } + } + } + } } return SPV_SUCCESS; } diff --git a/3rdparty/spirv-tools/source/val/validate_type.cpp b/3rdparty/spirv-tools/source/val/validate_type.cpp index 30fc2fbd8..8e027e488 100644 --- a/3rdparty/spirv-tools/source/val/validate_type.cpp +++ b/3rdparty/spirv-tools/source/val/validate_type.cpp @@ -262,17 +262,23 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) { spv_result_t ValidateTypePointer(ValidationState_t& _, const Instruction* inst) { - const auto type_id = inst->GetOperandAs(2); - const auto type = _.FindDef(type_id); + auto type_id = inst->GetOperandAs(2); + auto type = _.FindDef(type_id); if (!type || !spvOpcodeGeneratesType(type->opcode())) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpTypePointer Type '" << _.getIdName(type_id) << "' is not a type."; } // See if this points to a storage image. - if (type->opcode() == SpvOpTypeImage) { - const auto storage_class = inst->GetOperandAs(1); - if (storage_class == SpvStorageClassUniformConstant) { + const auto storage_class = inst->GetOperandAs(1); + if (storage_class == SpvStorageClassUniformConstant) { + // Unpack an optional level of arraying. + if (type->opcode() == SpvOpTypeArray || + type->opcode() == SpvOpTypeRuntimeArray) { + type_id = type->GetOperandAs(1); + type = _.FindDef(type_id); + } + if (type->opcode() == SpvOpTypeImage) { const auto sampled = type->GetOperandAs(6); // 2 indicates this image is known to be be used without a sampler, i.e. // a storage image. diff --git a/3rdparty/spirv-tools/test/opt/inst_bindless_check_test.cpp b/3rdparty/spirv-tools/test/opt/inst_bindless_check_test.cpp index a426ce04c..bde794ca5 100644 --- a/3rdparty/spirv-tools/test/opt/inst_bindless_check_test.cpp +++ b/3rdparty/spirv-tools/test/opt/inst_bindless_check_test.cpp @@ -630,274 +630,6 @@ OpFunctionEnd true); } -TEST_F(InstBindlessTest, ReuseConstsTypesBuiltins) { - // This test verifies that the pass resuses existing constants, types - // and builtin variables. This test was created by editing the SPIR-V - // from the Simple test. - - const std::string defs_before = - R"(OpCapability Shader -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %g_sAniso "g_sAniso" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 3 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %g_sAniso DescriptorSet 0 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -OpDecorate %85 DescriptorSet 7 -OpDecorate %85 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -%void = OpTypeVoid -%3 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%20 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%_arr_20_uint_128 = OpTypeArray %20 %uint_128 -%_ptr_UniformConstant__arr_20_uint_128 = OpTypePointer UniformConstant %_arr_20_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_20_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 -%35 = OpTypeSampler -%_ptr_UniformConstant_35 = OpTypePointer UniformConstant %35 -%g_sAniso = OpVariable %_ptr_UniformConstant_35 UniformConstant -%39 = OpTypeSampledImage %20 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_83 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_83 = OpTypePointer StorageBuffer %_struct_83 -%85 = OpVariable %_ptr_StorageBuffer__struct_83 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_9 = OpConstant %uint 9 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%131 = OpConstantNull %v4float -)"; - - const std::string defs_after = - R"(OpCapability Shader -OpExtension "SPV_KHR_storage_buffer_storage_class" -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord -OpExecutionMode %MainPs OriginUpperLeft -OpSource HLSL 500 -OpName %MainPs "MainPs" -OpName %g_tColor "g_tColor" -OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" -OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" -OpName %_ "" -OpName %g_sAniso "g_sAniso" -OpName %i_vTextureCoords "i.vTextureCoords" -OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" -OpDecorate %g_tColor DescriptorSet 3 -OpDecorate %g_tColor Binding 0 -OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 -OpDecorate %PerViewConstantBuffer_t Block -OpDecorate %g_sAniso DescriptorSet 0 -OpDecorate %i_vTextureCoords Location 0 -OpDecorate %_entryPointOutput_vColor Location 0 -OpDecorate %10 DescriptorSet 7 -OpDecorate %10 Binding 0 -OpDecorate %gl_FragCoord BuiltIn FragCoord -OpDecorate %_runtimearr_uint ArrayStride 4 -OpDecorate %_struct_34 Block -OpMemberDecorate %_struct_34 0 Offset 0 -OpMemberDecorate %_struct_34 1 Offset 4 -OpDecorate %74 DescriptorSet 7 -OpDecorate %74 Binding 0 -%void = OpTypeVoid -%12 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v2float = OpTypeVector %float 2 -%v4float = OpTypeVector %float 4 -%int = OpTypeInt 32 1 -%int_0 = OpConstant %int 0 -%18 = OpTypeImage %float 2D 0 0 0 1 Unknown -%uint = OpTypeInt 32 0 -%uint_128 = OpConstant %uint 128 -%_arr_18_uint_128 = OpTypeArray %18 %uint_128 -%_ptr_UniformConstant__arr_18_uint_128 = OpTypePointer UniformConstant %_arr_18_uint_128 -%g_tColor = OpVariable %_ptr_UniformConstant__arr_18_uint_128 UniformConstant -%PerViewConstantBuffer_t = OpTypeStruct %uint -%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t -%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant -%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint -%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 -%26 = OpTypeSampler -%_ptr_UniformConstant_26 = OpTypePointer UniformConstant %26 -%g_sAniso = OpVariable %_ptr_UniformConstant_26 UniformConstant -%28 = OpTypeSampledImage %18 -%_ptr_Input_v2float = OpTypePointer Input %v2float -%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output -%uint_0 = OpConstant %uint 0 -%bool = OpTypeBool -%_runtimearr_uint = OpTypeRuntimeArray %uint -%_struct_34 = OpTypeStruct %uint %_runtimearr_uint -%_ptr_StorageBuffer__struct_34 = OpTypePointer StorageBuffer %_struct_34 -%10 = OpVariable %_ptr_StorageBuffer__struct_34 StorageBuffer -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint -%uint_10 = OpConstant %uint 10 -%uint_4 = OpConstant %uint 4 -%uint_1 = OpConstant %uint 1 -%uint_23 = OpConstant %uint 23 -%uint_2 = OpConstant %uint 2 -%uint_9 = OpConstant %uint 9 -%uint_3 = OpConstant %uint 3 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%gl_FragCoord = OpVariable %_ptr_Input_v4float Input -%v4uint = OpTypeVector %uint 4 -%uint_5 = OpConstant %uint 5 -%uint_6 = OpConstant %uint 6 -%uint_7 = OpConstant %uint 7 -%uint_8 = OpConstant %uint 8 -%50 = OpConstantNull %v4float -%68 = OpTypeFunction %void %uint %uint %uint %uint -%74 = OpVariable %_ptr_StorageBuffer__struct_34 StorageBuffer -%uint_82 = OpConstant %uint 82 -)"; - - const std::string func_before = - R"(%MainPs = OpFunction %void None %3 -%5 = OpLabel -%53 = OpLoad %v2float %i_vTextureCoords -%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%64 = OpLoad %uint %63 -%65 = OpAccessChain %_ptr_UniformConstant_20 %g_tColor %64 -%67 = OpLoad %35 %g_sAniso -%78 = OpLoad %20 %65 -%79 = OpSampledImage %39 %78 %67 -%71 = OpImageSampleImplicitLod %v4float %79 %53 -OpStore %_entryPointOutput_vColor %71 -OpReturn -OpFunctionEnd -)"; - - const std::string func_after = - R"(%MainPs = OpFunction %void None %12 -%51 = OpLabel -%52 = OpLoad %v2float %i_vTextureCoords -%53 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 -%54 = OpLoad %uint %53 -%55 = OpAccessChain %_ptr_UniformConstant_18 %g_tColor %54 -%56 = OpLoad %26 %g_sAniso -%57 = OpLoad %18 %55 -%58 = OpSampledImage %28 %57 %56 -%60 = OpULessThan %bool %54 %uint_128 -OpSelectionMerge %61 None -OpBranchConditional %60 %62 %63 -%62 = OpLabel -%64 = OpLoad %18 %55 -%65 = OpSampledImage %28 %64 %56 -%66 = OpImageSampleImplicitLod %v4float %65 %52 -OpBranch %61 -%63 = OpLabel -%105 = OpFunctionCall %void %67 %uint_82 %uint_0 %54 %uint_128 -OpBranch %61 -%61 = OpLabel -%106 = OpPhi %v4float %66 %62 %50 %63 -OpStore %_entryPointOutput_vColor %106 -OpReturn -OpFunctionEnd -)"; - - const std::string output_func = - R"(%67 = OpFunction %void None %68 -%69 = OpFunctionParameter %uint -%70 = OpFunctionParameter %uint -%71 = OpFunctionParameter %uint -%72 = OpFunctionParameter %uint -%73 = OpLabel -%75 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_0 -%76 = OpAtomicIAdd %uint %75 %uint_4 %uint_0 %uint_9 -%77 = OpIAdd %uint %76 %uint_9 -%78 = OpArrayLength %uint %74 1 -%79 = OpULessThanEqual %bool %77 %78 -OpSelectionMerge %80 None -OpBranchConditional %79 %81 %80 -%81 = OpLabel -%82 = OpIAdd %uint %76 %uint_0 -%83 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %82 -OpStore %83 %uint_9 -%84 = OpIAdd %uint %76 %uint_1 -%85 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %84 -OpStore %85 %uint_23 -%86 = OpIAdd %uint %76 %uint_2 -%87 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %86 -OpStore %87 %69 -%88 = OpIAdd %uint %76 %uint_3 -%89 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %88 -OpStore %89 %uint_4 -%90 = OpLoad %v4float %gl_FragCoord -%91 = OpBitcast %v4uint %90 -%92 = OpCompositeExtract %uint %91 0 -%93 = OpIAdd %uint %76 %uint_4 -%94 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %93 -OpStore %94 %92 -%95 = OpCompositeExtract %uint %91 1 -%96 = OpIAdd %uint %76 %uint_5 -%97 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %96 -OpStore %97 %95 -%98 = OpIAdd %uint %76 %uint_6 -%99 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %98 -OpStore %99 %70 -%100 = OpIAdd %uint %76 %uint_7 -%101 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %100 -OpStore %101 %71 -%102 = OpIAdd %uint %76 %uint_8 -%103 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %102 -OpStore %103 %72 -OpBranch %80 -%80 = OpLabel -OpReturn -OpFunctionEnd -)"; - - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck( - defs_before + func_before, defs_after + func_after + output_func, true, - true); -} - TEST_F(InstBindlessTest, InstrumentOpImage) { // This test verifies that the pass will correctly instrument shader // using OpImage. This test was created by editing the SPIR-V @@ -1848,6 +1580,261 @@ OpFunctionEnd true); } +TEST_F(InstBindlessTest, RuntimeArray) { + // This test verifies that the pass will correctly instrument shader + // with runtime descriptor array. This test was created by editing the + // SPIR-V from the Simple test. + + const std::string defs_before = + R"(OpCapability Shader +OpCapability RuntimeDescriptorArrayEXT +OpExtension "SPV_EXT_descriptor_indexing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 0 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %g_sAniso Binding 1 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%20 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%_rarr_20 = OpTypeRuntimeArray %20 +%_ptr_UniformConstant__arr_20 = OpTypePointer UniformConstant %_rarr_20 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_20 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 +%35 = OpTypeSampler +%_ptr_UniformConstant_35 = OpTypePointer UniformConstant %35 +%g_sAniso = OpVariable %_ptr_UniformConstant_35 UniformConstant +%39 = OpTypeSampledImage %20 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability RuntimeDescriptorArrayEXT +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 0 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %g_sAniso Binding 1 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_40 Block +OpMemberDecorate %_struct_40 0 Offset 0 +OpDecorate %42 DescriptorSet 7 +OpDecorate %42 Binding 1 +OpDecorate %_struct_64 Block +OpMemberDecorate %_struct_64 0 Offset 0 +OpMemberDecorate %_struct_64 1 Offset 4 +OpDecorate %66 DescriptorSet 7 +OpDecorate %66 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%16 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%_runtimearr_16 = OpTypeRuntimeArray %16 +%_ptr_UniformConstant__runtimearr_16 = OpTypePointer UniformConstant %_runtimearr_16 +%g_tColor = OpVariable %_ptr_UniformConstant__runtimearr_16 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 +%24 = OpTypeSampler +%_ptr_UniformConstant_24 = OpTypePointer UniformConstant %24 +%g_sAniso = OpVariable %_ptr_UniformConstant_24 UniformConstant +%26 = OpTypeSampledImage %16 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%uint_0 = OpConstant %uint 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_40 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_40 = OpTypePointer StorageBuffer %_struct_40 +%42 = OpVariable %_ptr_StorageBuffer__struct_40 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%bool = OpTypeBool +%58 = OpTypeFunction %void %uint %uint %uint %uint +%_struct_64 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_64 = OpTypePointer StorageBuffer %_struct_64 +%66 = OpVariable %_ptr_StorageBuffer__struct_64 StorageBuffer +%uint_9 = OpConstant %uint 9 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_59 = OpConstant %uint 59 +%110 = OpConstantNull %v4float +)"; + + const std::string func_before = + R"(%MainPs = OpFunction %void None %3 +%5 = OpLabel +%53 = OpLoad %v2float %i_vTextureCoords +%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%64 = OpLoad %uint %63 +%65 = OpAccessChain %_ptr_UniformConstant_20 %g_tColor %64 +%66 = OpLoad %20 %65 +%67 = OpLoad %35 %g_sAniso +%68 = OpSampledImage %39 %66 %67 +%71 = OpImageSampleImplicitLod %v4float %68 %53 +OpStore %_entryPointOutput_vColor %71 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%MainPs = OpFunction %void None %10 +%29 = OpLabel +%30 = OpLoad %v2float %i_vTextureCoords +%31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%32 = OpLoad %uint %31 +%33 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %32 +%34 = OpLoad %16 %33 +%35 = OpLoad %24 %g_sAniso +%36 = OpSampledImage %26 %34 %35 +%44 = OpAccessChain %_ptr_StorageBuffer_uint %42 %uint_0 %uint_1 +%45 = OpLoad %uint %44 +%46 = OpIAdd %uint %45 %uint_0 +%47 = OpAccessChain %_ptr_StorageBuffer_uint %42 %uint_0 %46 +%48 = OpLoad %uint %47 +%50 = OpULessThan %bool %32 %48 +OpSelectionMerge %51 None +OpBranchConditional %50 %52 %53 +%52 = OpLabel +%54 = OpLoad %16 %33 +%55 = OpSampledImage %26 %54 %35 +%56 = OpImageSampleImplicitLod %v4float %55 %30 +OpBranch %51 +%53 = OpLabel +%109 = OpFunctionCall %void %57 %uint_59 %uint_0 %32 %48 +OpBranch %51 +%51 = OpLabel +%111 = OpPhi %v4float %56 %52 %110 %53 +OpStore %_entryPointOutput_vColor %111 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(%57 = OpFunction %void None %58 +%59 = OpFunctionParameter %uint +%60 = OpFunctionParameter %uint +%61 = OpFunctionParameter %uint +%62 = OpFunctionParameter %uint +%63 = OpLabel +%67 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_0 +%70 = OpAtomicIAdd %uint %67 %uint_4 %uint_0 %uint_9 +%71 = OpIAdd %uint %70 %uint_9 +%72 = OpArrayLength %uint %66 1 +%73 = OpULessThanEqual %bool %71 %72 +OpSelectionMerge %74 None +OpBranchConditional %73 %75 %74 +%75 = OpLabel +%76 = OpIAdd %uint %70 %uint_0 +%77 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %76 +OpStore %77 %uint_9 +%79 = OpIAdd %uint %70 %uint_1 +%80 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %79 +OpStore %80 %uint_23 +%82 = OpIAdd %uint %70 %uint_2 +%83 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %82 +OpStore %83 %59 +%85 = OpIAdd %uint %70 %uint_3 +%86 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %85 +OpStore %86 %uint_4 +%89 = OpLoad %v4float %gl_FragCoord +%91 = OpBitcast %v4uint %89 +%92 = OpCompositeExtract %uint %91 0 +%93 = OpIAdd %uint %70 %uint_4 +%94 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %93 +OpStore %94 %92 +%95 = OpCompositeExtract %uint %91 1 +%97 = OpIAdd %uint %70 %uint_5 +%98 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %97 +OpStore %98 %95 +%100 = OpIAdd %uint %70 %uint_6 +%101 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %100 +OpStore %101 %60 +%103 = OpIAdd %uint %70 %uint_7 +%104 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %103 +OpStore %104 %61 +%106 = OpIAdd %uint %70 %uint_8 +%107 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %106 +OpStore %107 %62 +OpBranch %74 +%74 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + output_func, true, + true); +} + // TODO(greg-lunarg): Add tests to verify handling of these cases: // // TODO(greg-lunarg): Come up with cases to put here :) diff --git a/3rdparty/spirv-tools/test/opt/optimizer_test.cpp b/3rdparty/spirv-tools/test/opt/optimizer_test.cpp index 90abc00d0..a7246b299 100644 --- a/3rdparty/spirv-tools/test/opt/optimizer_test.cpp +++ b/3rdparty/spirv-tools/test/opt/optimizer_test.cpp @@ -222,6 +222,133 @@ TEST(Optimizer, CanRegisterPassesFromFlags) { EXPECT_EQ(msg_level, SPV_MSG_ERROR); } +TEST(Optimizer, WebGPUModeSetsCorrectPasses) { + Optimizer opt(SPV_ENV_WEBGPU_0); + opt.RegisterWebGPUPasses(); + 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 = { + "eliminate-dead-branches", "eliminate-dead-code-aggressive", + "flatten-decorations", "strip-debug"}; + 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]); +} + +TEST(Optimizer, WebGPUModeFlattenDecorationsRuns) { + const std::string input = R"(OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %main "main" %hue %saturation %value +OpExecutionMode %main OriginUpperLeft +OpDecorate %group Flat +OpDecorate %group NoPerspective +%group = OpDecorationGroup +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float +%hue = OpVariable %_ptr_Input_float Input +%saturation = OpVariable %_ptr_Input_float Input +%value = OpVariable %_ptr_Input_float Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string expected = R"(OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %1 "main" %2 %3 %4 +OpExecutionMode %1 OriginUpperLeft +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float +%2 = OpVariable %_ptr_Input_float Input +%3 = OpVariable %_ptr_Input_float Input +%4 = OpVariable %_ptr_Input_float Input +%1 = OpFunction %void None %7 +%10 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SpirvTools tools(SPV_ENV_WEBGPU_0); + std::vector binary; + tools.Assemble(input, &binary); + + Optimizer opt(SPV_ENV_WEBGPU_0); + opt.RegisterWebGPUPasses(); + + std::vector optimized; + ValidatorOptions validator_options; + ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized, + validator_options, true)); + + std::string disassembly; + tools.Disassemble(optimized.data(), optimized.size(), &disassembly); + + EXPECT_EQ(expected, disassembly); +} + +TEST(Optimizer, WebGPUModeStripDebugRuns) { + const std::string input = R"(OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Vertex %func "shader" +OpName %main "main" +OpName %void_fn "void_fn" +%void = OpTypeVoid +%void_f = OpTypeFunction %void +%func = OpFunction %void None %void_f +%label = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string expected = R"(OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Vertex %1 "shader" +%void = OpTypeVoid +%5 = OpTypeFunction %void +%1 = OpFunction %void None %5 +%6 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SpirvTools tools(SPV_ENV_WEBGPU_0); + std::vector binary; + tools.Assemble(input, &binary); + + Optimizer opt(SPV_ENV_WEBGPU_0); + opt.RegisterWebGPUPasses(); + + std::vector optimized; + ValidatorOptions validator_options; + ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized, + validator_options, true)); + + std::string disassembly; + tools.Disassemble(optimized.data(), optimized.size(), &disassembly); + + EXPECT_EQ(expected, disassembly); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/reduce/CMakeLists.txt b/3rdparty/spirv-tools/test/reduce/CMakeLists.txt index c1c9d7676..df9542bfd 100644 --- a/3rdparty/spirv-tools/test/reduce/CMakeLists.txt +++ b/3rdparty/spirv-tools/test/reduce/CMakeLists.txt @@ -20,6 +20,7 @@ add_spvtools_unittest(TARGET reduce reduce_test_util.cpp reduce_test_util.h reducer_test.cpp + remove_function_test.cpp remove_opname_instruction_test.cpp remove_unreferenced_instruction_test.cpp structured_loop_to_selection_test.cpp diff --git a/3rdparty/spirv-tools/test/reduce/remove_function_test.cpp b/3rdparty/spirv-tools/test/reduce/remove_function_test.cpp new file mode 100644 index 000000000..6508bc9fc --- /dev/null +++ b/3rdparty/spirv-tools/test/reduce/remove_function_test.cpp @@ -0,0 +1,294 @@ +// 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 "reduce_test_util.h" +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "source/reduce/remove_function_reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { +namespace { + +// Helper to count the number of functions in the module. +// Remove if there turns out to be a more direct way to do this. +uint32_t count_functions(opt::IRContext* context) { + uint32_t result = 0; + for (auto& function : *context->module()) { + (void)(function); + ++result; + } + return result; +} + +TEST(RemoveFunctionTest, BasicCheck) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %10 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + + ASSERT_EQ(3, count_functions(context.get())); + + auto ops = + RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities( + context.get()); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + ASSERT_EQ(2, count_functions(context.get())); + + std::string after_first = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_first, context.get()); + + ops = RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities( + context.get()); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + ASSERT_EQ(1, count_functions(context.get())); + + std::string after_second = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_second, context.get()); +} + +TEST(RemoveFunctionTest, NothingToRemove) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %10 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + auto ops = + RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities( + context.get()); + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveFunctionTest, TwoRemovableFunctions) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + + ASSERT_EQ(3, count_functions(context.get())); + + auto ops = + RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities( + context.get()); + ASSERT_EQ(2, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + ASSERT_EQ(2, count_functions(context.get())); + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + ASSERT_EQ(1, count_functions(context.get())); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after, context.get()); +} + +TEST(RemoveFunctionTest, NoRemovalsDueToOpName) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "foo(" + OpName %8 "bar(" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + auto ops = + RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities( + context.get()); + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveFunctionTest, NoRemovalDueToLinkageDecoration) { + // The non-entry point function is not removable because it is referenced by a + // linkage decoration. Thus no function can be removed. + std::string shader = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpName %1 "main" + OpDecorate %2 LinkageAttributes "ExportedFunc" Export + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %1 = OpFunction %4 None %5 + %6 = OpLabel + OpReturn + OpFunctionEnd + %2 = OpFunction %4 None %5 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + auto ops = + RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities( + context.get()); + ASSERT_EQ(0, ops.size()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/val/CMakeLists.txt b/3rdparty/spirv-tools/test/val/CMakeLists.txt index b4fad2879..9f538c285 100644 --- a/3rdparty/spirv-tools/test/val/CMakeLists.txt +++ b/3rdparty/spirv-tools/test/val/CMakeLists.txt @@ -51,8 +51,9 @@ add_spvtools_unittest(TARGET val_limits PCH_FILE pch_test_val ) -add_spvtools_unittest(TARGET val_ijklmnop +add_spvtools_unittest(TARGET val_fghijklmnop SRCS + val_function_test.cpp val_id_test.cpp val_image_test.cpp val_interfaces_test.cpp diff --git a/3rdparty/spirv-tools/test/val/val_builtins_test.cpp b/3rdparty/spirv-tools/test/val/val_builtins_test.cpp index 9971464c5..797194f58 100644 --- a/3rdparty/spirv-tools/test/val/val_builtins_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_builtins_test.cpp @@ -1995,7 +1995,7 @@ INSTANTIATE_TEST_SUITE_P( "needs to be a 4-component 32-bit float vector", "is not a float vector")))); -INSTANTIATE_TEST_CASE_P( +INSTANTIATE_TEST_SUITE_P( PositionArrayedF32Vec4Vertex, ValidateWebGPUCombineBuiltInArrayedVariable, Combine(Values("Position"), Values("Vertex"), Values("Output"), Values("%f32vec4"), diff --git a/3rdparty/spirv-tools/test/val/val_decoration_test.cpp b/3rdparty/spirv-tools/test/val/val_decoration_test.cpp index c8dfc8d38..1247a16f5 100644 --- a/3rdparty/spirv-tools/test/val/val_decoration_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_decoration_test.cpp @@ -5517,6 +5517,8 @@ std::string ShaderWithNonWritableTarget(const std::string& target, return std::string(R"( OpCapability Shader + OpCapability RuntimeDescriptorArrayEXT + OpExtension "SPV_EXT_descriptor_indexing" OpExtension "SPV_KHR_storage_buffer_storage_class" OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %main "main" @@ -5546,6 +5548,8 @@ std::string ShaderWithNonWritableTarget(const std::string& target, %void_fn = OpTypeFunction %void %float = OpTypeFloat 32 %float_0 = OpConstant %float 0 + %int = OpTypeInt 32 0 + %int_2 = OpConstant %int 2 %struct_b = OpTypeStruct %float %struct_bb = OpTypeStruct %float %rtarr = OpTypeRuntimeArray %float @@ -5555,6 +5559,8 @@ std::string ShaderWithNonWritableTarget(const std::string& target, %imstor = OpTypeImage %float 2D 0 0 0 2 R32f ; sampled image %imsam = OpTypeImage %float 2D 0 0 0 1 R32f +%array_imstor = OpTypeArray %imstor %int_2 +%rta_imstor = OpTypeRuntimeArray %imstor %_ptr_Uniform_stb = OpTypePointer Uniform %struct_b %_ptr_Uniform_stbb = OpTypePointer Uniform %struct_bb @@ -5565,6 +5571,8 @@ std::string ShaderWithNonWritableTarget(const std::string& target, %_ptr_Function = OpTypePointer Function %float %_ptr_imstor = OpTypePointer UniformConstant %imstor %_ptr_imsam = OpTypePointer UniformConstant %imsam +%_ptr_array_imstor = OpTypePointer UniformConstant %array_imstor +%_ptr_rta_imstor = OpTypePointer UniformConstant %rta_imstor %extra_fn = OpTypeFunction %void %float %_ptr_Private %_ptr_imstor @@ -5576,6 +5584,8 @@ std::string ShaderWithNonWritableTarget(const std::string& target, %var_priv = OpVariable %_ptr_Private Private %var_imstor = OpVariable %_ptr_imstor UniformConstant %var_imsam = OpVariable %_ptr_imsam UniformConstant +%var_array_imstor = OpVariable %_ptr_array_imstor UniformConstant +%var_rta_imstor = OpVariable %_ptr_rta_imstor UniformConstant %helper = OpFunction %void None %extra_fn %param_f = OpFunctionParameter %float @@ -5754,6 +5764,20 @@ TEST_F(ValidateDecorations, NonWritableVarFunctionBad) { "buffer\n %var_func")); } +TEST_F(ValidateDecorations, NonWritableArrayGood) { + std::string spirv = ShaderWithNonWritableTarget("%var_array_imstor"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateDecorations, NonWritableRuntimeArrayGood) { + std::string spirv = ShaderWithNonWritableTarget("%var_rta_imstor"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + TEST_P(ValidateWebGPUCombineDecorationResult, Decorate) { const char* const decoration = std::get<0>(GetParam()); const TestResult& test_result = std::get<1>(GetParam()); diff --git a/3rdparty/spirv-tools/test/val/val_fixtures.h b/3rdparty/spirv-tools/test/val/val_fixtures.h index 69bc9d82f..fd5368bc5 100644 --- a/3rdparty/spirv-tools/test/val/val_fixtures.h +++ b/3rdparty/spirv-tools/test/val/val_fixtures.h @@ -125,6 +125,13 @@ void ValidateBase::OverwriteAssembledBinary(uint32_t index, uint32_t word) { template spv_result_t ValidateBase::ValidateInstructions(spv_target_env env) { DestroyDiagnostic(); + if (binary_ == nullptr) { + fprintf(stderr, + "ERROR: Attempting to validate a null binary, did you forget to " + "call CompileSuccessfully?"); + fflush(stderr); + } + assert(binary_ != nullptr); return spvValidateWithOptions(ScopedContext(env).context, options_, get_const_binary(), &diagnostic_); } diff --git a/3rdparty/spirv-tools/test/val/val_function_test.cpp b/3rdparty/spirv-tools/test/val/val_function_test.cpp new file mode 100644 index 000000000..f3dd15e76 --- /dev/null +++ b/3rdparty/spirv-tools/test/val/val_function_test.cpp @@ -0,0 +1,424 @@ +// 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 +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Combine; +using ::testing::HasSubstr; +using ::testing::Values; + +using ValidateFunctionCall = spvtest::ValidateBase; + +std::string GenerateShader(const std::string& storage_class, + const std::string& capabilities, + const std::string& extensions) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability AtomicStorage +)" + capabilities + R"( +OpExtension "SPV_KHR_storage_buffer_storage_class" +)" + + extensions + R"( +OpMemoryModel Logical GLSL450 +OpName %var "var" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr = OpTypePointer )" + storage_class + R"( %int +%caller_ty = OpTypeFunction %void +%callee_ty = OpTypeFunction %void %ptr +)"; + + if (storage_class != "Function") { + spirv += "%var = OpVariable %ptr " + storage_class; + } + + spirv += R"( +%caller = OpFunction %void None %caller_ty +%1 = OpLabel +)"; + + if (storage_class == "Function") { + spirv += "%var = OpVariable %ptr Function"; + } + + spirv += R"( +%call = OpFunctionCall %void %callee %var +OpReturn +OpFunctionEnd +%callee = OpFunction %void None %callee_ty +%param = OpFunctionParameter %ptr +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + return spirv; +} + +std::string GenerateShaderParameter(const std::string& storage_class, + const std::string& capabilities, + const std::string& extensions) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability AtomicStorage +)" + capabilities + R"( +OpExtension "SPV_KHR_storage_buffer_storage_class" +)" + + extensions + R"( +OpMemoryModel Logical GLSL450 +OpName %p "p" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr = OpTypePointer )" + storage_class + R"( %int +%func_ty = OpTypeFunction %void %ptr +%caller = OpFunction %void None %func_ty +%p = OpFunctionParameter %ptr +%1 = OpLabel +%call = OpFunctionCall %void %callee %p +OpReturn +OpFunctionEnd +%callee = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + return spirv; +} + +std::string GenerateShaderAccessChain(const std::string& storage_class, + const std::string& capabilities, + const std::string& extensions) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability AtomicStorage +)" + capabilities + R"( +OpExtension "SPV_KHR_storage_buffer_storage_class" +)" + + extensions + R"( +OpMemoryModel Logical GLSL450 +OpName %var "var" +OpName %gep "gep" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int2 = OpTypeVector %int 2 +%int_0 = OpConstant %int 0 +%ptr = OpTypePointer )" + storage_class + R"( %int2 +%ptr2 = OpTypePointer )" + + storage_class + R"( %int +%caller_ty = OpTypeFunction %void +%callee_ty = OpTypeFunction %void %ptr2 +)"; + + if (storage_class != "Function") { + spirv += "%var = OpVariable %ptr " + storage_class; + } + + spirv += R"( +%caller = OpFunction %void None %caller_ty +%1 = OpLabel +)"; + + if (storage_class == "Function") { + spirv += "%var = OpVariable %ptr Function"; + } + + spirv += R"( +%gep = OpAccessChain %ptr2 %var %int_0 +%call = OpFunctionCall %void %callee %gep +OpReturn +OpFunctionEnd +%callee = OpFunction %void None %callee_ty +%param = OpFunctionParameter %ptr2 +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + return spirv; +} + +TEST_P(ValidateFunctionCall, VariableNoVariablePointers) { + const std::string storage_class = GetParam(); + + std::string spirv = GenerateShader(storage_class, "", ""); + + const std::vector valid_storage_classes = { + "UniformConstant", "Function", "Private", "Workgroup", "AtomicCounter"}; + bool valid = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + + CompileSuccessfully(spirv); + if (valid) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + if (storage_class == "StorageBuffer") { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("StorageBuffer pointer operand 1[%var] requires a " + "variable pointers capability")); + } else { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 1[%var]")); + } + } +} + +TEST_P(ValidateFunctionCall, VariableVariablePointersStorageClass) { + const std::string storage_class = GetParam(); + + std::string spirv = GenerateShader( + storage_class, "OpCapability VariablePointersStorageBuffer", + "OpExtension \"SPV_KHR_variable_pointers\""); + + const std::vector valid_storage_classes = { + "UniformConstant", "Function", "Private", + "Workgroup", "StorageBuffer", "AtomicCounter"}; + bool valid = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + + CompileSuccessfully(spirv); + if (valid) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 1[%var]")); + } +} + +TEST_P(ValidateFunctionCall, VariableVariablePointers) { + const std::string storage_class = GetParam(); + + std::string spirv = + GenerateShader(storage_class, "OpCapability VariablePointers", + "OpExtension \"SPV_KHR_variable_pointers\""); + + const std::vector valid_storage_classes = { + "UniformConstant", "Function", "Private", + "Workgroup", "StorageBuffer", "AtomicCounter"}; + bool valid = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + + CompileSuccessfully(spirv); + if (valid) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 1[%var]")); + } +} + +TEST_P(ValidateFunctionCall, ParameterNoVariablePointers) { + const std::string storage_class = GetParam(); + + std::string spirv = GenerateShaderParameter(storage_class, "", ""); + + const std::vector valid_storage_classes = { + "UniformConstant", "Function", "Private", "Workgroup", "AtomicCounter"}; + bool valid = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + + CompileSuccessfully(spirv); + if (valid) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + if (storage_class == "StorageBuffer") { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("StorageBuffer pointer operand 1[%p] requires a " + "variable pointers capability")); + } else { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 1[%p]")); + } + } +} + +TEST_P(ValidateFunctionCall, ParameterVariablePointersStorageBuffer) { + const std::string storage_class = GetParam(); + + std::string spirv = GenerateShaderParameter( + storage_class, "OpCapability VariablePointersStorageBuffer", + "OpExtension \"SPV_KHR_variable_pointers\""); + + const std::vector valid_storage_classes = { + "UniformConstant", "Function", "Private", + "Workgroup", "StorageBuffer", "AtomicCounter"}; + bool valid = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + + CompileSuccessfully(spirv); + if (valid) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 1[%p]")); + } +} + +TEST_P(ValidateFunctionCall, ParameterVariablePointers) { + const std::string storage_class = GetParam(); + + std::string spirv = + GenerateShaderParameter(storage_class, "OpCapability VariablePointers", + "OpExtension \"SPV_KHR_variable_pointers\""); + + const std::vector valid_storage_classes = { + "UniformConstant", "Function", "Private", + "Workgroup", "StorageBuffer", "AtomicCounter"}; + bool valid = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + + CompileSuccessfully(spirv); + if (valid) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 1[%p]")); + } +} + +TEST_P(ValidateFunctionCall, NonMemoryObjectDeclarationNoVariablePointers) { + const std::string storage_class = GetParam(); + + std::string spirv = GenerateShaderAccessChain(storage_class, "", ""); + + const std::vector valid_storage_classes = { + "UniformConstant", "Function", "Private", "Workgroup", "AtomicCounter"}; + bool valid_sc = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + if (valid_sc) { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Pointer operand 2[%gep] must be a memory object declaration")); + } else { + if (storage_class == "StorageBuffer") { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("StorageBuffer pointer operand 2[%gep] requires a " + "variable pointers capability")); + } else { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 2[%gep]")); + } + } +} + +TEST_P(ValidateFunctionCall, + NonMemoryObjectDeclarationVariablePointersStorageBuffer) { + const std::string storage_class = GetParam(); + + std::string spirv = GenerateShaderAccessChain( + storage_class, "OpCapability VariablePointersStorageBuffer", + "OpExtension \"SPV_KHR_variable_pointers\""); + + const std::vector valid_storage_classes = { + "UniformConstant", "Function", "Private", + "Workgroup", "StorageBuffer", "AtomicCounter"}; + bool valid_sc = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + bool validate = storage_class == "StorageBuffer"; + + CompileSuccessfully(spirv); + if (validate) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + if (valid_sc) { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Pointer operand 2[%gep] must be a memory object declaration")); + } else { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 2[%gep]")); + } + } +} + +TEST_P(ValidateFunctionCall, NonMemoryObjectDeclarationVariablePointers) { + const std::string storage_class = GetParam(); + + std::string spirv = + GenerateShaderAccessChain(storage_class, "OpCapability VariablePointers", + "OpExtension \"SPV_KHR_variable_pointers\""); + + const std::vector valid_storage_classes = { + "UniformConstant", "Function", "Private", + "Workgroup", "StorageBuffer", "AtomicCounter"}; + bool valid_sc = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + bool validate = + storage_class == "StorageBuffer" || storage_class == "Workgroup"; + + CompileSuccessfully(spirv); + if (validate) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + if (valid_sc) { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Pointer operand 2[%gep] must be a memory object declaration")); + } else { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 2[%gep]")); + } + } +} + +INSTANTIATE_TEST_SUITE_P(StorageClass, ValidateFunctionCall, + Values("UniformConstant", "Input", "Uniform", "Output", + "Workgroup", "Private", "Function", + "PushConstant", "Image", "StorageBuffer", + "AtomicCounter")); +} // namespace +} // namespace val +} // namespace spvtools diff --git a/3rdparty/spirv-tools/test/val/val_id_test.cpp b/3rdparty/spirv-tools/test/val/val_id_test.cpp index 9910a09dd..df177f25f 100644 --- a/3rdparty/spirv-tools/test/val/val_id_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_id_test.cpp @@ -2274,6 +2274,7 @@ void createVariablePointerSpirvProgram(std::ostringstream* spirv, *spirv << "OpCapability VariablePointers "; *spirv << "OpExtension \"SPV_KHR_variable_pointers\" "; } + *spirv << "OpExtension \"SPV_KHR_storage_buffer_storage_class\" "; *spirv << R"( OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" @@ -2282,12 +2283,12 @@ void createVariablePointerSpirvProgram(std::ostringstream* spirv, %bool = OpTypeBool %i32 = OpTypeInt 32 1 %f32 = OpTypeFloat 32 - %f32ptr = OpTypePointer Uniform %f32 + %f32ptr = OpTypePointer StorageBuffer %f32 %i = OpConstant %i32 1 %zero = OpConstant %i32 0 %float_1 = OpConstant %f32 1.0 - %ptr1 = OpVariable %f32ptr Uniform - %ptr2 = OpVariable %f32ptr Uniform + %ptr1 = OpVariable %f32ptr StorageBuffer + %ptr2 = OpVariable %f32ptr StorageBuffer )"; if (add_helper_function) { *spirv << R"( diff --git a/3rdparty/spirv-tools/tools/CMakeLists.txt b/3rdparty/spirv-tools/tools/CMakeLists.txt index 9fb3a91a0..d42d4cc61 100644 --- a/3rdparty/spirv-tools/tools/CMakeLists.txt +++ b/3rdparty/spirv-tools/tools/CMakeLists.txt @@ -42,7 +42,9 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES}) add_spvtools_tool(TARGET spirv-dis SRCS dis/dis.cpp LIBS ${SPIRV_TOOLS}) add_spvtools_tool(TARGET spirv-val SRCS val/val.cpp util/cli_consumer.cpp LIBS ${SPIRV_TOOLS}) add_spvtools_tool(TARGET spirv-opt SRCS opt/opt.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS}) - add_spvtools_tool(TARGET spirv-reduce SRCS reduce/reduce.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-reduce ${SPIRV_TOOLS}) + if (NOT DEFINED IOS_PLATFORM) # iOS does not allow std::system calls which spirv-reduce requires + add_spvtools_tool(TARGET spirv-reduce SRCS reduce/reduce.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-reduce ${SPIRV_TOOLS}) + endif() add_spvtools_tool(TARGET spirv-link SRCS link/linker.cpp LIBS SPIRV-Tools-link ${SPIRV_TOOLS}) add_spvtools_tool(TARGET spirv-stats SRCS stats/stats.cpp @@ -62,7 +64,10 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES}) ${SPIRV_HEADER_INCLUDE_DIR}) set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val spirv-opt spirv-stats - spirv-cfg spirv-link spirv-reduce) + spirv-cfg spirv-link) + if(NOT DEFINED IOS_PLATFORM) + set(SPIRV_INSTALL_TARGETS ${SPIRV_INSTALL_TARGETS} spirv-reduce) + endif() if(SPIRV_BUILD_COMPRESSION) add_spvtools_tool(TARGET spirv-markv diff --git a/3rdparty/spirv-tools/tools/reduce/reduce.cpp b/3rdparty/spirv-tools/tools/reduce/reduce.cpp index e119e1ef9..bbbfd1abc 100644 --- a/3rdparty/spirv-tools/tools/reduce/reduce.cpp +++ b/3rdparty/spirv-tools/tools/reduce/reduce.cpp @@ -25,6 +25,7 @@ #include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h" #include "source/reduce/operand_to_undef_reduction_opportunity_finder.h" #include "source/reduce/reducer.h" +#include "source/reduce/remove_function_reduction_opportunity_finder.h" #include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h" #include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h" #include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h" @@ -243,6 +244,8 @@ int main(int argc, const char** argv) { StructuredLoopToSelectionReductionOpportunityFinder>()); reducer.AddReductionPass( spvtools::MakeUnique()); + reducer.AddReductionPass( + spvtools::MakeUnique()); reducer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);