From 2d6c2cebeb0f8cdf4e32fa54a425d30edbc35dbb 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, 30 Mar 2019 21:02:17 -0700 Subject: [PATCH] Updated spirv-tools. --- .../include/generated/build-version.inc | 2 +- .../include/spirv-tools/libspirv.h | 13 +- .../include/spirv-tools/libspirv.hpp | 14 +- .../include/spirv-tools/optimizer.hpp | 3 + .../spirv-tools/source/opt/basic_block.cpp | 12 ++ 3rdparty/spirv-tools/source/opt/basic_block.h | 6 + 3rdparty/spirv-tools/source/opt/optimizer.cpp | 7 + .../spirv-tools/source/opt/pass_manager.cpp | 14 ++ .../spirv-tools/source/opt/pass_manager.h | 29 ++- .../source/opt/struct_cfg_analysis.cpp | 4 +- .../spirv-tools/source/reduce/reducer.cpp | 38 +++- 3rdparty/spirv-tools/source/reduce/reducer.h | 9 +- .../source/reduce/reduction_pass.cpp | 30 ++- .../source/reduce/reduction_pass.h | 25 ++- ...oop_to_selection_reduction_opportunity.cpp | 23 +-- ...selection_reduction_opportunity_finder.cpp | 19 +- .../source/spirv_reducer_options.cpp | 5 + .../source/spirv_reducer_options.h | 8 +- .../spirv-tools/source/val/validate_cfg.cpp | 32 +++ .../spirv-tools/source/val/validate_image.cpp | 45 ++++- .../spirv-tools/test/opt/block_merge_test.cpp | 2 +- 3rdparty/spirv-tools/test/opt/inline_test.cpp | 15 +- .../test/opt/struct_cfg_analysis_test.cpp | 20 ++ .../test/reduce/reduce_test_util.cpp | 24 +++ .../test/reduce/reduce_test_util.h | 4 + .../spirv-tools/test/reduce/reducer_test.cpp | 2 + .../remove_unreferenced_instruction_test.cpp | 153 ++------------- .../structured_loop_to_selection_test.cpp | 185 ++++++++++++++++++ .../validation_during_reduction_test.cpp | 16 +- .../spirv-tools/test/val/val_cfg_test.cpp | 139 ++++++++++++- 3rdparty/spirv-tools/test/val/val_id_test.cpp | 14 +- .../spirv-tools/test/val/val_image_test.cpp | 77 +++++++- .../test/val/val_validation_state_test.cpp | 14 +- 3rdparty/spirv-tools/tools/opt/opt.cpp | 4 + 3rdparty/spirv-tools/tools/reduce/reduce.cpp | 37 +--- 35 files changed, 775 insertions(+), 269 deletions(-) diff --git a/3rdparty/spirv-tools/include/generated/build-version.inc b/3rdparty/spirv-tools/include/generated/build-version.inc index 273ee54ba..4fcd7ac0a 100644 --- a/3rdparty/spirv-tools/include/generated/build-version.inc +++ b/3rdparty/spirv-tools/include/generated/build-version.inc @@ -1 +1 @@ -"v2019.2", "SPIRV-Tools v2019.2 027e592038bb74c2ffe6b07f9cc4e2ab1b3fbb84" +"v2019.2", "SPIRV-Tools v2019.2 2ddefa50b7e00747527827ee9aeddb9741bd6c2e" diff --git a/3rdparty/spirv-tools/include/spirv-tools/libspirv.h b/3rdparty/spirv-tools/include/spirv-tools/libspirv.h index 13bd959a8..da590373d 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/libspirv.h +++ b/3rdparty/spirv-tools/include/spirv-tools/libspirv.h @@ -557,14 +557,17 @@ SPIRV_TOOLS_EXPORT spv_reducer_options spvReducerOptionsCreate(); // Destroys the given reducer options object. SPIRV_TOOLS_EXPORT void spvReducerOptionsDestroy(spv_reducer_options options); -// Records the maximum number of reduction steps that should run before the -// reducer gives up. +// Sets the maximum number of reduction steps that should run before the reducer +// gives up. SPIRV_TOOLS_EXPORT void spvReducerOptionsSetStepLimit( spv_reducer_options options, uint32_t step_limit); -// Sets seed for random number generation. -SPIRV_TOOLS_EXPORT void spvReducerOptionsSetSeed(spv_reducer_options options, - uint32_t seed); +// Sets the fail-on-validation-error option; if true, the reducer will return +// kStateInvalid if a reduction step yields a state that fails SPIR-V +// validation. Otherwise, an invalid state is treated as uninteresting and the +// reduction backtracks and continues. +SPIRV_TOOLS_EXPORT void spvReducerOptionsSetFailOnValidationError( + spv_reducer_options options, bool fail_on_validation_error); // Encodes the given SPIR-V assembly text to its binary representation. The // length parameter specifies the number of bytes for text. Encoded binary will diff --git a/3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp b/3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp index ee4efe852..12c705591 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp +++ b/3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp @@ -151,16 +151,20 @@ class ReducerOptions { ~ReducerOptions() { spvReducerOptionsDestroy(options_); } // Allow implicit conversion to the underlying object. - operator spv_reducer_options() const { return options_; } + operator spv_reducer_options() const { // NOLINT(google-explicit-constructor) + return options_; + } - // Records the maximum number of reduction steps that should - // run before the reducer gives up. + // See spvReducerOptionsSetStepLimit. void set_step_limit(uint32_t step_limit) { spvReducerOptionsSetStepLimit(options_, step_limit); } - // Sets a seed to be used for random number generation. - void set_seed(uint32_t seed) { spvReducerOptionsSetSeed(options_, seed); } + // See spvReducerOptionsSetFailOnValidationError. + void set_fail_on_validation_error(bool fail_on_validation_error) { + spvReducerOptionsSetFailOnValidationError(options_, + fail_on_validation_error); + } private: spv_reducer_options options_; diff --git a/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp b/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp index 08ef5e67a..adfd492d3 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp +++ b/3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp @@ -199,6 +199,9 @@ class Optimizer { // |out| output stream. Optimizer& SetTimeReport(std::ostream* out); + // Sets the option to validate the module after each pass. + Optimizer& SetValidateAfterAll(bool validate); + private: struct Impl; // Opaque struct for holding internal data. std::unique_ptr impl_; // Unique pointer to internal data. diff --git a/3rdparty/spirv-tools/source/opt/basic_block.cpp b/3rdparty/spirv-tools/source/opt/basic_block.cpp index aafee5143..4da0a12b9 100644 --- a/3rdparty/spirv-tools/source/opt/basic_block.cpp +++ b/3rdparty/spirv-tools/source/opt/basic_block.cpp @@ -184,6 +184,12 @@ uint32_t BasicBlock::MergeBlockIdIfAny() const { return mbid; } +uint32_t BasicBlock::MergeBlockId() const { + uint32_t mbid = MergeBlockIdIfAny(); + assert(mbid && "Expected block to have a corresponding merge block"); + return mbid; +} + uint32_t BasicBlock::ContinueBlockIdIfAny() const { auto merge_ii = cend(); --merge_ii; @@ -197,6 +203,12 @@ uint32_t BasicBlock::ContinueBlockIdIfAny() const { return cbid; } +uint32_t BasicBlock::ContinueBlockId() const { + uint32_t cbid = ContinueBlockIdIfAny(); + assert(cbid && "Expected block to have a corresponding continue target"); + return cbid; +} + std::ostream& operator<<(std::ostream& str, const BasicBlock& block) { str << block.PrettyPrint(); return str; diff --git a/3rdparty/spirv-tools/source/opt/basic_block.h b/3rdparty/spirv-tools/source/opt/basic_block.h index ff3a41280..e53f3eae9 100644 --- a/3rdparty/spirv-tools/source/opt/basic_block.h +++ b/3rdparty/spirv-tools/source/opt/basic_block.h @@ -183,10 +183,16 @@ class BasicBlock { // block, if any. If none, returns zero. uint32_t MergeBlockIdIfAny() const; + // Returns MergeBlockIdIfAny() and asserts that it is non-zero. + uint32_t MergeBlockId() const; + // Returns the ID of the continue block declared by a merge instruction in // this block, if any. If none, returns zero. uint32_t ContinueBlockIdIfAny() const; + // Returns ContinueBlockIdIfAny() and asserts that it is non-zero. + uint32_t ContinueBlockId() const; + // Returns the terminator instruction. Assumes the terminator exists. Instruction* terminator() { return &*tail(); } const Instruction* terminator() const { return &*ctail(); } diff --git a/3rdparty/spirv-tools/source/opt/optimizer.cpp b/3rdparty/spirv-tools/source/opt/optimizer.cpp index d4499a880..887a9c22f 100644 --- a/3rdparty/spirv-tools/source/opt/optimizer.cpp +++ b/3rdparty/spirv-tools/source/opt/optimizer.cpp @@ -507,6 +507,8 @@ bool Optimizer::Run(const uint32_t* original_binary, context->set_max_id_bound(opt_options->max_id_bound_); + impl_->pass_manager.SetValidatorOptions(&opt_options->val_options_); + impl_->pass_manager.SetTargetEnv(impl_->target_env); auto status = impl_->pass_manager.Run(context.get()); if (status == opt::Pass::Status::SuccessWithChange || (status == opt::Pass::Status::SuccessWithoutChange && @@ -529,6 +531,11 @@ Optimizer& Optimizer::SetTimeReport(std::ostream* out) { return *this; } +Optimizer& Optimizer::SetValidateAfterAll(bool validate) { + impl_->pass_manager.SetValidateAfterAll(validate); + return *this; +} + Optimizer::PassToken CreateNullPass() { return MakeUnique(MakeUnique()); } diff --git a/3rdparty/spirv-tools/source/opt/pass_manager.cpp b/3rdparty/spirv-tools/source/opt/pass_manager.cpp index fa1e1d8a8..be53d3440 100644 --- a/3rdparty/spirv-tools/source/opt/pass_manager.cpp +++ b/3rdparty/spirv-tools/source/opt/pass_manager.cpp @@ -51,6 +51,20 @@ Pass::Status PassManager::Run(IRContext* context) { if (one_status == Pass::Status::Failure) return one_status; if (one_status == Pass::Status::SuccessWithChange) status = one_status; + if (validate_after_all_) { + spvtools::SpirvTools tools(target_env_); + tools.SetMessageConsumer(consumer()); + std::vector binary; + context->module()->ToBinary(&binary, true); + if (!tools.Validate(binary.data(), binary.size(), val_options_)) { + std::string msg = "Validation failed after pass "; + msg += pass->name(); + spv_position_t null_pos{0, 0, 0}; + consumer()(SPV_MSG_INTERNAL_ERROR, "", null_pos, msg.c_str()); + return Pass::Status::Failure; + } + } + // Reset the pass to free any memory used by the pass. pass.reset(nullptr); } diff --git a/3rdparty/spirv-tools/source/opt/pass_manager.h b/3rdparty/spirv-tools/source/opt/pass_manager.h index ed88aa17c..9686dddc2 100644 --- a/3rdparty/spirv-tools/source/opt/pass_manager.h +++ b/3rdparty/spirv-tools/source/opt/pass_manager.h @@ -43,7 +43,10 @@ class PassManager { PassManager() : consumer_(nullptr), print_all_stream_(nullptr), - time_report_stream_(nullptr) {} + time_report_stream_(nullptr), + target_env_(SPV_ENV_UNIVERSAL_1_2), + val_options_(nullptr), + validate_after_all_(false) {} // Sets the message consumer to the given |consumer|. void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); } @@ -89,6 +92,24 @@ class PassManager { return *this; } + // Sets the target environment for validation. + PassManager& SetTargetEnv(spv_target_env env) { + target_env_ = env; + return *this; + } + + // Sets the validation options. + PassManager& SetValidatorOptions(spv_validator_options options) { + val_options_ = options; + return *this; + } + + // Sets the option to validate after each pass. + PassManager& SetValidateAfterAll(bool validate) { + validate_after_all_ = validate; + return *this; + } + private: // Consumer for messages. MessageConsumer consumer_; @@ -100,6 +121,12 @@ class PassManager { // The output stream to write the resource utilization of each pass. If this // is null, no output is generated. std::ostream* time_report_stream_; + // The target environment. + spv_target_env target_env_; + // The validator options (used when validating each pass). + spv_validator_options val_options_; + // Controls whether validation occurs after every pass. + bool validate_after_all_; }; inline void PassManager::AddPass(std::unique_ptr pass) { diff --git a/3rdparty/spirv-tools/source/opt/struct_cfg_analysis.cpp b/3rdparty/spirv-tools/source/opt/struct_cfg_analysis.cpp index d78ec560e..dcfd4a59b 100644 --- a/3rdparty/spirv-tools/source/opt/struct_cfg_analysis.cpp +++ b/3rdparty/spirv-tools/source/opt/struct_cfg_analysis.cpp @@ -19,7 +19,7 @@ namespace { const uint32_t kMergeNodeIndex = 0; const uint32_t kContinueNodeIndex = 1; -} +} // namespace namespace spvtools { namespace opt { @@ -37,6 +37,8 @@ StructuredCFGAnalysis::StructuredCFGAnalysis(IRContext* ctx) : context_(ctx) { } void StructuredCFGAnalysis::AddBlocksInFunction(Function* func) { + if (func->begin() == func->end()) return; + std::list order; context_->cfg()->ComputeStructuredOrder(func, &*func->begin(), &order); diff --git a/3rdparty/spirv-tools/source/reduce/reducer.cpp b/3rdparty/spirv-tools/source/reduce/reducer.cpp index 29d32688c..462c13cff 100644 --- a/3rdparty/spirv-tools/source/reduce/reducer.cpp +++ b/3rdparty/spirv-tools/source/reduce/reducer.cpp @@ -15,10 +15,17 @@ #include #include +#include "source/reduce/merge_blocks_reduction_opportunity_finder.h" +#include "source/reduce/operand_to_const_reduction_opportunity_finder.h" +#include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h" +#include "source/reduce/operand_to_undef_reduction_opportunity_finder.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" #include "source/spirv_reducer_options.h" #include "reducer.h" -#include "reduction_pass.h" namespace spvtools { namespace reduce { @@ -105,13 +112,15 @@ Reducer::ReductionResultStatus Reducer::Run( do { auto maybe_result = pass->TryApplyReduction(current_binary); if (maybe_result.empty()) { - // This pass did not have any impact, so move on to the next pass. + // For this round, the pass has no more opportunities (chunks) to + // apply, so move on to the next pass. impl_->consumer( SPV_MSG_INFO, nullptr, {}, ("Pass " + pass->GetName() + " did not make a reduction step.") .c_str()); break; } + bool interesting = false; std::stringstream stringstream; reductions_applied++; stringstream << "Pass " << pass->GetName() << " made reduction step " @@ -125,6 +134,9 @@ Reducer::ReductionResultStatus Reducer::Run( // invalid binary from being regarded as interesting. impl_->consumer(SPV_MSG_INFO, nullptr, {}, "Reduction step produced an invalid binary."); + if (options->fail_on_validation_error) { + return Reducer::ReductionResultStatus::kStateInvalid; + } } else if (impl_->interestingness_function(maybe_result, reductions_applied)) { // Success! The binary produced by this reduction step is @@ -133,8 +145,11 @@ Reducer::ReductionResultStatus Reducer::Run( impl_->consumer(SPV_MSG_INFO, nullptr, {}, "Reduction step succeeded."); current_binary = std::move(maybe_result); + interesting = true; another_round_worthwhile = true; } + // We must call this before the next call to TryApplyReduction. + pass->NotifyInteresting(interesting); // Bail out if the reduction step limit has been reached. } while (!impl_->ReachedStepLimit(reductions_applied, options)); } @@ -153,6 +168,25 @@ Reducer::ReductionResultStatus Reducer::Run( return Reducer::ReductionResultStatus::kComplete; } +void Reducer::AddDefaultReductionPasses() { + AddReductionPass(spvtools::MakeUnique< + RemoveOpNameInstructionReductionOpportunityFinder>()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass(spvtools::MakeUnique< + RemoveUnreferencedInstructionReductionOpportunityFinder>()); + AddReductionPass(spvtools::MakeUnique< + StructuredLoopToSelectionReductionOpportunityFinder>()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass( + spvtools::MakeUnique()); +} + void Reducer::AddReductionPass( std::unique_ptr&& finder) { impl_->passes.push_back(spvtools::MakeUnique( diff --git a/3rdparty/spirv-tools/source/reduce/reducer.h b/3rdparty/spirv-tools/source/reduce/reducer.h index 5ff6b5050..81fb0dd77 100644 --- a/3rdparty/spirv-tools/source/reduce/reducer.h +++ b/3rdparty/spirv-tools/source/reduce/reducer.h @@ -34,7 +34,11 @@ class Reducer { kInitialStateNotInteresting, kReachedStepLimit, kComplete, - kInitialStateInvalid + kInitialStateInvalid, + + // Returned when the fail-on-validation-error option is set and a + // reduction step yields a state that fails validation. + kStateInvalid, }; // The type for a function that will take a binary and return true if and @@ -76,6 +80,9 @@ class Reducer { void SetInterestingnessFunction( InterestingnessFunction interestingness_function); + // Adds all default reduction passes. + void AddDefaultReductionPasses(); + // Adds a reduction pass based on the given finder to the sequence of passes // that will be iterated over. void AddReductionPass(std::unique_ptr&& finder); diff --git a/3rdparty/spirv-tools/source/reduce/reduction_pass.cpp b/3rdparty/spirv-tools/source/reduce/reduction_pass.cpp index 66e11cb59..dda752b87 100644 --- a/3rdparty/spirv-tools/source/reduce/reduction_pass.cpp +++ b/3rdparty/spirv-tools/source/reduce/reduction_pass.cpp @@ -36,20 +36,19 @@ std::vector ReductionPass::TryApplyReduction( std::vector> opportunities = finder_->GetAvailableOpportunities(context.get()); - if (!is_initialized_) { - is_initialized_ = true; - index_ = 0; - granularity_ = (uint32_t)opportunities.size(); - } - - if (opportunities.empty()) { - granularity_ = 1; - return std::vector(); + // There is no point in having a granularity larger than the number of + // opportunities, so reduce the granularity in this case. + if (granularity_ > opportunities.size()) { + granularity_ = std::max((uint32_t)1, (uint32_t)opportunities.size()); } assert(granularity_ > 0); if (index_ >= opportunities.size()) { + // We have reached the end of the available opportunities and, therefore, + // the end of the round for this pass, so reset the index and decrease the + // granularity for the next round. Return an empty vector to signal the end + // of the round. index_ = 0; granularity_ = std::max((uint32_t)1, granularity_ / 2); return std::vector(); @@ -61,8 +60,6 @@ std::vector ReductionPass::TryApplyReduction( opportunities[i]->TryToApply(); } - index_ += granularity_; - std::vector result; context->module()->ToBinary(&result, false); return result; @@ -73,16 +70,17 @@ void ReductionPass::SetMessageConsumer(MessageConsumer consumer) { } bool ReductionPass::ReachedMinimumGranularity() const { - if (!is_initialized_) { - // Conceptually we can think that if the pass has not yet been initialized, - // it is operating at unbounded granularity. - return false; - } assert(granularity_ != 0); return granularity_ == 1; } std::string ReductionPass::GetName() const { return finder_->GetName(); } +void ReductionPass::NotifyInteresting(bool interesting) { + if (!interesting) { + index_ += granularity_; + } +} + } // namespace reduce } // namespace spvtools diff --git a/3rdparty/spirv-tools/source/reduce/reduction_pass.h b/3rdparty/spirv-tools/source/reduce/reduction_pass.h index 46cd19244..f2d937ba6 100644 --- a/3rdparty/spirv-tools/source/reduce/reduction_pass.h +++ b/3rdparty/spirv-tools/source/reduce/reduction_pass.h @@ -15,10 +15,11 @@ #ifndef SOURCE_REDUCE_REDUCTION_PASS_H_ #define SOURCE_REDUCE_REDUCTION_PASS_H_ -#include "spirv-tools/libspirv.hpp" +#include -#include "reduction_opportunity_finder.h" #include "source/opt/ir_context.h" +#include "source/reduce/reduction_opportunity_finder.h" +#include "spirv-tools/libspirv.hpp" namespace spvtools { namespace reduce { @@ -33,17 +34,28 @@ namespace reduce { class ReductionPass { public: // Constructs a reduction pass with a given target environment, |target_env|, - // and a given finder of reduction opportunities, |finder|. Initially the - // pass is uninitialized. + // and a given finder of reduction opportunities, |finder|. explicit ReductionPass(const spv_target_env target_env, std::unique_ptr finder) : target_env_(target_env), finder_(std::move(finder)), - is_initialized_(false) {} + index_(0), + granularity_(std::numeric_limits::max()) {} - // Applies the reduction pass to the given binary. + // Applies the reduction pass to the given binary by applying a "chunk" of + // reduction opportunities. Returns the new binary if a chunk was applied; in + // this case, before the next call the caller must invoke + // NotifyInteresting(...) to indicate whether the new binary is interesting. + // Returns an empty vector if there are no more chunks left to apply; in this + // case, the index will be reset and the granularity lowered for the next + // round. std::vector TryApplyReduction(const std::vector& binary); + // Notifies the reduction pass whether the binary returned from + // TryApplyReduction is interesting, so that the next call to + // TryApplyReduction will avoid applying the same chunk of opportunities. + void NotifyInteresting(bool interesting); + // Sets a consumer to which relevant messages will be directed. void SetMessageConsumer(MessageConsumer consumer); @@ -59,7 +71,6 @@ class ReductionPass { const spv_target_env target_env_; const std::unique_ptr finder_; MessageConsumer consumer_; - bool is_initialized_; uint32_t index_; uint32_t granularity_; }; diff --git a/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp b/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp index bf0e085ad..8e258376e 100644 --- a/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp +++ b/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp @@ -23,7 +23,6 @@ namespace reduce { namespace { const uint32_t kMergeNodeIndex = 0; -const uint32_t kContinueNodeIndex = 1; } // namespace bool StructuredLoopToSelectionReductionOpportunity::PreconditionHolds() { @@ -43,15 +42,11 @@ void StructuredLoopToSelectionReductionOpportunity::Apply() { // (1) Redirect edges that point to the loop's continue target to their // closest merge block. - RedirectToClosestMergeBlock( - loop_construct_header_->GetLoopMergeInst()->GetSingleWordOperand( - kContinueNodeIndex)); + RedirectToClosestMergeBlock(loop_construct_header_->ContinueBlockId()); // (2) Redirect edges that point to the loop's merge block to their closest // merge block (which might be that of an enclosing selection, for instance). - RedirectToClosestMergeBlock( - loop_construct_header_->GetLoopMergeInst()->GetSingleWordOperand( - kMergeNodeIndex)); + RedirectToClosestMergeBlock(loop_construct_header_->MergeBlockId()); // (3) Turn the loop construct header into a selection. ChangeLoopToSelection(); @@ -127,12 +122,8 @@ void StructuredLoopToSelectionReductionOpportunity::RedirectEdge( // original_target_id must either be the merge target or continue construct // for the loop being operated on. - assert(original_target_id == - loop_construct_header_->GetMergeInst()->GetSingleWordOperand( - kMergeNodeIndex) || - original_target_id == - loop_construct_header_->GetMergeInst()->GetSingleWordOperand( - kContinueNodeIndex)); + assert(original_target_id == loop_construct_header_->MergeBlockId() || + original_target_id == loop_construct_header_->ContinueBlockId()); auto terminator = context_->cfg()->block(source_id)->terminator(); @@ -221,7 +212,7 @@ void StructuredLoopToSelectionReductionOpportunity::ChangeLoopToSelection() { const analysis::Bool* bool_type = context_->get_type_mgr()->GetRegisteredType(&temp)->AsBool(); auto const_mgr = context_->get_constant_mgr(); - auto true_const = const_mgr->GetConstant(bool_type, {true}); + auto true_const = const_mgr->GetConstant(bool_type, {1}); auto true_const_result_id = const_mgr->GetDefiningInstruction(true_const)->result_id(); auto original_branch_id = terminator->GetSingleWordOperand(0); @@ -250,6 +241,10 @@ void StructuredLoopToSelectionReductionOpportunity::FixNonDominatedIdUses() { context_->get_def_use_mgr()->ForEachUse(&def, [this, &block, &def]( Instruction* use, uint32_t index) { + // Ignore uses outside of blocks, such as in OpDecorate. + if (context_->get_instr_block(use) == nullptr) { + return; + } // If a use is not appropriately dominated by its definition, // replace the use with an OpUndef, unless the definition is an // access chain, in which case replace it with some (possibly fresh) diff --git a/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp b/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp index 7d150fbe8..6d835fc01 100644 --- a/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp +++ b/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp @@ -33,10 +33,9 @@ StructuredLoopToSelectionReductionOpportunityFinder::GetAvailableOpportunities( std::set merge_block_ids; for (auto& function : *context->module()) { for (auto& block : function) { - auto merge_inst = block.GetMergeInst(); - if (merge_inst) { - merge_block_ids.insert( - merge_inst->GetSingleWordOperand(kMergeNodeIndex)); + auto merge_block_id = block.MergeBlockIdIfAny(); + if (merge_block_id) { + merge_block_ids.insert(merge_block_id); } } } @@ -50,11 +49,19 @@ StructuredLoopToSelectionReductionOpportunityFinder::GetAvailableOpportunities( continue; } + uint32_t continue_block_id = + loop_merge_inst->GetSingleWordOperand(kContinueNodeIndex); + // Check whether the loop construct's continue target is the merge block // of some structured control flow construct. If it is, we cautiously do // not consider applying a transformation. - if (merge_block_ids.find(loop_merge_inst->GetSingleWordOperand( - kContinueNodeIndex)) != merge_block_ids.end()) { + if (merge_block_ids.find(continue_block_id) != merge_block_ids.end()) { + continue; + } + + // Check whether the loop header block is also the continue target. If it + // is, we cautiously do not consider applying a transformation. + if (block.id() == continue_block_id) { continue; } diff --git a/3rdparty/spirv-tools/source/spirv_reducer_options.cpp b/3rdparty/spirv-tools/source/spirv_reducer_options.cpp index 110ea3e09..0426800e1 100644 --- a/3rdparty/spirv-tools/source/spirv_reducer_options.cpp +++ b/3rdparty/spirv-tools/source/spirv_reducer_options.cpp @@ -29,3 +29,8 @@ SPIRV_TOOLS_EXPORT void spvReducerOptionsSetStepLimit( spv_reducer_options options, uint32_t step_limit) { options->step_limit = step_limit; } + +SPIRV_TOOLS_EXPORT void spvReducerOptionsSetFailOnValidationError( + spv_reducer_options options, bool fail_on_validation_error) { + options->fail_on_validation_error = fail_on_validation_error; +} diff --git a/3rdparty/spirv-tools/source/spirv_reducer_options.h b/3rdparty/spirv-tools/source/spirv_reducer_options.h index d48303ca6..363517bb5 100644 --- a/3rdparty/spirv-tools/source/spirv_reducer_options.h +++ b/3rdparty/spirv-tools/source/spirv_reducer_options.h @@ -26,10 +26,14 @@ const uint32_t kDefaultStepLimit = 250; // Manages command line options passed to the SPIR-V Reducer. New struct // members may be added for any new option. struct spv_reducer_options_t { - spv_reducer_options_t() : step_limit(kDefaultStepLimit) {} + spv_reducer_options_t() + : step_limit(kDefaultStepLimit), fail_on_validation_error(false) {} - // The number of steps the reducer will run for before giving up. + // See spvReducerOptionsSetStepLimit. uint32_t step_limit; + + // See spvReducerOptionsSetFailOnValidationError. + bool fail_on_validation_error; }; #endif // SOURCE_SPIRV_REDUCER_OPTIONS_H_ diff --git a/3rdparty/spirv-tools/source/val/validate_cfg.cpp b/3rdparty/spirv-tools/source/val/validate_cfg.cpp index 6e7acbd6a..1d6f920d1 100644 --- a/3rdparty/spirv-tools/source/val/validate_cfg.cpp +++ b/3rdparty/spirv-tools/source/val/validate_cfg.cpp @@ -725,6 +725,36 @@ spv_result_t PerformWebGPUCfgChecks(ValidationState_t& _, Function* function) { return SPV_SUCCESS; } +// Checks that there are no OpUnreachable instructions reachable from the entry +// block of |function|. +spv_result_t ValidateUnreachableBlocks(ValidationState_t& _, + Function& function) { + auto* entry_block = function.first_block(); + std::vector stack; + std::unordered_set seen; + if (entry_block) stack.push_back(entry_block); + + while (!stack.empty()) { + auto* block = stack.back(); + stack.pop_back(); + + if (!seen.insert(block).second) continue; + + auto* terminator = block->terminator(); + if (terminator->opcode() == SpvOpUnreachable) { + return _.diag(SPV_ERROR_INVALID_CFG, block->label()) + << "Statically reachable blocks cannot be terminated by " + "OpUnreachable"; + } + + for (auto* succ : *block->successors()) { + stack.push_back(succ); + } + } + + return SPV_SUCCESS; +} + spv_result_t PerformCfgChecks(ValidationState_t& _) { for (auto& function : _.functions()) { // Check all referenced blocks are defined within a function @@ -827,6 +857,8 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) { } } + if (auto error = ValidateUnreachableBlocks(_, function)) return error; + /// Structured control flow checks are only required for shader capabilities if (_.HasCapability(SpvCapabilityShader)) { if (auto error = StructuredControlFlowChecks(_, &function, back_edges)) diff --git a/3rdparty/spirv-tools/source/val/validate_image.cpp b/3rdparty/spirv-tools/source/val/validate_image.cpp index 72f9c51f8..b99f4cff5 100644 --- a/3rdparty/spirv-tools/source/val/validate_image.cpp +++ b/3rdparty/spirv-tools/source/val/validate_image.cpp @@ -748,6 +748,33 @@ spv_result_t ValidateTypeSampledImage(ValidationState_t& _, return SPV_SUCCESS; } +bool IsAllowedSampledImageOperand(SpvOp opcode) { + switch (opcode) { + case SpvOpSampledImage: + case SpvOpImageSampleImplicitLod: + case SpvOpImageSampleExplicitLod: + case SpvOpImageSampleDrefImplicitLod: + case SpvOpImageSampleDrefExplicitLod: + case SpvOpImageSampleProjImplicitLod: + case SpvOpImageSampleProjExplicitLod: + case SpvOpImageSampleProjDrefImplicitLod: + case SpvOpImageSampleProjDrefExplicitLod: + case SpvOpImageGather: + case SpvOpImageDrefGather: + case SpvOpImage: + case SpvOpImageQueryLod: + case SpvOpImageSparseSampleImplicitLod: + case SpvOpImageSparseSampleExplicitLod: + case SpvOpImageSparseSampleDrefImplicitLod: + case SpvOpImageSparseSampleDrefExplicitLod: + case SpvOpImageSparseGather: + case SpvOpImageSparseDrefGather: + return true; + default: + return false; + } +} + spv_result_t ValidateSampledImage(ValidationState_t& _, const Instruction* inst) { if (_.GetIdOpcode(inst->type_id()) != SpvOpTypeSampledImage) { @@ -815,11 +842,7 @@ spv_result_t ValidateSampledImage(ValidationState_t& _, "block. The consumer instruction is '" << _.getIdName(consumer_instr->id()) << "'."; } - // TODO: The following check is incomplete. We should also check that the - // Sampled Image is not used by instructions that should not take - // SampledImage as an argument. We could find the list of valid - // instructions by scanning for "Sampled Image" in the operand description - // field in the grammar file. + if (consumer_opcode == SpvOpPhi || consumer_opcode == SpvOpSelect) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Result from OpSampledImage instruction must not appear " @@ -830,6 +853,18 @@ spv_result_t ValidateSampledImage(ValidationState_t& _, << "' as an operand of '" << _.getIdName(consumer_instr->id()) << "'."; } + + if (!IsAllowedSampledImageOperand(consumer_opcode)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result from OpSampledImage instruction must not appear " + "as operand for Op" + << spvOpcodeString(static_cast(consumer_opcode)) + << ", since it is not specificed as taking an " + << "OpTypeSampledImage." + << " Found result '" << _.getIdName(inst->id()) + << "' as an operand of '" + << _.getIdName(consumer_instr->id()) << "'."; + } } } return SPV_SUCCESS; diff --git a/3rdparty/spirv-tools/test/opt/block_merge_test.cpp b/3rdparty/spirv-tools/test/opt/block_merge_test.cpp index b0f914a71..f87fd8048 100644 --- a/3rdparty/spirv-tools/test/opt/block_merge_test.cpp +++ b/3rdparty/spirv-tools/test/opt/block_merge_test.cpp @@ -590,7 +590,7 @@ OpUnreachable OpFunctionEnd )"; - SinglePassRunAndMatch(text, true); + SinglePassRunAndMatch(text, false); } TEST_F(BlockMergeTest, DontMergeReturn) { diff --git a/3rdparty/spirv-tools/test/opt/inline_test.cpp b/3rdparty/spirv-tools/test/opt/inline_test.cpp index 44a969858..717081210 100644 --- a/3rdparty/spirv-tools/test/opt/inline_test.cpp +++ b/3rdparty/spirv-tools/test/opt/inline_test.cpp @@ -2421,7 +2421,8 @@ OpBranchConditional %true %44 %43 OpStore %38 %float_1 OpBranch %40 %43 = OpLabel -OpUnreachable +OpStore %38 %float_1 +OpBranch %40 %41 = OpLabel OpBranchConditional %false %39 %40 %40 = OpLabel @@ -2446,7 +2447,7 @@ OpBranchConditional %true %36 %35 %36 = OpLabel OpReturnValue %float_1 %35 = OpLabel -OpUnreachable +OpReturnValue %float_1 OpFunctionEnd )"; @@ -3058,6 +3059,7 @@ OpDecorate %2 DescriptorSet 439418829 %4 = OpTypeFunction %void %float = OpTypeFloat 32 %_struct_6 = OpTypeStruct %float %float +%15 = OpConstantNull %_struct_6 %7 = OpTypeFunction %_struct_6 %1 = OpFunction %void Pure|Const %4 %8 = OpLabel @@ -3067,10 +3069,11 @@ OpFunctionEnd %9 = OpFunction %_struct_6 None %7 %10 = OpLabel %11 = OpFunctionCall %_struct_6 %9 -OpUnreachable +OpReturnValue %15 OpFunctionEnd )"; + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck(test, test, false, true); } @@ -3086,6 +3089,7 @@ OpDecorate %2 DescriptorSet 439418829 %4 = OpTypeFunction %void %float = OpTypeFloat 32 %_struct_6 = OpTypeStruct %float %float +%15 = OpConstantNull %_struct_6 %7 = OpTypeFunction %_struct_6 %1 = OpFunction %void Pure|Const %4 %8 = OpLabel @@ -3095,15 +3099,16 @@ OpFunctionEnd %9 = OpFunction %_struct_6 None %7 %10 = OpLabel %11 = OpFunctionCall %_struct_6 %12 -OpUnreachable +OpReturnValue %15 OpFunctionEnd %12 = OpFunction %_struct_6 None %7 %13 = OpLabel %14 = OpFunctionCall %_struct_6 %9 -OpUnreachable +OpReturnValue %15 OpFunctionEnd )"; + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); SinglePassRunAndCheck(test, test, false, true); } diff --git a/3rdparty/spirv-tools/test/opt/struct_cfg_analysis_test.cpp b/3rdparty/spirv-tools/test/opt/struct_cfg_analysis_test.cpp index 13f9022d0..3a980fe8f 100644 --- a/3rdparty/spirv-tools/test/opt/struct_cfg_analysis_test.cpp +++ b/3rdparty/spirv-tools/test/opt/struct_cfg_analysis_test.cpp @@ -461,6 +461,26 @@ OpFunctionEnd } } +TEST_F(StructCFGAnalysisTest, EmptyFunctionTest) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "x" Import +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + // #2451: This segfaulted on empty functions. + StructuredCFGAnalysis analysis(context.get()); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/reduce/reduce_test_util.cpp b/3rdparty/spirv-tools/test/reduce/reduce_test_util.cpp index 19ef74989..96fb4ac59 100644 --- a/3rdparty/spirv-tools/test/reduce/reduce_test_util.cpp +++ b/3rdparty/spirv-tools/test/reduce/reduce_test_util.cpp @@ -14,6 +14,8 @@ #include "reduce_test_util.h" +#include + namespace spvtools { namespace reduce { @@ -68,5 +70,27 @@ void NopDiagnostic(spv_message_level_t /*level*/, const char* /*source*/, const spv_position_t& /*position*/, const char* /*message*/) {} +void CLIMessageConsumer(spv_message_level_t level, const char*, + const spv_position_t& position, const char* message) { + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + std::cerr << "error: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_WARNING: + std::cout << "warning: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_INFO: + std::cout << "info: line " << position.index << ": " << message + << std::endl; + break; + default: + break; + } +} + } // namespace reduce } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/reduce/reduce_test_util.h b/3rdparty/spirv-tools/test/reduce/reduce_test_util.h index 080edad10..387d99597 100644 --- a/3rdparty/spirv-tools/test/reduce/reduce_test_util.h +++ b/3rdparty/spirv-tools/test/reduce/reduce_test_util.h @@ -59,6 +59,10 @@ const uint32_t kReduceDisassembleOption = void NopDiagnostic(spv_message_level_t /*level*/, const char* /*source*/, const spv_position_t& /*position*/, const char* /*message*/); +// Prints reducer messages (for debugging). +void CLIMessageConsumer(spv_message_level_t level, const char*, + const spv_position_t& position, const char* message); + } // namespace reduce } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/reduce/reducer_test.cpp b/3rdparty/spirv-tools/test/reduce/reducer_test.cpp index 73effc35a..cb169f882 100644 --- a/3rdparty/spirv-tools/test/reduce/reducer_test.cpp +++ b/3rdparty/spirv-tools/test/reduce/reducer_test.cpp @@ -229,6 +229,7 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) { std::vector binary_out; spvtools::ReducerOptions reducer_options; reducer_options.set_step_limit(500); + reducer_options.set_fail_on_validation_error(true); spvtools::ValidatorOptions validator_options; Reducer::ReductionResultStatus status = reducer.Run( @@ -304,6 +305,7 @@ TEST(ReducerTest, RemoveOpnameAndRemoveUnreferenced) { std::vector binary_out; spvtools::ReducerOptions reducer_options; reducer_options.set_step_limit(500); + reducer_options.set_fail_on_validation_error(true); spvtools::ValidatorOptions validator_options; Reducer::ReductionResultStatus status = reducer.Run( diff --git a/3rdparty/spirv-tools/test/reduce/remove_unreferenced_instruction_test.cpp b/3rdparty/spirv-tools/test/reduce/remove_unreferenced_instruction_test.cpp index 76d7ddd09..12cd3e2cf 100644 --- a/3rdparty/spirv-tools/test/reduce/remove_unreferenced_instruction_test.cpp +++ b/3rdparty/spirv-tools/test/reduce/remove_unreferenced_instruction_test.cpp @@ -65,12 +65,16 @@ TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) { OpStore %14 %15 )" + epilogue; - const std::string expected = prologue + R"( + const std::string expected_after_2 = prologue + R"( OpStore %12 %13 %15 = OpLoad %6 %8 OpStore %14 %15 )" + epilogue; + const std::string expected_after_4 = prologue + R"( + %15 = OpLoad %6 %8 + )" + epilogue; + const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; const auto context = @@ -83,149 +87,14 @@ TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) { ASSERT_TRUE(ops[1]->PreconditionHolds()); ops[1]->TryToApply(); - CheckEqual(env, expected, context.get()); -} + CheckEqual(env, expected_after_2, context.get()); -TEST(RemoveUnreferencedInstructionReductionPassTest, ApplyReduction) { - const std::string prologue = 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 %8 "a" - OpName %10 "b" - OpName %12 "c" - OpName %14 "d" - %2 = OpTypeVoid - %3 = OpTypeFunction %2 - %6 = OpTypeInt 32 1 - %7 = OpTypePointer Function %6 - %9 = OpConstant %6 10 - %11 = OpConstant %6 20 - %13 = OpConstant %6 30 - %4 = OpFunction %2 None %3 - %5 = OpLabel - %8 = OpVariable %7 Function - %10 = OpVariable %7 Function - %12 = OpVariable %7 Function - %14 = OpVariable %7 Function - )"; + ASSERT_TRUE(ops[2]->PreconditionHolds()); + ops[2]->TryToApply(); + ASSERT_TRUE(ops[3]->PreconditionHolds()); + ops[3]->TryToApply(); - const std::string epilogue = R"( - OpReturn - OpFunctionEnd - )"; - - const std::string original = prologue + R"( - OpStore %8 %9 - OpStore %10 %11 - OpStore %12 %13 - %15 = OpLoad %6 %8 - OpStore %14 %15 - )" + epilogue; - - const auto env = SPV_ENV_UNIVERSAL_1_3; - - std::vector binary; - SpirvTools t(env); - ASSERT_TRUE(t.Assemble(original, &binary, kReduceAssembleOption)); - - ReductionPass pass( - env, spvtools::MakeUnique< - RemoveUnreferencedInstructionReductionOpportunityFinder>()); - - { - // Attempt 1 should remove everything removable. - const std::string expected_reduced = prologue + R"( - %15 = OpLoad %6 %8 - )" + epilogue; - auto reduced_binary = pass.TryApplyReduction(binary); - CheckEqual(env, expected_reduced, reduced_binary); - } - - // Attempt 2 should fail as pass with granularity 4 got to end. - ASSERT_EQ(0, pass.TryApplyReduction(binary).size()); - - { - // Attempt 3 should remove first two removable statements. - const std::string expected_reduced = prologue + R"( - OpStore %12 %13 - %15 = OpLoad %6 %8 - OpStore %14 %15 - )" + epilogue; - auto reduced_binary = pass.TryApplyReduction(binary); - CheckEqual(env, expected_reduced, reduced_binary); - } - - { - // Attempt 4 should remove last two removable statements. - const std::string expected_reduced = prologue + R"( - OpStore %8 %9 - OpStore %10 %11 - %15 = OpLoad %6 %8 - )" + epilogue; - auto reduced_binary = pass.TryApplyReduction(binary); - CheckEqual(env, expected_reduced, reduced_binary); - } - - // Attempt 5 should fail as pass with granularity 2 got to end. - ASSERT_EQ(0, pass.TryApplyReduction(binary).size()); - - { - // Attempt 6 should remove first removable statement. - const std::string expected_reduced = prologue + R"( - OpStore %10 %11 - OpStore %12 %13 - %15 = OpLoad %6 %8 - OpStore %14 %15 - )" + epilogue; - auto reduced_binary = pass.TryApplyReduction(binary); - CheckEqual(env, expected_reduced, reduced_binary); - } - - { - // Attempt 7 should remove second removable statement. - const std::string expected_reduced = prologue + R"( - OpStore %8 %9 - OpStore %12 %13 - %15 = OpLoad %6 %8 - OpStore %14 %15 - )" + epilogue; - auto reduced_binary = pass.TryApplyReduction(binary); - CheckEqual(env, expected_reduced, reduced_binary); - } - - { - // Attempt 8 should remove third removable statement. - const std::string expected_reduced = prologue + R"( - OpStore %8 %9 - OpStore %10 %11 - %15 = OpLoad %6 %8 - OpStore %14 %15 - )" + epilogue; - auto reduced_binary = pass.TryApplyReduction(binary); - CheckEqual(env, expected_reduced, reduced_binary); - } - - { - // Attempt 9 should remove fourth removable statement. - const std::string expected_reduced = prologue + R"( - OpStore %8 %9 - OpStore %10 %11 - OpStore %12 %13 - %15 = OpLoad %6 %8 - )" + epilogue; - auto reduced_binary = pass.TryApplyReduction(binary); - CheckEqual(env, expected_reduced, reduced_binary); - } - - // Attempt 10 should fail as pass with granularity 1 got to end. - ASSERT_EQ(0, pass.TryApplyReduction(binary).size()); - - ASSERT_TRUE(pass.ReachedMinimumGranularity()); + CheckEqual(env, expected_after_4, context.get()); } } // namespace diff --git a/3rdparty/spirv-tools/test/reduce/structured_loop_to_selection_test.cpp b/3rdparty/spirv-tools/test/reduce/structured_loop_to_selection_test.cpp index ae55d55cc..d4f645999 100644 --- a/3rdparty/spirv-tools/test/reduce/structured_loop_to_selection_test.cpp +++ b/3rdparty/spirv-tools/test/reduce/structured_loop_to_selection_test.cpp @@ -3436,6 +3436,191 @@ TEST(StructuredLoopToSelectionReductionPassTest, LongAccessChains) { // CheckEqual(env, expected, context.get()); } +TEST(StructuredLoopToSelectionReductionPassTest, LoopyShaderWithOpDecorate) { + // A shader containing a function that contains a loop and some definitions + // that are "used" in OpDecorate instructions (outside the function). These + // "uses" were causing segfaults because we try to calculate their dominance + // information, which doesn't make sense. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "_GLF_color" + OpName %14 "buf0" + OpMemberName %14 0 "a" + OpName %16 "" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpMemberDecorate %14 0 RelaxedPrecision + OpMemberDecorate %14 0 Offset 0 + OpDecorate %14 Block + OpDecorate %16 DescriptorSet 0 + OpDecorate %16 Binding 0 + OpDecorate %21 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpConstant %6 1 + %11 = OpConstantComposite %7 %10 %10 %10 %10 + %14 = OpTypeStruct %6 + %15 = OpTypePointer Uniform %14 + %16 = OpVariable %15 Uniform + %17 = OpTypeInt 32 1 + %18 = OpConstant %17 0 + %19 = OpTypePointer Uniform %6 + %28 = OpConstant %6 2 + %29 = OpTypeBool + %31 = OpTypeInt 32 0 + %32 = OpConstant %31 0 + %33 = OpTypePointer Output %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpStore %9 %11 + %20 = OpAccessChain %19 %16 %18 + %21 = OpLoad %6 %20 + OpBranch %22 + %22 = OpLabel + %40 = OpPhi %6 %21 %5 %39 %23 + %30 = OpFOrdLessThan %29 %40 %28 + OpLoopMerge %24 %23 None + OpBranchConditional %30 %23 %24 + %23 = OpLabel + %34 = OpAccessChain %33 %9 %32 + %35 = OpLoad %6 %34 + %36 = OpFAdd %6 %35 %10 + OpStore %34 %36 + %39 = OpFAdd %6 %40 %10 + OpBranch %22 + %24 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get()); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "_GLF_color" + OpName %14 "buf0" + OpMemberName %14 0 "a" + OpName %16 "" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpMemberDecorate %14 0 RelaxedPrecision + OpMemberDecorate %14 0 Offset 0 + OpDecorate %14 Block + OpDecorate %16 DescriptorSet 0 + OpDecorate %16 Binding 0 + OpDecorate %21 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpConstant %6 1 + %11 = OpConstantComposite %7 %10 %10 %10 %10 + %14 = OpTypeStruct %6 + %15 = OpTypePointer Uniform %14 + %16 = OpVariable %15 Uniform + %17 = OpTypeInt 32 1 + %18 = OpConstant %17 0 + %19 = OpTypePointer Uniform %6 + %28 = OpConstant %6 2 + %29 = OpTypeBool + %31 = OpTypeInt 32 0 + %32 = OpConstant %31 0 + %33 = OpTypePointer Output %6 + %41 = OpUndef %6 ; Added + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpStore %9 %11 + %20 = OpAccessChain %19 %16 %18 + %21 = OpLoad %6 %20 + OpBranch %22 + %22 = OpLabel + %40 = OpPhi %6 %21 %5 %41 %23 ; Changed + %30 = OpFOrdLessThan %29 %40 %28 + OpSelectionMerge %24 None ; Changed + OpBranchConditional %30 %24 %24 + %23 = OpLabel + %34 = OpAccessChain %33 %9 %32 + %35 = OpLoad %6 %34 + %36 = OpFAdd %6 %35 %10 + OpStore %34 %36 + %39 = OpFAdd %6 %41 %10 ; Changed + OpBranch %22 + %24 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_0, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, + LoopWithCombinedHeaderAndContinue) { + // A shader containing a loop where the header is also the continue target. + // For now, we don't simplify such loops. + + 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 + %6 = OpTypeBool + %30 = OpConstantFalse %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel ; loop header and continue target + OpLoopMerge %12 %10 None + OpBranchConditional %30 %10 %12 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get()); + ASSERT_EQ(0, ops.size()); +} + } // namespace } // namespace reduce } // namespace spvtools diff --git a/3rdparty/spirv-tools/test/reduce/validation_during_reduction_test.cpp b/3rdparty/spirv-tools/test/reduce/validation_during_reduction_test.cpp index 4fc577159..863795bcc 100644 --- a/3rdparty/spirv-tools/test/reduce/validation_during_reduction_test.cpp +++ b/3rdparty/spirv-tools/test/reduce/validation_during_reduction_test.cpp @@ -113,7 +113,9 @@ class OpVariableDuplicatorReductionOpportunityFinder TEST(ValidationDuringReductionTest, CheckInvalidPassMakesNoProgress) { // A module whose global values are all referenced, so that any application of - // MakeModuleInvalidPass will make the module invalid. + // MakeModuleInvalidPass will make the module invalid. Check that the reducer + // makes no progress, as every step will be invalid and treated as + // uninteresting. std::string original = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -219,6 +221,8 @@ TEST(ValidationDuringReductionTest, CheckInvalidPassMakesNoProgress) { std::vector binary_out; spvtools::ReducerOptions reducer_options; reducer_options.set_step_limit(500); + // Don't fail on a validation error; just treat it as uninteresting. + reducer_options.set_fail_on_validation_error(false); spvtools::ValidatorOptions validator_options; Reducer::ReductionResultStatus status = reducer.Run( @@ -428,6 +432,8 @@ TEST(ValidationDuringReductionTest, CheckNotAlwaysInvalidCanMakeProgress) { std::vector binary_out; spvtools::ReducerOptions reducer_options; reducer_options.set_step_limit(500); + // Don't fail on a validation error; just treat it as uninteresting. + reducer_options.set_fail_on_validation_error(false); spvtools::ValidatorOptions validator_options; Reducer::ReductionResultStatus status = reducer.Run( @@ -518,6 +524,7 @@ TEST(ValidationDuringReductionTest, CheckValidationOptions) { spvtools::ValidatorOptions validator_options; reducer_options.set_step_limit(3); + reducer_options.set_fail_on_validation_error(true); // Reduction should fail because the initial state is invalid without the // "skip-block-layout" validator option. Note that the interestingness test @@ -553,8 +560,9 @@ TEST(ValidationDuringReductionTest, CheckValidationOptions) { validator_options.SetUniversalLimit(spv_validator_limit_max_local_variables, 2); - // Reduction should "complete"; after one step, a local variable is added and - // the module becomes "invalid" given the validator limits. + // Reduction should now fail due to reaching an invalid state; after one step, + // a local variable is added and the module becomes "invalid" given the + // validator limits. { Reducer reducer(env); setupReducerForCheckValidationOptions(&reducer); @@ -563,7 +571,7 @@ TEST(ValidationDuringReductionTest, CheckValidationOptions) { reducer.Run(std::vector(binary_in), &binary_out, reducer_options, validator_options); - ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete); + ASSERT_EQ(status, Reducer::ReductionResultStatus::kStateInvalid); } } diff --git a/3rdparty/spirv-tools/test/val/val_cfg_test.cpp b/3rdparty/spirv-tools/test/val/val_cfg_test.cpp index a0ee79ace..7e7dae8eb 100644 --- a/3rdparty/spirv-tools/test/val/val_cfg_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_cfg_test.cpp @@ -1203,7 +1203,11 @@ std::string GetUnreachableMergeWithBranchUse(SpvCapability cap, TEST_P(ValidateCFG, UnreachableMergeWithBranchUse) { CompileSuccessfully( GetUnreachableMergeWithBranchUse(GetParam(), SPV_ENV_UNIVERSAL_1_0)); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Statically reachable blocks cannot be terminated by OpUnreachable")); } TEST_F(ValidateCFG, WebGPUUnreachableMergeWithBranchUse) { @@ -1938,7 +1942,10 @@ OpDecorate %id BuiltIn GlobalInvocationId str += "OpFunctionEnd"; CompileSuccessfully(str); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Statically reachable blocks cannot be terminated by " + "OpUnreachable")); } TEST_F(ValidateCFG, LoopWithZeroBackEdgesBad) { @@ -2852,6 +2859,134 @@ TEST_F(ValidateCFG, BranchTargetMustBeLabel) { "be the ID of an OpLabel instruction")); } +TEST_F(ValidateCFG, ReachableOpUnreachableOneBlock) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Statically reachable blocks cannot be terminated by OpUnreachable")); +} + +TEST_F(ValidateCFG, ReachableOpUnreachableOpBranch) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %block +%block = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Statically reachable blocks cannot be terminated by OpUnreachable")); +} + +TEST_F(ValidateCFG, ReachableOpUnreachableOpBranchConditional) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranchConditional %undef %block %unreachable +%block = OpLabel +OpReturn +%unreachable = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Statically reachable blocks cannot be terminated by OpUnreachable")); +} + +TEST_F(ValidateCFG, ReachableOpUnreachableOpSwitch) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%undef = OpUndef %int +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSwitch %undef %block1 0 %unreachable 1 %block2 +%block1 = OpLabel +OpReturn +%unreachable = OpLabel +OpUnreachable +%block2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Statically reachable blocks cannot be terminated by OpUnreachable")); +} + +TEST_F(ValidateCFG, ReachableOpUnreachableLoop) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %unreachable %loop None +OpBranchConditional %undef %loop %unreachable +%unreachable = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Statically reachable blocks cannot be terminated by OpUnreachable")); +} + TEST_F(ValidateCFG, OneContinueTwoBackedges) { const std::string text = R"( OpCapability Shader diff --git a/3rdparty/spirv-tools/test/val/val_id_test.cpp b/3rdparty/spirv-tools/test/val/val_id_test.cpp index 266aaf3ef..73e2ce114 100644 --- a/3rdparty/spirv-tools/test/val/val_id_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_id_test.cpp @@ -6203,20 +6203,22 @@ TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock1) { %4 = OpTypeFunction %3 %5 = OpFunction %1 None %2 %6 = OpLabel -%7 = OpFunctionCall %3 %8 +OpReturn +%7 = OpLabel +%8 = OpFunctionCall %3 %9 OpUnreachable OpFunctionEnd -%8 = OpFunction %3 None %4 -%9 = OpLabel -OpReturnValue %7 +%9 = OpFunction %3 None %4 +%10 = OpLabel +OpReturnValue %8 OpFunctionEnd )"; CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("ID 7[%7] defined in block 6[%6] does not dominate its " - "use in block 9[%9]\n %9 = OpLabel")); + HasSubstr("ID 8[%8] defined in block 7[%7] does not dominate its " + "use in block 10[%10]\n %10 = OpLabel")); } TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock2) { diff --git a/3rdparty/spirv-tools/test/val/val_image_test.cpp b/3rdparty/spirv-tools/test/val/val_image_test.cpp index 2d6fe29b4..a90f17a40 100644 --- a/3rdparty/spirv-tools/test/val/val_image_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_image_test.cpp @@ -2283,7 +2283,7 @@ TEST_F(ValidateImage, FetchNotImage) { %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 %sampler = OpLoad %type_sampler %uniform_sampler %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler -%res1 = OpImageFetch %f32vec4 %simg %u32vec2_01 +%res1 = OpImageFetch %f32vec4 %sampler %u32vec2_01 )"; CompileSuccessfully(GenerateShaderCode(body).c_str()); @@ -2292,6 +2292,21 @@ TEST_F(ValidateImage, FetchNotImage) { HasSubstr("Expected Image to be of type OpTypeImage")); } +TEST_F(ValidateImage, FetchSampledImageDirectly) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageFetch %f32vec4 %simg %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSampledImage instruction must not appear as operand " + "for OpImageFetch")); +} + TEST_F(ValidateImage, FetchNotSampled) { const std::string body = R"( %img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 @@ -3191,7 +3206,7 @@ TEST_F(ValidateImage, QueryFormatNotImage) { %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 %sampler = OpLoad %type_sampler %uniform_sampler %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler -%res1 = OpImageQueryFormat %u32 %simg +%res1 = OpImageQueryFormat %u32 %sampler )"; CompileSuccessfully(GenerateKernelCode(body).c_str()); @@ -3227,7 +3242,7 @@ TEST_F(ValidateImage, QueryOrderNotImage) { %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 %sampler = OpLoad %type_sampler %uniform_sampler %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler -%res1 = OpImageQueryOrder %u32 %simg +%res1 = OpImageQueryOrder %u32 %sampler )"; CompileSuccessfully(GenerateKernelCode(body).c_str()); @@ -3276,7 +3291,7 @@ TEST_F(ValidateImage, QuerySizeLodNotImage) { %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 %sampler = OpLoad %type_sampler %uniform_sampler %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler -%res1 = OpImageQuerySizeLod %u32vec2 %simg %u32_1 +%res1 = OpImageQuerySizeLod %u32vec2 %sampler %u32_1 )"; CompileSuccessfully(GenerateKernelCode(body).c_str()); @@ -3285,6 +3300,21 @@ TEST_F(ValidateImage, QuerySizeLodNotImage) { HasSubstr("Expected Image to be of type OpTypeImage")); } +TEST_F(ValidateImage, QuerySizeLodSampledImageDirectly) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQuerySizeLod %u32vec2 %simg %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSampledImage instruction must not appear as operand " + "for OpImageQuerySizeLod")); +} + TEST_F(ValidateImage, QuerySizeLodWrongImageDim) { const std::string body = R"( %img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 @@ -3348,7 +3378,7 @@ TEST_F(ValidateImage, QuerySizeNotImage) { %img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 %sampler = OpLoad %type_sampler %uniform_sampler %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler -%res1 = OpImageQuerySize %u32vec2 %simg +%res1 = OpImageQuerySize %u32vec2 %sampler )"; CompileSuccessfully(GenerateKernelCode(body).c_str()); @@ -3357,6 +3387,21 @@ TEST_F(ValidateImage, QuerySizeNotImage) { HasSubstr("Expected Image to be of type OpTypeImage")); } +TEST_F(ValidateImage, QuerySizeSampledImageDirectly) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQuerySize %u32vec2 %simg +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSampledImage instruction must not appear as operand " + "for OpImageQuerySize")); +} + TEST_F(ValidateImage, QuerySizeDimSubpassDataBad) { const std::string body = R"( %img = OpLoad %type_image_f32_spd_0002 %uniform_image_f32_spd_0002 @@ -3531,7 +3576,7 @@ TEST_F(ValidateImage, QueryLevelsNotImage) { %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 %sampler = OpLoad %type_sampler %uniform_sampler %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler -%res1 = OpImageQueryLevels %u32 %simg +%res1 = OpImageQueryLevels %u32 %sampler )"; CompileSuccessfully(GenerateKernelCode(body).c_str()); @@ -3540,6 +3585,21 @@ TEST_F(ValidateImage, QueryLevelsNotImage) { HasSubstr("Expected Image to be of type OpTypeImage")); } +TEST_F(ValidateImage, QueryLevelsSampledImageDirectly) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQueryLevels %u32 %simg +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSampledImage instruction must not appear as operand " + "for OpImageQueryLevels")); +} + TEST_F(ValidateImage, QueryLevelsWrongDim) { const std::string body = R"( %img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 @@ -4481,7 +4541,10 @@ TEST_F(ValidateImage, Issue2463NoSegFault) { )"; CompileSuccessfully(spirv); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSampledImage instruction must not appear as operand " + "for OpReturnValue")); } } // namespace diff --git a/3rdparty/spirv-tools/test/val/val_validation_state_test.cpp b/3rdparty/spirv-tools/test/val/val_validation_state_test.cpp index bf6509497..458157902 100644 --- a/3rdparty/spirv-tools/test/val/val_validation_state_test.cpp +++ b/3rdparty/spirv-tools/test/val/val_validation_state_test.cpp @@ -56,15 +56,16 @@ OpExecutionMode %1 OriginUpperLeft %4 = OpTypeFunction %void %float = OpTypeFloat 32 %_struct_6 = OpTypeStruct %float %float +%null = OpConstantNull %_struct_6 %7 = OpTypeFunction %_struct_6 %12 = OpFunction %_struct_6 None %7 %13 = OpLabel -OpUnreachable +OpReturnValue %null OpFunctionEnd %9 = OpFunction %_struct_6 None %7 %10 = OpLabel %11 = OpFunctionCall %_struct_6 %12 -OpUnreachable +OpReturnValue %null OpFunctionEnd %1 = OpFunction %void Pure|Const %4 %8 = OpLabel @@ -89,7 +90,7 @@ OpFunctionEnd %1 = OpFunction %void Pure|Const %4 %8 = OpLabel %2 = OpFunctionCall %_struct_6 %9 -OpUnreachable +OpReturn OpFunctionEnd )"; @@ -100,16 +101,17 @@ OpExecutionMode %1 OriginUpperLeft %4 = OpTypeFunction %void %float = OpTypeFloat 32 %_struct_6 = OpTypeStruct %float %float +%null = OpConstantNull %_struct_6 %7 = OpTypeFunction %_struct_6 %9 = OpFunction %_struct_6 None %7 %10 = OpLabel %11 = OpFunctionCall %_struct_6 %12 -OpUnreachable +OpReturnValue %null OpFunctionEnd %12 = OpFunction %_struct_6 None %7 %13 = OpLabel %14 = OpFunctionCall %_struct_6 %9 -OpUnreachable +OpReturnValue %null OpFunctionEnd %1 = OpFunction %void Pure|Const %4 %8 = OpLabel @@ -303,7 +305,7 @@ TEST_F(ValidationStateTest, CheckWebGPUIndirectlyRecursiveBodyBad) { ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("For WebGPU, functions need to be defined before being " - "called.\n %9 = OpFunctionCall %_struct_5 %10\n")); + "called.\n %10 = OpFunctionCall %_struct_5 %11\n")); } TEST_F(ValidationStateTest, diff --git a/3rdparty/spirv-tools/tools/opt/opt.cpp b/3rdparty/spirv-tools/tools/opt/opt.cpp index 0e77d95fb..fb48110e3 100644 --- a/3rdparty/spirv-tools/tools/opt/opt.cpp +++ b/3rdparty/spirv-tools/tools/opt/opt.cpp @@ -384,6 +384,8 @@ Options (in lexicographical order): Current workarounds: Avoid OpUnreachable in loops. --unify-const Remove the duplicated constants. + --validate-after-all + Validate the module after each pass is performed. -h, --help Print this help. --version @@ -628,6 +630,8 @@ OptStatus ParseFlags(int argc, const char** argv, optimizer->SetTargetEnv(SPV_ENV_WEBGPU_0); optimizer->RegisterWebGPUPasses(); + } else if (0 == strcmp(cur_arg, "--validate-after-all")) { + optimizer->SetValidateAfterAll(true); } else { // Some passes used to accept the form '--pass arg', canonicalize them // to '--pass=arg'. diff --git a/3rdparty/spirv-tools/tools/reduce/reduce.cpp b/3rdparty/spirv-tools/tools/reduce/reduce.cpp index a25e6d104..4c3c2d02d 100644 --- a/3rdparty/spirv-tools/tools/reduce/reduce.cpp +++ b/3rdparty/spirv-tools/tools/reduce/reduce.cpp @@ -18,21 +18,10 @@ #include #include "source/opt/build_module.h" -#include "source/opt/ir_context.h" #include "source/opt/log.h" -#include "source/reduce/merge_blocks_reduction_opportunity_finder.h" -#include "source/reduce/operand_to_const_reduction_opportunity_finder.h" -#include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h" -#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" #include "source/spirv_reducer_options.h" -#include "source/util/make_unique.h" #include "source/util/string_utils.h" -#include "spirv-tools/libspirv.hpp" #include "tools/io.h" #include "tools/util/cli_consumer.h" @@ -103,6 +92,10 @@ should be the path to a script. NOTE: The reducer is a work in progress. Options (in lexicographical order): + + --fail-on-validation-error + Stop reduction with an error if any reduction step produces a + SPIR-V module that fails to validate. -h, --help Print this help. --step-limit @@ -172,6 +165,8 @@ ReduceStatus ParseFlags(int argc, const char** argv, const char** in_file, assert(!*interestingness_test); *interestingness_test = cur_arg; positional_arg_index++; + } else if (0 == strcmp(cur_arg, "--fail-on-validation-error")) { + reducer_options->set_fail_on_validation_error(true); } else if (0 == strcmp(cur_arg, "--relax-logical-pointer")) { validator_options->SetRelaxLogicalPointer(true); } else if (0 == strcmp(cur_arg, "--relax-block-layout")) { @@ -246,25 +241,7 @@ int main(int argc, const char** argv) { return ExecuteCommand(command); }); - reducer.AddReductionPass( - spvtools::MakeUnique< - RemoveOpNameInstructionReductionOpportunityFinder>()); - reducer.AddReductionPass( - spvtools::MakeUnique()); - reducer.AddReductionPass( - spvtools::MakeUnique()); - reducer.AddReductionPass( - spvtools::MakeUnique()); - reducer.AddReductionPass( - spvtools::MakeUnique< - RemoveUnreferencedInstructionReductionOpportunityFinder>()); - reducer.AddReductionPass( - spvtools::MakeUnique< - StructuredLoopToSelectionReductionOpportunityFinder>()); - reducer.AddReductionPass( - spvtools::MakeUnique()); - reducer.AddReductionPass( - spvtools::MakeUnique()); + reducer.AddDefaultReductionPasses(); reducer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);