reduce: improve remove unref instr pass (#2945)

* Remove Impl struct in Reducer; we can re-add it later (in a cleaner fashion) if we need to. 
* Add cleanup passes in Reducer; needed so that removal of constants can be disabled during the main passes, and then enabled during cleanup passes, otherwise some main passes can perform worse due to lack of available constants. 
* Delete passes: remove op name, remove relaxed precision. And delete associated tests. 
* Add more tests for remove unreferenced instructions. 
* Always return and write the output file, even if there was a reduction failure. 
* Only exit with 0 if the reduction completed or we hit the reduction step limit.
This commit is contained in:
Paul Thomson 2019-10-08 13:02:34 +01:00 committed by GitHub
parent 81d227f36b
commit 2f6a87f610
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 592 additions and 843 deletions

View File

@ -29,8 +29,6 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
remove_instruction_reduction_opportunity.h
remove_function_reduction_opportunity.h
remove_function_reduction_opportunity_finder.h
remove_opname_instruction_reduction_opportunity_finder.h
remove_relaxed_precision_decoration_opportunity_finder.h
remove_selection_reduction_opportunity.h
remove_selection_reduction_opportunity_finder.h
remove_unreferenced_instruction_reduction_opportunity_finder.h
@ -57,11 +55,9 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
remove_function_reduction_opportunity.cpp
remove_function_reduction_opportunity_finder.cpp
remove_instruction_reduction_opportunity.cpp
remove_relaxed_precision_decoration_opportunity_finder.cpp
remove_selection_reduction_opportunity.cpp
remove_selection_reduction_opportunity_finder.cpp
remove_unreferenced_instruction_reduction_opportunity_finder.cpp
remove_opname_instruction_reduction_opportunity_finder.cpp
structured_loop_to_selection_reduction_opportunity.cpp
structured_loop_to_selection_reduction_opportunity_finder.cpp
conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp

View File

@ -24,8 +24,6 @@
#include "source/reduce/operand_to_undef_reduction_opportunity_finder.h"
#include "source/reduce/remove_block_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_relaxed_precision_decoration_opportunity_finder.h"
#include "source/reduce/remove_selection_reduction_opportunity_finder.h"
#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
#include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
@ -35,41 +33,32 @@
namespace spvtools {
namespace reduce {
struct Reducer::Impl {
explicit Impl(spv_target_env env) : target_env(env) {}
bool ReachedStepLimit(uint32_t current_step,
spv_const_reducer_options options);
const spv_target_env target_env; // Target environment.
MessageConsumer consumer; // Message consumer.
InterestingnessFunction interestingness_function;
std::vector<std::unique_ptr<ReductionPass>> passes;
};
Reducer::Reducer(spv_target_env env) : impl_(MakeUnique<Impl>(env)) {}
Reducer::Reducer(spv_target_env target_env) : target_env_(target_env) {}
Reducer::~Reducer() = default;
void Reducer::SetMessageConsumer(MessageConsumer c) {
for (auto& pass : impl_->passes) {
for (auto& pass : passes_) {
pass->SetMessageConsumer(c);
}
impl_->consumer = std::move(c);
for (auto& pass : cleanup_passes_) {
pass->SetMessageConsumer(c);
}
consumer_ = std::move(c);
}
void Reducer::SetInterestingnessFunction(
Reducer::InterestingnessFunction interestingness_function) {
impl_->interestingness_function = std::move(interestingness_function);
interestingness_function_ = std::move(interestingness_function);
}
Reducer::ReductionResultStatus Reducer::Run(
std::vector<uint32_t>&& binary_in, std::vector<uint32_t>* binary_out,
spv_const_reducer_options options,
spv_validator_options validator_options) const {
spv_validator_options validator_options) {
std::vector<uint32_t> current_binary(std::move(binary_in));
spvtools::SpirvTools tools(impl_->target_env);
spvtools::SpirvTools tools(target_env_);
assert(tools.IsValid() && "Failed to create SPIRV-Tools interface");
// Keeps track of how many reduction attempts have been tried. Reduction
@ -79,113 +68,49 @@ Reducer::ReductionResultStatus Reducer::Run(
// Initial state should be valid.
if (!tools.Validate(&current_binary[0], current_binary.size(),
validator_options)) {
impl_->consumer(SPV_MSG_INFO, nullptr, {},
"Initial binary is invalid; stopping.");
consumer_(SPV_MSG_INFO, nullptr, {},
"Initial binary is invalid; stopping.");
return Reducer::ReductionResultStatus::kInitialStateInvalid;
}
// Initial state should be interesting.
if (!impl_->interestingness_function(current_binary, reductions_applied)) {
impl_->consumer(SPV_MSG_INFO, nullptr, {},
"Initial state was not interesting; stopping.");
if (!interestingness_function_(current_binary, reductions_applied)) {
consumer_(SPV_MSG_INFO, nullptr, {},
"Initial state was not interesting; stopping.");
return Reducer::ReductionResultStatus::kInitialStateNotInteresting;
}
// Determines whether, on completing one round of reduction passes, it is
// worthwhile trying a further round.
bool another_round_worthwhile = true;
Reducer::ReductionResultStatus result =
RunPasses(&passes_, options, validator_options, tools, &current_binary,
&reductions_applied);
// Apply round after round of reduction passes until we hit the reduction
// step limit, or deem that another round is not going to be worthwhile.
while (!impl_->ReachedStepLimit(reductions_applied, options) &&
another_round_worthwhile) {
// At the start of a round of reduction passes, assume another round will
// not be worthwhile unless we find evidence to the contrary.
another_round_worthwhile = false;
// Iterate through the available passes
for (auto& pass : impl_->passes) {
// If this pass hasn't reached its minimum granularity then it's
// worth eventually doing another round of reductions, in order to
// try this pass at a finer granularity.
another_round_worthwhile |= !pass->ReachedMinimumGranularity();
// Keep applying this pass at its current granularity until it stops
// working or we hit the reduction step limit.
impl_->consumer(SPV_MSG_INFO, nullptr, {},
("Trying pass " + pass->GetName() + ".").c_str());
do {
auto maybe_result = pass->TryApplyReduction(current_binary);
if (maybe_result.empty()) {
// 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 "
<< reductions_applied << ".";
impl_->consumer(SPV_MSG_INFO, nullptr, {},
(stringstream.str().c_str()));
if (!tools.Validate(&maybe_result[0], maybe_result.size(),
validator_options)) {
// The reduction step went wrong and an invalid binary was produced.
// By design, this shouldn't happen; this is a safeguard to stop an
// 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
// interesting, so make it the binary of interest henceforth, and
// note that it's worth doing another round of reduction passes.
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));
}
if (result == Reducer::ReductionResultStatus::kComplete) {
// Cleanup passes.
result = RunPasses(&cleanup_passes_, options, validator_options, tools,
&current_binary, &reductions_applied);
}
if (result == Reducer::ReductionResultStatus::kComplete) {
consumer_(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping.");
}
// Even if the reduction has failed by this point (e.g. due to producing an
// invalid binary), we still update the output binary for better debugging.
*binary_out = std::move(current_binary);
// Report whether reduction completed, or bailed out early due to reaching
// the step limit.
if (impl_->ReachedStepLimit(reductions_applied, options)) {
impl_->consumer(SPV_MSG_INFO, nullptr, {},
"Reached reduction step limit; stopping.");
return Reducer::ReductionResultStatus::kReachedStepLimit;
}
impl_->consumer(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping.");
return Reducer::ReductionResultStatus::kComplete;
return result;
}
void Reducer::AddDefaultReductionPasses() {
AddReductionPass(spvtools::MakeUnique<
RemoveOpNameInstructionReductionOpportunityFinder>());
AddReductionPass(spvtools::MakeUnique<
RemoveRelaxedPrecisionDecorationOpportunityFinder>());
AddReductionPass(
spvtools::MakeUnique<
RemoveUnreferencedInstructionReductionOpportunityFinder>(false));
AddReductionPass(
spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
AddReductionPass(
spvtools::MakeUnique<OperandToConstReductionOpportunityFinder>());
AddReductionPass(
spvtools::MakeUnique<OperandToDominatingIdReductionOpportunityFinder>());
AddReductionPass(spvtools::MakeUnique<
RemoveUnreferencedInstructionReductionOpportunityFinder>());
AddReductionPass(spvtools::MakeUnique<
StructuredLoopToSelectionReductionOpportunityFinder>());
AddReductionPass(
@ -201,18 +126,117 @@ void Reducer::AddDefaultReductionPasses() {
ConditionalBranchToSimpleConditionalBranchOpportunityFinder>());
AddReductionPass(
spvtools::MakeUnique<SimpleConditionalBranchToBranchOpportunityFinder>());
// Cleanup passes.
AddCleanupReductionPass(
spvtools::MakeUnique<
RemoveUnreferencedInstructionReductionOpportunityFinder>(true));
}
void Reducer::AddReductionPass(
std::unique_ptr<ReductionOpportunityFinder>&& finder) {
impl_->passes.push_back(spvtools::MakeUnique<ReductionPass>(
impl_->target_env, std::move(finder)));
passes_.push_back(
spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder)));
}
bool Reducer::Impl::ReachedStepLimit(uint32_t current_step,
spv_const_reducer_options options) {
void Reducer::AddCleanupReductionPass(
std::unique_ptr<ReductionOpportunityFinder>&& finder) {
cleanup_passes_.push_back(
spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder)));
}
bool Reducer::ReachedStepLimit(uint32_t current_step,
spv_const_reducer_options options) {
return current_step >= options->step_limit;
}
Reducer::ReductionResultStatus Reducer::RunPasses(
std::vector<std::unique_ptr<ReductionPass>>* passes,
spv_const_reducer_options options, spv_validator_options validator_options,
const SpirvTools& tools, std::vector<uint32_t>* current_binary,
uint32_t* const reductions_applied) {
// Determines whether, on completing one round of reduction passes, it is
// worthwhile trying a further round.
bool another_round_worthwhile = true;
// Apply round after round of reduction passes until we hit the reduction
// step limit, or deem that another round is not going to be worthwhile.
while (!ReachedStepLimit(*reductions_applied, options) &&
another_round_worthwhile) {
// At the start of a round of reduction passes, assume another round will
// not be worthwhile unless we find evidence to the contrary.
another_round_worthwhile = false;
// Iterate through the available passes.
for (auto& pass : *passes) {
// If this pass hasn't reached its minimum granularity then it's
// worth eventually doing another round of reductions, in order to
// try this pass at a finer granularity.
another_round_worthwhile |= !pass->ReachedMinimumGranularity();
// Keep applying this pass at its current granularity until it stops
// working or we hit the reduction step limit.
consumer_(SPV_MSG_INFO, nullptr, {},
("Trying pass " + pass->GetName() + ".").c_str());
do {
auto maybe_result = pass->TryApplyReduction(*current_binary);
if (maybe_result.empty()) {
// For this round, the pass has no more opportunities (chunks) to
// apply, so move on to the next pass.
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 "
<< *reductions_applied << ".";
consumer_(SPV_MSG_INFO, nullptr, {}, (stringstream.str().c_str()));
if (!tools.Validate(&maybe_result[0], maybe_result.size(),
validator_options)) {
// The reduction step went wrong and an invalid binary was produced.
// By design, this shouldn't happen; this is a safeguard to stop an
// invalid binary from being regarded as interesting.
consumer_(SPV_MSG_INFO, nullptr, {},
"Reduction step produced an invalid binary.");
if (options->fail_on_validation_error) {
// In this mode, we fail, so we update the current binary so it is
// output for debugging.
*current_binary = std::move(maybe_result);
return Reducer::ReductionResultStatus::kStateInvalid;
}
} else if (interestingness_function_(maybe_result,
*reductions_applied)) {
// Success! The binary produced by this reduction step is
// interesting, so make it the binary of interest henceforth, and
// note that it's worth doing another round of reduction passes.
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 (!ReachedStepLimit(*reductions_applied, options));
}
}
// Report whether reduction completed, or bailed out early due to reaching
// the step limit.
if (ReachedStepLimit(*reductions_applied, options)) {
consumer_(SPV_MSG_INFO, nullptr, {},
"Reached reduction step limit; stopping.");
return Reducer::ReductionResultStatus::kReachedStepLimit;
}
// The passes completed successfully, although we may still run more passes.
return Reducer::ReductionResultStatus::kComplete;
}
} // namespace reduce
} // namespace spvtools

View File

@ -50,7 +50,7 @@ class Reducer {
using InterestingnessFunction =
std::function<bool(const std::vector<uint32_t>&, uint32_t)>;
// Constructs an instance with the given target |env|, which is used to
// Constructs an instance with the given target |target_env|, which is used to
// decode the binary to be reduced later.
//
// The constructed instance will have an empty message consumer, which just
@ -59,7 +59,7 @@ class Reducer {
//
// The constructed instance also needs to have an interestingness function
// set and some reduction passes added to it in order to be useful.
explicit Reducer(spv_target_env env);
explicit Reducer(spv_target_env target_env);
// Disables copy/move constructor/assignment operations.
Reducer(const Reducer&) = delete;
@ -86,17 +86,34 @@ class Reducer {
// that will be iterated over.
void AddReductionPass(std::unique_ptr<ReductionOpportunityFinder>&& finder);
// Adds a cleanup reduction pass based on the given finder to the sequence of
// passes that will run after other passes.
void AddCleanupReductionPass(
std::unique_ptr<ReductionOpportunityFinder>&& finder);
// Reduces the given SPIR-V module |binary_out|.
// The reduced binary ends up in |binary_out|.
// A status is returned.
ReductionResultStatus Run(std::vector<uint32_t>&& binary_in,
std::vector<uint32_t>* binary_out,
spv_const_reducer_options options,
spv_validator_options validator_options) const;
spv_validator_options validator_options);
private:
struct Impl; // Opaque struct for holding internal data.
std::unique_ptr<Impl> impl_; // Unique pointer to internal data.
static bool ReachedStepLimit(uint32_t current_step,
spv_const_reducer_options options);
ReductionResultStatus RunPasses(
std::vector<std::unique_ptr<ReductionPass>>* passes,
spv_const_reducer_options options,
spv_validator_options validator_options, const SpirvTools& tools,
std::vector<uint32_t>* current_binary, uint32_t* reductions_applied);
const spv_target_env target_env_;
MessageConsumer consumer_;
InterestingnessFunction interestingness_function_;
std::vector<std::unique_ptr<ReductionPass>> passes_;
std::vector<std::unique_ptr<ReductionPass>> cleanup_passes_;
};
} // namespace reduce

View File

@ -1,45 +0,0 @@
// Copyright (c) 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
#include "source/opcode.h"
#include "source/opt/instruction.h"
#include "source/reduce/remove_instruction_reduction_opportunity.h"
namespace spvtools {
namespace reduce {
using opt::IRContext;
std::vector<std::unique_ptr<ReductionOpportunity>>
RemoveOpNameInstructionReductionOpportunityFinder::GetAvailableOpportunities(
IRContext* context) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
for (auto& inst : context->module()->debugs2()) {
if (inst.opcode() == SpvOpName || inst.opcode() == SpvOpMemberName) {
result.push_back(
MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}
}
return result;
}
std::string RemoveOpNameInstructionReductionOpportunityFinder::GetName() const {
return "RemoveOpNameInstructionReductionOpportunityFinder";
}
} // namespace reduce
} // namespace spvtools

View File

@ -1,45 +0,0 @@
// Copyright (c) 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
#define SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
#include "source/reduce/reduction_opportunity_finder.h"
namespace spvtools {
namespace reduce {
// A finder for opportunities to remove OpName instructions. As well as making
// the module smaller, removing an OpName instruction may create opportunities
// for subsequently removing the instructions that create the ids to which the
// OpName applies.
class RemoveOpNameInstructionReductionOpportunityFinder
: public ReductionOpportunityFinder {
public:
RemoveOpNameInstructionReductionOpportunityFinder() = default;
~RemoveOpNameInstructionReductionOpportunityFinder() override = default;
std::string GetName() const final;
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
private:
};
} // namespace reduce
} // namespace spvtools
#endif // SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_

View File

@ -1,49 +0,0 @@
// Copyright (c) 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
#include "source/reduce/remove_instruction_reduction_opportunity.h"
namespace spvtools {
namespace reduce {
std::vector<std::unique_ptr<ReductionOpportunity>>
RemoveRelaxedPrecisionDecorationOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
// Consider all annotation instructions
for (auto& inst : context->module()->annotations()) {
// We are interested in removing instructions of the form:
// SpvOpDecorate %id RelaxedPrecision
// and
// SpvOpMemberDecorate %id member RelaxedPrecision
if ((inst.opcode() == SpvOpDecorate &&
inst.GetSingleWordInOperand(1) == SpvDecorationRelaxedPrecision) ||
(inst.opcode() == SpvOpMemberDecorate &&
inst.GetSingleWordInOperand(2) == SpvDecorationRelaxedPrecision)) {
result.push_back(
MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}
}
return result;
}
std::string RemoveRelaxedPrecisionDecorationOpportunityFinder::GetName() const {
return "RemoveRelaxedPrecisionDecorationOpportunityFinder";
}
} // namespace reduce
} // namespace spvtools

View File

@ -1,36 +0,0 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
#define SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
#include "source/reduce/reduction_opportunity_finder.h"
namespace spvtools {
namespace reduce {
// A finder for opportunities to remove relaxed precision decorations.
class RemoveRelaxedPrecisionDecorationOpportunityFinder
: public ReductionOpportunityFinder {
public:
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const override;
std::string GetName() const override;
};
} // namespace reduce
} // namespace spvtools
#endif // SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_

View File

@ -21,28 +21,109 @@
namespace spvtools {
namespace reduce {
RemoveUnreferencedInstructionReductionOpportunityFinder::
RemoveUnreferencedInstructionReductionOpportunityFinder(
bool remove_constants_and_undefs)
: remove_constants_and_undefs_(remove_constants_and_undefs) {}
std::vector<std::unique_ptr<ReductionOpportunity>>
RemoveUnreferencedInstructionReductionOpportunityFinder::
GetAvailableOpportunities(opt::IRContext* context) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
for (auto& inst : context->module()->debugs1()) {
if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
continue;
}
result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}
for (auto& inst : context->module()->debugs2()) {
if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
continue;
}
result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}
for (auto& inst : context->module()->debugs3()) {
if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
continue;
}
result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}
for (auto& inst : context->module()->types_values()) {
if (context->get_def_use_mgr()->NumUsers(&inst) > 0) {
continue;
}
if (!remove_constants_and_undefs_ &&
spvOpcodeIsConstantOrUndef(inst.opcode())) {
continue;
}
result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}
for (auto& inst : context->module()->annotations()) {
if (context->get_def_use_mgr()->NumUsers(&inst) > 0) {
continue;
}
uint32_t decoration = SpvDecorationMax;
switch (inst.opcode()) {
case SpvOpDecorate:
case SpvOpDecorateId:
case SpvOpDecorateString:
decoration = inst.GetSingleWordInOperand(1u);
break;
case SpvOpMemberDecorate:
case SpvOpMemberDecorateString:
decoration = inst.GetSingleWordInOperand(2u);
break;
default:
break;
}
// We conservatively only remove specific decorations that we believe will
// not change the shader interface, will not make the shader invalid, will
// actually be found in practice, etc.
switch (decoration) {
case SpvDecorationRelaxedPrecision:
case SpvDecorationNoSignedWrap:
case SpvDecorationNoContraction:
case SpvDecorationNoUnsignedWrap:
case SpvDecorationUserSemantic:
break;
default:
// Give up.
continue;
}
result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}
for (auto& function : *context->module()) {
for (auto& block : function) {
for (auto& inst : block) {
if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
continue;
}
if (!remove_constants_and_undefs_ &&
spvOpcodeIsConstantOrUndef(inst.opcode())) {
continue;
}
if (spvOpcodeIsBlockTerminator(inst.opcode()) ||
inst.opcode() == SpvOpSelectionMerge ||
inst.opcode() == SpvOpLoopMerge) {
// In this reduction pass we do not want to affect static control
// flow.
// In this reduction pass we do not want to affect static
// control flow.
continue;
}
// Given that we're in a block, we should only get here if the
// instruction is not directly related to control flow; i.e., it's
// some straightforward instruction with an unused result, like an
// arithmetic operation or function call.
// Given that we're in a block, we should only get here if
// the instruction is not directly related to control flow;
// i.e., it's some straightforward instruction with an
// unused result, like an arithmetic operation or function
// call.
result.push_back(
MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}

View File

@ -28,7 +28,8 @@ namespace reduce {
class RemoveUnreferencedInstructionReductionOpportunityFinder
: public ReductionOpportunityFinder {
public:
RemoveUnreferencedInstructionReductionOpportunityFinder() = default;
explicit RemoveUnreferencedInstructionReductionOpportunityFinder(
bool remove_constants_and_undefs);
~RemoveUnreferencedInstructionReductionOpportunityFinder() override = default;
@ -38,6 +39,7 @@ class RemoveUnreferencedInstructionReductionOpportunityFinder
opt::IRContext* context) const final;
private:
bool remove_constants_and_undefs_;
};
} // namespace reduce

View File

@ -23,8 +23,6 @@ add_spvtools_unittest(TARGET reduce
reducer_test.cpp
remove_block_test.cpp
remove_function_test.cpp
remove_opname_instruction_test.cpp
remove_relaxed_precision_decoration_test.cpp
remove_selection_test.cpp
remove_unreferenced_instruction_test.cpp
structured_loop_to_selection_test.cpp

View File

@ -14,8 +14,8 @@
#include "source/reduce/reducer.h"
#include "source/opt/build_module.h"
#include "source/reduce/operand_to_const_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 "test/reduce/reduce_test_util.h"
@ -23,6 +23,12 @@ namespace spvtools {
namespace reduce {
namespace {
using opt::BasicBlock;
using opt::IRContext;
const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3;
const MessageConsumer kMessageConsumer = CLIMessageConsumer;
// This changes its mind each time IsInteresting is invoked as to whether the
// binary is interesting, until some limit is reached after which the binary is
// always deemed interesting. This is useful to test that reduction passes
@ -55,6 +61,8 @@ class PingPongInteresting {
};
TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
// Check that ExprToConstant and RemoveUnreferenced work together; once some
// ID uses have been changed to constants, those IDs can be removed.
std::string original = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@ -149,15 +157,6 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %60
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %16 "buf2"
OpMemberName %16 0 "i"
OpName %18 ""
OpName %25 "buf1"
OpMemberName %25 0 "f"
OpName %27 ""
OpName %60 "_GLF_color"
OpMemberDecorate %16 0 Offset 0
OpDecorate %16 Block
OpDecorate %18 DescriptorSet 0
@ -174,14 +173,12 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
%16 = OpTypeStruct %6
%17 = OpTypePointer Uniform %16
%18 = OpVariable %17 Uniform
%19 = OpTypePointer Uniform %6
%22 = OpTypeBool
%100 = OpConstantTrue %22
%24 = OpTypeFloat 32
%25 = OpTypeStruct %24
%26 = OpTypePointer Uniform %25
%27 = OpVariable %26 Uniform
%28 = OpTypePointer Uniform %24
%31 = OpConstant %24 2
%56 = OpConstant %6 1
%58 = OpTypeVector %24 4
@ -209,21 +206,21 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
OpFunctionEnd
)";
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
Reducer reducer(env);
Reducer reducer(kEnv);
PingPongInteresting ping_pong_interesting(10);
reducer.SetMessageConsumer(NopDiagnostic);
reducer.SetInterestingnessFunction(
[&](const std::vector<uint32_t>& binary, uint32_t) -> bool {
return ping_pong_interesting.IsInteresting(binary);
});
reducer.AddReductionPass(
MakeUnique<RemoveUnreferencedInstructionReductionOpportunityFinder>(
false));
reducer.AddReductionPass(
MakeUnique<OperandToConstReductionOpportunityFinder>());
reducer.AddReductionPass(
MakeUnique<RemoveUnreferencedInstructionReductionOpportunityFinder>());
std::vector<uint32_t> binary_in;
SpirvTools t(env);
SpirvTools t(kEnv);
ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
std::vector<uint32_t> binary_out;
@ -237,83 +234,7 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
CheckEqual(env, expected, binary_out);
}
TEST(ReducerTest, RemoveOpnameAndRemoveUnreferenced) {
const std::string original = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
OpName %2 "main"
OpName %3 "a"
OpName %4 "this-name-counts-as-usage-for-load-instruction"
%5 = OpTypeVoid
%6 = OpTypeFunction %5
%7 = OpTypeFloat 32
%8 = OpTypePointer Function %7
%9 = OpConstant %7 1
%2 = OpFunction %5 None %6
%10 = OpLabel
%3 = OpVariable %8 Function
%4 = OpLoad %7 %3
OpStore %3 %9
OpReturn
OpFunctionEnd
)";
const std::string expected = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%5 = OpTypeVoid
%6 = OpTypeFunction %5
%7 = OpTypeFloat 32
%8 = OpTypePointer Function %7
%9 = OpConstant %7 1
%2 = OpFunction %5 None %6
%10 = OpLabel
OpReturn
OpFunctionEnd
)";
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
Reducer reducer(env);
// Make ping-pong interesting very quickly, as there are not many
// opportunities.
PingPongInteresting ping_pong_interesting(1);
reducer.SetMessageConsumer(NopDiagnostic);
reducer.SetInterestingnessFunction(
[&](const std::vector<uint32_t>& binary, uint32_t) -> bool {
return ping_pong_interesting.IsInteresting(binary);
});
reducer.AddReductionPass(
MakeUnique<RemoveOpNameInstructionReductionOpportunityFinder>());
reducer.AddReductionPass(
MakeUnique<RemoveUnreferencedInstructionReductionOpportunityFinder>());
std::vector<uint32_t> binary_in;
SpirvTools t(env);
ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
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(
std::move(binary_in), &binary_out, reducer_options, validator_options);
ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
CheckEqual(env, expected, binary_out);
CheckEqual(kEnv, expected, binary_out);
}
} // namespace

View File

@ -1,225 +0,0 @@
// Copyright (c) 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
#include "source/opt/build_module.h"
#include "source/reduce/reduction_opportunity.h"
#include "source/reduce/reduction_pass.h"
#include "test/reduce/reduce_test_util.h"
namespace spvtools {
namespace reduce {
namespace {
TEST(RemoveOpnameInstructionReductionPassTest, NothingToRemove) {
const std::string source = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, source, kReduceAssembleOption);
const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(0, ops.size());
}
TEST(RemoveOpnameInstructionReductionPassTest, RemoveSingleOpName) {
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
)";
const std::string epilogue = R"(
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string original = prologue + R"(
OpName %4 "main"
)" + epilogue;
const std::string expected = prologue + epilogue;
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, original, kReduceAssembleOption);
const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
CheckEqual(env, expected, context.get());
}
TEST(RemoveOpnameInstructionReductionPassTest, TryApplyRemovesAllOpName) {
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
)";
const std::string epilogue = R"(
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%11 = OpVariable %7 Function
%12 = OpVariable %7 Function
OpStore %8 %9
OpStore %10 %9
OpStore %11 %9
OpStore %12 %9
OpReturn
OpFunctionEnd
)";
const std::string original = prologue + R"(
OpName %4 "main"
OpName %8 "a"
OpName %10 "b"
OpName %11 "c"
OpName %12 "d"
)" + epilogue;
const std::string expected = prologue + epilogue;
const auto env = SPV_ENV_UNIVERSAL_1_3;
{
// Check the right number of opportunities is detected
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, original, kReduceAssembleOption);
const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(5, ops.size());
}
{
// The reduction should remove all OpName
std::vector<uint32_t> binary;
SpirvTools t(env);
ASSERT_TRUE(t.Assemble(original, &binary, kReduceAssembleOption));
auto reduced_binary =
ReductionPass(env,
spvtools::MakeUnique<
RemoveOpNameInstructionReductionOpportunityFinder>())
.TryApplyReduction(binary);
CheckEqual(env, expected, reduced_binary);
}
}
TEST(RemoveOpnameInstructionReductionPassTest,
TryApplyRemovesAllOpNameAndOpMemberName) {
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
)";
const std::string epilogue = R"(
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeInt 32 1
%8 = OpTypeVector %6 3
%9 = OpTypeStruct %6 %7 %8
%10 = OpTypePointer Function %9
%12 = OpConstant %7 0
%13 = OpConstant %6 1
%14 = OpTypePointer Function %6
%4 = OpFunction %2 None %3
%5 = OpLabel
%11 = OpVariable %10 Function
%15 = OpAccessChain %14 %11 %12
OpStore %15 %13
OpReturn
OpFunctionEnd
)";
const std::string original = prologue + R"(
OpName %4 "main"
OpName %9 "S"
OpMemberName %9 0 "f"
OpMemberName %9 1 "i"
OpMemberName %9 2 "v"
OpName %11 "s"
)" + epilogue;
const std::string expected = prologue + epilogue;
const auto env = SPV_ENV_UNIVERSAL_1_3;
{
// Check the right number of opportunities is detected
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, original, kReduceAssembleOption);
const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(6, ops.size());
}
{
// The reduction should remove all OpName
std::vector<uint32_t> binary;
SpirvTools t(env);
ASSERT_TRUE(t.Assemble(original, &binary, kReduceAssembleOption));
auto reduced_binary =
ReductionPass(env,
spvtools::MakeUnique<
RemoveOpNameInstructionReductionOpportunityFinder>())
.TryApplyReduction(binary);
CheckEqual(env, expected, reduced_binary);
}
}
} // namespace
} // namespace reduce
} // namespace spvtools

