Updated spirv-tools.
This commit is contained in:
parent
2ddefa50b7
commit
2d6c2cebeb
@ -1 +1 @@
|
||||
"v2019.2", "SPIRV-Tools v2019.2 027e592038bb74c2ffe6b07f9cc4e2ab1b3fbb84"
|
||||
"v2019.2", "SPIRV-Tools v2019.2 2ddefa50b7e00747527827ee9aeddb9741bd6c2e"
|
||||
|
@ -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
|
||||
|
@ -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_;
|
||||
|
@ -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> impl_; // Unique pointer to internal data.
|
||||
|
12
3rdparty/spirv-tools/source/opt/basic_block.cpp
vendored
12
3rdparty/spirv-tools/source/opt/basic_block.cpp
vendored
@ -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;
|
||||
|
@ -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(); }
|
||||
|
@ -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<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
|
||||
}
|
||||
|
14
3rdparty/spirv-tools/source/opt/pass_manager.cpp
vendored
14
3rdparty/spirv-tools/source/opt/pass_manager.cpp
vendored
@ -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<uint32_t> 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);
|
||||
}
|
||||
|
29
3rdparty/spirv-tools/source/opt/pass_manager.h
vendored
29
3rdparty/spirv-tools/source/opt/pass_manager.h
vendored
@ -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> pass) {
|
||||
|
@ -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<BasicBlock*> order;
|
||||
context_->cfg()->ComputeStructuredOrder(func, &*func->begin(), &order);
|
||||
|
||||
|
38
3rdparty/spirv-tools/source/reduce/reducer.cpp
vendored
38
3rdparty/spirv-tools/source/reduce/reducer.cpp
vendored
@ -15,10 +15,17 @@
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
|
||||
#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<OperandToUndefReductionOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
spvtools::MakeUnique<OperandToConstReductionOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
spvtools::MakeUnique<OperandToDominatingIdReductionOpportunityFinder>());
|
||||
AddReductionPass(spvtools::MakeUnique<
|
||||
RemoveUnreferencedInstructionReductionOpportunityFinder>());
|
||||
AddReductionPass(spvtools::MakeUnique<
|
||||
StructuredLoopToSelectionReductionOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
spvtools::MakeUnique<MergeBlocksReductionOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
spvtools::MakeUnique<RemoveFunctionReductionOpportunityFinder>());
|
||||
}
|
||||
|
||||
void Reducer::AddReductionPass(
|
||||
std::unique_ptr<ReductionOpportunityFinder>&& finder) {
|
||||
impl_->passes.push_back(spvtools::MakeUnique<ReductionPass>(
|
||||
|
9
3rdparty/spirv-tools/source/reduce/reducer.h
vendored
9
3rdparty/spirv-tools/source/reduce/reducer.h
vendored
@ -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<ReductionOpportunityFinder>&& finder);
|
||||
|
@ -36,20 +36,19 @@ std::vector<uint32_t> ReductionPass::TryApplyReduction(
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> 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<uint32_t>();
|
||||
// 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<uint32_t>();
|
||||
@ -61,8 +60,6 @@ std::vector<uint32_t> ReductionPass::TryApplyReduction(
|
||||
opportunities[i]->TryToApply();
|
||||
}
|
||||
|
||||
index_ += granularity_;
|
||||
|
||||
std::vector<uint32_t> 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
|
||||
|
@ -15,10 +15,11 @@
|
||||
#ifndef SOURCE_REDUCE_REDUCTION_PASS_H_
|
||||
#define SOURCE_REDUCE_REDUCTION_PASS_H_
|
||||
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
#include <limits>
|
||||
|
||||
#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<ReductionOpportunityFinder> finder)
|
||||
: target_env_(target_env),
|
||||
finder_(std::move(finder)),
|
||||
is_initialized_(false) {}
|
||||
index_(0),
|
||||
granularity_(std::numeric_limits<uint32_t>::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<uint32_t> TryApplyReduction(const std::vector<uint32_t>& 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<ReductionOpportunityFinder> finder_;
|
||||
MessageConsumer consumer_;
|
||||
bool is_initialized_;
|
||||
uint32_t index_;
|
||||
uint32_t granularity_;
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -33,10 +33,9 @@ StructuredLoopToSelectionReductionOpportunityFinder::GetAvailableOpportunities(
|
||||
std::set<uint32_t> 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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_
|
||||
|
32
3rdparty/spirv-tools/source/val/validate_cfg.cpp
vendored
32
3rdparty/spirv-tools/source/val/validate_cfg.cpp
vendored
@ -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<BasicBlock*> stack;
|
||||
std::unordered_set<BasicBlock*> 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))
|
||||
|
@ -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 <id> 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 <id> from OpSampledImage instruction must not appear "
|
||||
@ -830,6 +853,18 @@ spv_result_t ValidateSampledImage(ValidationState_t& _,
|
||||
<< "' as an operand of <id> '"
|
||||
<< _.getIdName(consumer_instr->id()) << "'.";
|
||||
}
|
||||
|
||||
if (!IsAllowedSampledImageOperand(consumer_opcode)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Result <id> from OpSampledImage instruction must not appear "
|
||||
"as operand for Op"
|
||||
<< spvOpcodeString(static_cast<SpvOp>(consumer_opcode))
|
||||
<< ", since it is not specificed as taking an "
|
||||
<< "OpTypeSampledImage."
|
||||
<< " Found result <id> '" << _.getIdName(inst->id())
|
||||
<< "' as an operand of <id> '"
|
||||
<< _.getIdName(consumer_instr->id()) << "'.";
|
||||
}
|
||||
}
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
|
@ -590,7 +590,7 @@ OpUnreachable
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<BlockMergePass>(text, true);
|
||||
SinglePassRunAndMatch<BlockMergePass>(text, false);
|
||||
}
|
||||
|
||||
TEST_F(BlockMergeTest, DontMergeReturn) {
|
||||
|
15
3rdparty/spirv-tools/test/opt/inline_test.cpp
vendored
15
3rdparty/spirv-tools/test/opt/inline_test.cpp
vendored
@ -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<InlineExhaustivePass>(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<InlineExhaustivePass>(test, test, false, true);
|
||||
}
|
||||
|
||||
|
@ -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<IRContext> 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
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
#include "reduce_test_util.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
|
||||
|
@ -229,6 +229,7 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
|
||||
std::vector<uint32_t> 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<uint32_t> 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(
|
||||
|
@ -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<uint32_t> 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
|
||||
|
@ -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
|
||||
|
@ -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<uint32_t> 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<uint32_t> 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<uint32_t>(binary_in), &binary_out,
|
||||
reducer_options, validator_options);
|
||||
|
||||
ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
|
||||
ASSERT_EQ(status, Reducer::ReductionResultStatus::kStateInvalid);
|
||||
}
|
||||
}
|
||||
|
||||
|
139
3rdparty/spirv-tools/test/val/val_cfg_test.cpp
vendored
139
3rdparty/spirv-tools/test/val/val_cfg_test.cpp
vendored
@ -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
|
||||
|
14
3rdparty/spirv-tools/test/val/val_id_test.cpp
vendored
14
3rdparty/spirv-tools/test/val/val_id_test.cpp
vendored
@ -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) {
|
||||
|
77
3rdparty/spirv-tools/test/val/val_image_test.cpp
vendored
77
3rdparty/spirv-tools/test/val/val_image_test.cpp
vendored
@ -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
|
||||
|
@ -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,
|
||||
|
4
3rdparty/spirv-tools/tools/opt/opt.cpp
vendored
4
3rdparty/spirv-tools/tools/opt/opt.cpp
vendored
@ -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'.
|
||||
|
37
3rdparty/spirv-tools/tools/reduce/reduce.cpp
vendored
37
3rdparty/spirv-tools/tools/reduce/reduce.cpp
vendored
@ -18,21 +18,10 @@
|
||||
#include <functional>
|
||||
|
||||
#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<OperandToUndefReductionOpportunityFinder>());
|
||||
reducer.AddReductionPass(
|
||||
spvtools::MakeUnique<OperandToConstReductionOpportunityFinder>());
|
||||
reducer.AddReductionPass(
|
||||
spvtools::MakeUnique<OperandToDominatingIdReductionOpportunityFinder>());
|
||||
reducer.AddReductionPass(
|
||||
spvtools::MakeUnique<
|
||||
RemoveUnreferencedInstructionReductionOpportunityFinder>());
|
||||
reducer.AddReductionPass(
|
||||
spvtools::MakeUnique<
|
||||
StructuredLoopToSelectionReductionOpportunityFinder>());
|
||||
reducer.AddReductionPass(
|
||||
spvtools::MakeUnique<MergeBlocksReductionOpportunityFinder>());
|
||||
reducer.AddReductionPass(
|
||||
spvtools::MakeUnique<RemoveFunctionReductionOpportunityFinder>());
|
||||
reducer.AddDefaultReductionPasses();
|
||||
|
||||
reducer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user