View File

@ -1,177 +0,0 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
#include "source/opt/build_module.h"
#include "source/reduce/reduction_opportunity.h"
#include "source/reduce/reduction_pass.h"
#include "test/reduce/reduce_test_util.h"
namespace spvtools {
namespace reduce {
namespace {
TEST(RemoveRelaxedPrecisionDecorationTest, NothingToRemove) {
const std::string source = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, source, kReduceAssembleOption);
const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(0, ops.size());
}
TEST(RemoveRelaxedPrecisionDecorationTest, RemoveDecorations) {
const std::string source = 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 "f"
OpName %12 "i"
OpName %16 "v"
OpName %19 "S"
OpMemberName %19 0 "a"
OpMemberName %19 1 "b"
OpMemberName %19 2 "c"
OpName %21 "s"
OpDecorate %8 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
OpDecorate %16 RelaxedPrecision
OpDecorate %17 RelaxedPrecision
OpDecorate %18 RelaxedPrecision
OpMemberDecorate %19 0 RelaxedPrecision
OpMemberDecorate %19 1 RelaxedPrecision
OpMemberDecorate %19 2 RelaxedPrecision
OpDecorate %22 RelaxedPrecision
OpDecorate %23 RelaxedPrecision
OpDecorate %24 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypePointer Function %6
%9 = OpConstant %6 2
%10 = OpTypeInt 32 1
%11 = OpTypePointer Function %10
%13 = OpConstant %10 22
%14 = OpTypeVector %6 2
%15 = OpTypePointer Function %14
%19 = OpTypeStruct %10 %6 %14
%20 = OpTypePointer Function %19
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%12 = OpVariable %11 Function
%16 = OpVariable %15 Function
%21 = OpVariable %20 Function
OpStore %8 %9
OpStore %12 %13
%17 = OpLoad %6 %8
%18 = OpCompositeConstruct %14 %17 %17
OpStore %16 %18
%22 = OpLoad %10 %12
%23 = OpLoad %6 %8
%24 = OpLoad %14 %16
%25 = OpCompositeConstruct %19 %22 %23 %24
OpStore %21 %25
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, source, kReduceAssembleOption);
const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(11, ops.size());
for (auto& op : ops) {
ASSERT_TRUE(op->PreconditionHolds());
op->TryToApply();
}
const std::string expected = 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 "f"
OpName %12 "i"
OpName %16 "v"
OpName %19 "S"
OpMemberName %19 0 "a"
OpMemberName %19 1 "b"
OpMemberName %19 2 "c"
OpName %21 "s"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypePointer Function %6
%9 = OpConstant %6 2
%10 = OpTypeInt 32 1
%11 = OpTypePointer Function %10
%13 = OpConstant %10 22
%14 = OpTypeVector %6 2
%15 = OpTypePointer Function %14
%19 = OpTypeStruct %10 %6 %14
%20 = OpTypePointer Function %19
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%12 = OpVariable %11 Function
%16 = OpVariable %15 Function
%21 = OpVariable %20 Function
OpStore %8 %9
OpStore %12 %13
%17 = OpLoad %6 %8
%18 = OpCompositeConstruct %14 %17 %17
OpStore %16 %18
%22 = OpLoad %10 %12
%23 = OpLoad %6 %8
%24 = OpLoad %14 %16
%25 = OpCompositeConstruct %19 %22 %23 %24
OpStore %21 %25
OpReturn
OpFunctionEnd
)";
CheckEqual(env, expected, context.get());
}
} // namespace
} // namespace reduce
} // namespace spvtools

View File

@ -23,19 +23,26 @@ namespace spvtools {
namespace reduce {
namespace {
const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3;
TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) {
const std::string prologue = R"(
// A module with some unused instructions, including some unused OpStore
// instructions.
RemoveUnreferencedInstructionReductionOpportunityFinder finder(true);
const std::string original = 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"
OpSource ESSL 310 ; 0
OpName %4 "main" ; 1
OpName %8 "a" ; 2
OpName %10 "b" ; 3
OpName %12 "c" ; 4
OpName %14 "d" ; 5
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
@ -49,51 +56,323 @@ TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) {
%10 = OpVariable %7 Function
%12 = OpVariable %7 Function
%14 = OpVariable %7 Function
OpStore %8 %9 ; 6
OpStore %10 %11 ; 7
OpStore %12 %13 ; 8
%15 = OpLoad %6 %8
OpStore %14 %15 ; 9
OpReturn
OpFunctionEnd
)";
const std::string epilogue = R"(
const MessageConsumer consumer = nullptr;
const auto context =
BuildModule(kEnv, consumer, original, kReduceAssembleOption);
CheckValid(kEnv, context.get());
auto ops = finder.GetAvailableOpportunities(context.get());
ASSERT_EQ(10, ops.size());
for (auto& op : ops) {
ASSERT_TRUE(op->PreconditionHolds());
op->TryToApply();
CheckValid(kEnv, context.get());
}
const std::string step_2 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 10 ; 0
%11 = OpConstant %6 20 ; 1
%13 = OpConstant %6 30 ; 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function ; 3
%12 = OpVariable %7 Function ; 4
%14 = OpVariable %7 Function ; 5
%15 = OpLoad %6 %8 ; 6
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;
CheckEqual(kEnv, step_2, context.get());
const std::string expected_after_2 = prologue + R"(
OpStore %12 %13
%15 = OpLoad %6 %8
OpStore %14 %15
)" + epilogue;
ops = finder.GetAvailableOpportunities(context.get());
const std::string expected_after_4 = prologue + R"(
%15 = OpLoad %6 %8
)" + epilogue;
ASSERT_EQ(7, ops.size());
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, original, kReduceAssembleOption);
const auto ops = RemoveUnreferencedInstructionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(4, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
ASSERT_TRUE(ops[1]->PreconditionHolds());
ops[1]->TryToApply();
for (auto& op : ops) {
ASSERT_TRUE(op->PreconditionHolds());
op->TryToApply();
CheckValid(kEnv, context.get());
}
CheckEqual(env, expected_after_2, context.get());
const std::string step_3 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function ; 0
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(ops[2]->PreconditionHolds());
ops[2]->TryToApply();
ASSERT_TRUE(ops[3]->PreconditionHolds());
ops[3]->TryToApply();
CheckEqual(kEnv, step_3, context.get());
CheckEqual(env, expected_after_4, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
for (auto& op : ops) {
ASSERT_TRUE(op->PreconditionHolds());
op->TryToApply();
CheckValid(kEnv, context.get());
}
const std::string step_4 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6 ; 0
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(kEnv, step_4, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
for (auto& op : ops) {
ASSERT_TRUE(op->PreconditionHolds());
op->TryToApply();
CheckValid(kEnv, context.get());
}
const std::string step_5 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1 ; 0
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(kEnv, step_5, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
for (auto& op : ops) {
ASSERT_TRUE(op->PreconditionHolds());
op->TryToApply();
CheckValid(kEnv, context.get());
}
const std::string step_6 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(kEnv, step_6, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ASSERT_EQ(0, ops.size());
}
TEST(RemoveUnreferencedInstructionReductionPassTest, Referenced) {
// A module with some unused global variables, constants, and types. Some will
// not be removed initially because of the OpDecorate instructions.
RemoveUnreferencedInstructionReductionOpportunityFinder finder(true);
const std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310 ; 1
OpName %4 "main" ; 2
OpName %12 "a" ; 3
OpDecorate %12 RelaxedPrecision ; 4
OpDecorate %13 RelaxedPrecision ; 5
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeBool
%7 = OpConstantTrue %6 ; 6
%10 = OpTypeInt 32 1
%11 = OpTypePointer Private %10
%12 = OpVariable %11 Private
%13 = OpConstant %10 1
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
CheckValid(kEnv, context.get());
auto ops = finder.GetAvailableOpportunities(context.get());
ASSERT_EQ(6, ops.size());
for (auto& op : ops) {
ASSERT_TRUE(op->PreconditionHolds());
op->TryToApply();
CheckValid(kEnv, context.get());
}
std::string after = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeBool ; 1
%10 = OpTypeInt 32 1
%11 = OpTypePointer Private %10
%12 = OpVariable %11 Private ; 2
%13 = OpConstant %10 1 ; 3
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(kEnv, after, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ASSERT_EQ(3, ops.size());
for (auto& op : ops) {
ASSERT_TRUE(op->PreconditionHolds());
op->TryToApply();
CheckValid(kEnv, context.get());
}
std::string after_2 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%10 = OpTypeInt 32 1
%11 = OpTypePointer Private %10 ; 1
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(kEnv, after_2, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
for (auto& op : ops) {
ASSERT_TRUE(op->PreconditionHolds());
op->TryToApply();
CheckValid(kEnv, context.get());
}
std::string after_3 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%10 = OpTypeInt 32 1 ; 1
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(kEnv, after_3, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
for (auto& op : ops) {
ASSERT_TRUE(op->PreconditionHolds());
op->TryToApply();
CheckValid(kEnv, context.get());
}
std::string after_4 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CheckEqual(kEnv, after_4, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ASSERT_EQ(0, ops.size());
}
} // namespace

View File

@ -308,12 +308,20 @@ int main(int argc, const char** argv) {
const auto reduction_status = reducer.Run(std::move(binary_in), &binary_out,
reducer_options, validator_options);
if (reduction_status == spvtools::reduce::Reducer::ReductionResultStatus::
kInitialStateNotInteresting ||
!WriteFile<uint32_t>(out_binary_file.c_str(), "wb", binary_out.data(),
// Always try to write the output file, even if the reduction failed.
if (!WriteFile<uint32_t>(out_binary_file.c_str(), "wb", binary_out.data(),
binary_out.size())) {
return 1;
}
return 0;
// These are the only successful statuses.
switch (reduction_status) {
case spvtools::reduce::Reducer::ReductionResultStatus::kComplete:
case spvtools::reduce::Reducer::ReductionResultStatus::kReachedStepLimit:
return 0;
default:
break;
}
return 1;
}