spirv-reduce: Support reducing a specific function (#3774)

Motivated by integrating spirv-reduce into spirv-fuzz, so that an
added function can be made smaller during shrinking, this adds support
in spirv-reduce for asking reduction to be restricted to the
instructions of a single specified function.
This commit is contained in:
Alastair Donaldson 2020-09-11 06:29:43 +01:00 committed by GitHub
parent de7d57984d
commit 5dcb576b69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 727 additions and 229 deletions

View File

@ -674,6 +674,13 @@ SPIRV_TOOLS_EXPORT void spvReducerOptionsSetStepLimit(
SPIRV_TOOLS_EXPORT void spvReducerOptionsSetFailOnValidationError(
spv_reducer_options options, bool fail_on_validation_error);
// Sets the function that the reducer should target. If set to zero the reducer
// will target all functions as well as parts of the module that lie outside
// functions. Otherwise the reducer will restrict reduction to the function
// with result id |target_function|, which is required to exist.
SPIRV_TOOLS_EXPORT void spvReducerOptionsSetTargetFunction(
spv_reducer_options options, uint32_t target_function);
// Creates a fuzzer options object with default options. Returns a valid
// options object. The object remains valid until it is passed into
// |spvFuzzerOptionsDestroy|.

View File

@ -202,6 +202,11 @@ class ReducerOptions {
fail_on_validation_error);
}
// See spvReducerOptionsSetTargetFunction.
void set_target_function(uint32_t target_function) {
spvReducerOptionsSetTargetFunction(options_, target_function);
}
private:
spv_reducer_options options_;
};

View File

@ -50,6 +50,7 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
operand_to_dominating_id_reduction_opportunity_finder.cpp
reducer.cpp
reduction_opportunity.cpp
reduction_opportunity_finder.cpp
reduction_pass.cpp
reduction_util.cpp
remove_block_reduction_opportunity.cpp

View File

@ -22,7 +22,8 @@ namespace reduce {
std::vector<std::unique_ptr<ReductionOpportunity>>
ConditionalBranchToSimpleConditionalBranchOpportunityFinder::
GetAvailableOpportunities(opt::IRContext* context) const {
GetAvailableOpportunities(opt::IRContext* context,
uint32_t target_function) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
// Find the opportunities for redirecting all false targets before the
@ -31,10 +32,10 @@ ConditionalBranchToSimpleConditionalBranchOpportunityFinder::
// reducer is improved by avoiding contiguous opportunities that disable one
// another.
for (bool redirect_to_true : {true, false}) {
// Consider every function.
for (auto& function : *context->module()) {
// Consider every relevant function.
for (auto* function : GetTargetFunctions(context, target_function)) {
// Consider every block in the function.
for (auto& block : function) {
for (auto& block : *function) {
// The terminator must be SpvOpBranchConditional.
opt::Instruction* terminator = block.terminator();
if (terminator->opcode() != SpvOpBranchConditional) {

View File

@ -26,7 +26,7 @@ class ConditionalBranchToSimpleConditionalBranchOpportunityFinder
: public ReductionOpportunityFinder {
public:
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const override;
opt::IRContext* context, uint32_t target_function) const override;
std::string GetName() const override;
};

View File

@ -25,17 +25,17 @@ std::string MergeBlocksReductionOpportunityFinder::GetName() const {
std::vector<std::unique_ptr<ReductionOpportunity>>
MergeBlocksReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
opt::IRContext* context, uint32_t target_function) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
// Consider every block in every function.
for (auto& function : *context->module()) {
for (auto& block : function) {
for (auto* function : GetTargetFunctions(context, target_function)) {
for (auto& block : *function) {
// See whether it is possible to merge this block with its successor.
if (opt::blockmergeutil::CanMergeWithSuccessor(context, &block)) {
// It is, so record an opportunity to do this.
result.push_back(spvtools::MakeUnique<MergeBlocksReductionOpportunity>(
context, &function, &block));
context, function, &block));
}
}
}

View File

@ -31,7 +31,7 @@ class MergeBlocksReductionOpportunityFinder
std::string GetName() const final;
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
opt::IRContext* context, uint32_t target_function) const final;
private:
};

View File

@ -22,7 +22,7 @@ namespace reduce {
std::vector<std::unique_ptr<ReductionOpportunity>>
OperandToConstReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
opt::IRContext* context, uint32_t target_function) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
assert(result.empty());
@ -35,8 +35,8 @@ OperandToConstReductionOpportunityFinder::GetAvailableOpportunities(
// contiguous blocks of opportunities early on, and we want to avoid having a
// large block of incompatible opportunities if possible.
for (const auto& constant : context->GetConstants()) {
for (auto& function : *context->module()) {
for (auto& block : function) {
for (auto* function : GetTargetFunctions(context, target_function)) {
for (auto& block : *function) {
for (auto& inst : block) {
// We iterate through the operands using an explicit index (rather
// than using a lambda) so that we use said index in the construction

View File

@ -33,7 +33,7 @@ class OperandToConstReductionOpportunityFinder
std::string GetName() const final;
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
opt::IRContext* context, uint32_t target_function) const final;
private:
};

View File

@ -22,7 +22,7 @@ namespace reduce {
std::vector<std::unique_ptr<ReductionOpportunity>>
OperandToDominatingIdReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
opt::IRContext* context, uint32_t target_function) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
// Go through every instruction in every block, considering it as a potential
@ -38,15 +38,15 @@ OperandToDominatingIdReductionOpportunityFinder::GetAvailableOpportunities(
// to prioritise replacing e with its smallest sub-expressions; generalising
// this idea to dominating ids this roughly corresponds to more distant
// dominators.
for (auto& function : *context->module()) {
for (auto dominating_block = function.begin();
dominating_block != function.end(); ++dominating_block) {
for (auto* function : GetTargetFunctions(context, target_function)) {
for (auto dominating_block = function->begin();
dominating_block != function->end(); ++dominating_block) {
for (auto& dominating_inst : *dominating_block) {
if (dominating_inst.HasResultId() && dominating_inst.type_id()) {
// Consider replacing any operand with matching type in a dominated
// instruction with the id generated by this instruction.
GetOpportunitiesForDominatingInst(
&result, &dominating_inst, dominating_block, &function, context);
&result, &dominating_inst, dominating_block, function, context);
}
}
}

View File

@ -40,7 +40,7 @@ class OperandToDominatingIdReductionOpportunityFinder
std::string GetName() const final;
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
opt::IRContext* context, uint32_t target_function) const final;
private:
void GetOpportunitiesForDominatingInst(

View File

@ -22,11 +22,11 @@ namespace reduce {
std::vector<std::unique_ptr<ReductionOpportunity>>
OperandToUndefReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
opt::IRContext* context, uint32_t target_function) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
for (auto& function : *context->module()) {
for (auto& block : function) {
for (auto* function : GetTargetFunctions(context, target_function)) {
for (auto& block : *function) {
for (auto& inst : block) {
// Skip instructions that result in a pointer type.
auto type_id = inst.type_id();

View File

@ -32,7 +32,7 @@ class OperandToUndefReductionOpportunityFinder
std::string GetName() const final;
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
opt::IRContext* context, uint32_t target_function) const final;
private:
};

View File

@ -183,7 +183,8 @@ Reducer::ReductionResultStatus Reducer::RunPasses(
consumer_(SPV_MSG_INFO, nullptr, {},
("Trying pass " + pass->GetName() + ".").c_str());
do {
auto maybe_result = pass->TryApplyReduction(*current_binary);
auto maybe_result =
pass->TryApplyReduction(*current_binary, options->target_function);
if (maybe_result.empty()) {
// For this round, the pass has no more opportunities (chunks) to
// apply, so move on to the next pass.

View File

@ -0,0 +1,34 @@
// Copyright (c) 2020 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 "reduction_opportunity_finder.h"
namespace spvtools {
namespace reduce {
std::vector<opt::Function*> ReductionOpportunityFinder::GetTargetFunctions(
opt::IRContext* ir_context, uint32_t target_function) {
std::vector<opt::Function*> result;
for (auto& function : *ir_context->module()) {
if (!target_function || function.result_id() == target_function) {
result.push_back(&function);
}
}
assert((!target_function || !result.empty()) &&
"Requested target function must exist.");
return result;
}
} // namespace reduce
} // namespace spvtools

View File

@ -15,6 +15,8 @@
#ifndef SOURCE_REDUCE_REDUCTION_OPPORTUNITY_FINDER_H_
#define SOURCE_REDUCE_REDUCTION_OPPORTUNITY_FINDER_H_
#include <vector>
#include "source/opt/ir_context.h"
#include "source/reduce/reduction_opportunity.h"
@ -29,12 +31,25 @@ class ReductionOpportunityFinder {
virtual ~ReductionOpportunityFinder() = default;
// Finds and returns the reduction opportunities relevant to this pass that
// could be applied to the given SPIR-V module.
// could be applied to SPIR-V module |context|.
//
// If |target_function| is non-zero then the available opportunities will be
// restricted to only those opportunities that modify the function with result
// id |target_function|.
virtual std::vector<std::unique_ptr<ReductionOpportunity>>
GetAvailableOpportunities(opt::IRContext* context) const = 0;
GetAvailableOpportunities(opt::IRContext* context,
uint32_t target_function) const = 0;
// Provides a name for the finder.
virtual std::string GetName() const = 0;
protected:
// Requires that |target_function| is zero or the id of a function in
// |ir_context|. If |target_function| is zero, returns all the functions in
// |ir_context|. Otherwise, returns the function with id |target_function|.
// This allows fuzzer passes to restrict attention to a single function.
static std::vector<opt::Function*> GetTargetFunctions(
opt::IRContext* ir_context, uint32_t target_function);
};
} // namespace reduce

View File

@ -22,7 +22,7 @@ namespace spvtools {
namespace reduce {
std::vector<uint32_t> ReductionPass::TryApplyReduction(
const std::vector<uint32_t>& binary) {
const std::vector<uint32_t>& binary, uint32_t target_function) {
// We represent modules as binaries because (a) attempts at reduction need to
// end up in binary form to be passed on to SPIR-V-consuming tools, and (b)
// when we apply a reduction step we need to do it on a fresh version of the
@ -34,7 +34,7 @@ std::vector<uint32_t> ReductionPass::TryApplyReduction(
assert(context);
std::vector<std::unique_ptr<ReductionOpportunity>> opportunities =
finder_->GetAvailableOpportunities(context.get());
finder_->GetAvailableOpportunities(context.get(), target_function);
// There is no point in having a granularity larger than the number of
// opportunities, so reduce the granularity in this case.

View File

@ -49,7 +49,12 @@ class ReductionPass {
// 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);
//
// If |target_function| is non-zero, only reduction opportunities that
// simplify the internals of the function with result id |target_function|
// will be applied.
std::vector<uint32_t> TryApplyReduction(const std::vector<uint32_t>& binary,
uint32_t target_function);
// Notifies the reduction pass whether the binary returned from
// TryApplyReduction is interesting, so that the next call to

View File

@ -25,15 +25,15 @@ std::string RemoveBlockReductionOpportunityFinder::GetName() const {
std::vector<std::unique_ptr<ReductionOpportunity>>
RemoveBlockReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
opt::IRContext* context, uint32_t target_function) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
// Consider every block in every function.
for (auto& function : *context->module()) {
for (auto bi = function.begin(); bi != function.end(); ++bi) {
if (IsBlockValidOpportunity(context, function, bi)) {
result.push_back(spvtools::MakeUnique<RemoveBlockReductionOpportunity>(
&function, &*bi));
// Consider every block in every relevant function.
for (auto* function : GetTargetFunctions(context, target_function)) {
for (auto bi = function->begin(); bi != function->end(); ++bi) {
if (IsBlockValidOpportunity(context, function, &bi)) {
result.push_back(
MakeUnique<RemoveBlockReductionOpportunity>(function, &*bi));
}
}
}
@ -41,22 +41,22 @@ RemoveBlockReductionOpportunityFinder::GetAvailableOpportunities(
}
bool RemoveBlockReductionOpportunityFinder::IsBlockValidOpportunity(
opt::IRContext* context, opt::Function& function,
opt::Function::iterator& bi) {
assert(bi != function.end() && "Block iterator was out of bounds");
opt::IRContext* context, opt::Function* function,
opt::Function::iterator* bi) {
assert(*bi != function->end() && "Block iterator was out of bounds");
// Don't remove first block; we don't want to end up with no blocks.
if (bi == function.begin()) {
if (*bi == function->begin()) {
return false;
}
// Don't remove blocks with references.
if (context->get_def_use_mgr()->NumUsers(bi->id()) > 0) {
if (context->get_def_use_mgr()->NumUsers((*bi)->id()) > 0) {
return false;
}
// Don't remove blocks whose instructions have outside references.
if (!BlockInstructionsHaveNoOutsideReferences(context, bi)) {
if (!BlockInstructionsHaveNoOutsideReferences(context, *bi)) {
return false;
}

View File

@ -34,14 +34,14 @@ class RemoveBlockReductionOpportunityFinder
std::string GetName() const final;
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
opt::IRContext* context, uint32_t target_function) const final;
private:
// Returns true if the block |bi| in function |function| is a valid
// opportunity according to various restrictions.
static bool IsBlockValidOpportunity(opt::IRContext* context,
opt::Function& function,
opt::Function::iterator& bi);
opt::Function* function,
opt::Function::iterator* bi);
// Returns true if the instructions (definitions) in block |bi| have no
// references, except for references from inside the block itself.

View File

@ -21,7 +21,14 @@ namespace reduce {
std::vector<std::unique_ptr<ReductionOpportunity>>
RemoveFunctionReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
opt::IRContext* context, uint32_t target_function) const {
if (target_function) {
// If we are targeting a specific function then we are only interested in
// opportunities that simplify the internals of that function; removing
// whole functions does not fit the bill.
return {};
}
std::vector<std::unique_ptr<ReductionOpportunity>> result;
// Consider each function.
for (auto& function : *context->module()) {

View File

@ -31,7 +31,7 @@ class RemoveFunctionReductionOpportunityFinder
std::string GetName() const final;
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
opt::IRContext* context, uint32_t target_function) const final;
private:
};

View File

@ -30,11 +30,11 @@ std::string RemoveSelectionReductionOpportunityFinder::GetName() const {
std::vector<std::unique_ptr<ReductionOpportunity>>
RemoveSelectionReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
opt::IRContext* context, uint32_t target_function) const {
// Get all loop merge and continue blocks so we can check for these later.
std::unordered_set<uint32_t> merge_and_continue_blocks_from_loops;
for (auto& function : *context->module()) {
for (auto& block : function) {
for (auto* function : GetTargetFunctions(context, target_function)) {
for (auto& block : *function) {
if (auto merge_instruction = block.GetMergeInst()) {
if (merge_instruction->opcode() == SpvOpLoopMerge) {
uint32_t merge_block_id =

View File

@ -33,7 +33,7 @@ class RemoveSelectionReductionOpportunityFinder
std::string GetName() const final;
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
opt::IRContext* context, uint32_t target_function) const final;
// Returns true if the OpSelectionMerge instruction |merge_instruction| in
// block |header_block| can be removed.

View File

@ -28,61 +28,72 @@ RemoveUnusedInstructionReductionOpportunityFinder::
std::vector<std::unique_ptr<ReductionOpportunity>>
RemoveUnusedInstructionReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
opt::IRContext* context, uint32_t target_function) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
for (auto& inst : context->module()->debugs1()) {
if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
continue;
if (!target_function) {
// We are not restricting reduction to a specific function, so we consider
// unused instructions defined outside functions.
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()->ext_inst_debuginfo()) {
if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
continue;
}
result.push_back(
MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}
for (auto& inst : context->module()->types_values()) {
if (!remove_constants_and_undefs_ &&
spvOpcodeIsConstantOrUndef(inst.opcode())) {
continue;
}
if (!OnlyReferencedByIntimateDecorationOrEntryPointInterface(context,
inst)) {
continue;
}
result.push_back(
MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}
for (auto& inst : context->module()->annotations()) {
if (context->get_def_use_mgr()->NumUsers(&inst) > 0) {
continue;
}
if (!IsIndependentlyRemovableDecoration(inst)) {
continue;
}
result.push_back(
MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}
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()->ext_inst_debuginfo()) {
if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
continue;
}
result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}
for (auto& inst : context->module()->types_values()) {
if (!remove_constants_and_undefs_ &&
spvOpcodeIsConstantOrUndef(inst.opcode())) {
continue;
}
if (!OnlyReferencedByIntimateDecorationOrEntryPointInterface(context,
inst)) {
continue;
}
result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}
for (auto& inst : context->module()->annotations()) {
if (context->get_def_use_mgr()->NumUsers(&inst) > 0) {
continue;
}
if (!IsIndependentlyRemovableDecoration(inst)) {
continue;
}
result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
}
for (auto& function : *context->module()) {
for (auto& block : function) {
for (auto* function : GetTargetFunctions(context, target_function)) {
for (auto& block : *function) {
for (auto& inst : block) {
if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
continue;

View File

@ -38,7 +38,7 @@ class RemoveUnusedInstructionReductionOpportunityFinder
std::string GetName() const final;
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
opt::IRContext* context, uint32_t target_function) const final;
private:
// Returns true if and only if the only uses of |inst| are by decorations that

View File

@ -24,7 +24,14 @@ namespace reduce {
std::vector<std::unique_ptr<ReductionOpportunity>>
RemoveUnusedStructMemberReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
opt::IRContext* context, uint32_t target_function) const {
if (target_function) {
// Removing an unused struct member is a global change, as struct types are
// global. We thus do not consider such opportunities if we are targeting
// a specific function.
return {};
}
std::vector<std::unique_ptr<ReductionOpportunity>> result;
// We track those struct members that are never accessed. We do this by

View File

@ -32,7 +32,7 @@ class RemoveUnusedStructMemberReductionOpportunityFinder
std::string GetName() const final;
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
opt::IRContext* context, uint32_t target_function) const final;
private:
// A helper method to update |unused_members_to_structs| by removing from it

View File

@ -22,13 +22,13 @@ namespace reduce {
std::vector<std::unique_ptr<ReductionOpportunity>>
SimpleConditionalBranchToBranchOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
opt::IRContext* context, uint32_t target_function) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
// Consider every function.
for (auto& function : *context->module()) {
for (auto* function : GetTargetFunctions(context, target_function)) {
// Consider every block in the function.
for (auto& block : function) {
for (auto& block : *function) {
// The terminator must be SpvOpBranchConditional.
opt::Instruction* terminator = block.terminator();
if (terminator->opcode() != SpvOpBranchConditional) {

View File

@ -26,7 +26,7 @@ class SimpleConditionalBranchToBranchOpportunityFinder
: public ReductionOpportunityFinder {
public:
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const override;
opt::IRContext* context, uint32_t target_function) const override;
std::string GetName() const override;
};

View File

@ -26,12 +26,12 @@ const uint32_t kContinueNodeIndex = 1;
std::vector<std::unique_ptr<ReductionOpportunity>>
StructuredLoopToSelectionReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
opt::IRContext* context, uint32_t target_function) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
std::set<uint32_t> merge_block_ids;
for (auto& function : *context->module()) {
for (auto& block : function) {
for (auto* function : GetTargetFunctions(context, target_function)) {
for (auto& block : *function) {
auto merge_block_id = block.MergeBlockIdIfAny();
if (merge_block_id) {
merge_block_ids.insert(merge_block_id);
@ -40,8 +40,8 @@ StructuredLoopToSelectionReductionOpportunityFinder::GetAvailableOpportunities(
}
// Consider each loop construct header in the module.
for (auto& function : *context->module()) {
for (auto& block : function) {
for (auto* function : GetTargetFunctions(context, target_function)) {
for (auto& block : *function) {
auto loop_merge_inst = block.GetLoopMergeInst();
if (!loop_merge_inst) {
// This is not a loop construct header.
@ -69,8 +69,8 @@ StructuredLoopToSelectionReductionOpportunityFinder::GetAvailableOpportunities(
// so we cautiously do not consider applying a transformation.
auto merge_block_id =
loop_merge_inst->GetSingleWordInOperand(kMergeNodeIndex);
if (!context->GetDominatorAnalysis(&function)->Dominates(
block.id(), merge_block_id)) {
if (!context->GetDominatorAnalysis(function)->Dominates(block.id(),
merge_block_id)) {
continue;
}
@ -78,7 +78,7 @@ StructuredLoopToSelectionReductionOpportunityFinder::GetAvailableOpportunities(
// construct header. If not (e.g. because the loop contains OpReturn,
// OpKill or OpUnreachable), we cautiously do not consider applying
// a transformation.
if (!context->GetPostDominatorAnalysis(&function)->Dominates(
if (!context->GetPostDominatorAnalysis(function)->Dominates(
merge_block_id, block.id())) {
continue;
}
@ -87,7 +87,7 @@ StructuredLoopToSelectionReductionOpportunityFinder::GetAvailableOpportunities(
// opportunity to do so.
result.push_back(
MakeUnique<StructuredLoopToSelectionReductionOpportunity>(
context, &block, &function));
context, &block, function));
}
}
return result;

View File

@ -46,7 +46,7 @@ class StructuredLoopToSelectionReductionOpportunityFinder
std::string GetName() const final;
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
opt::IRContext* context, uint32_t target_function) const final;
private:
};

View File

@ -23,7 +23,9 @@ const uint32_t kDefaultStepLimit = 2500;
} // namespace
spv_reducer_options_t::spv_reducer_options_t()
: step_limit(kDefaultStepLimit), fail_on_validation_error(false) {}
: step_limit(kDefaultStepLimit),
fail_on_validation_error(false),
target_function(0) {}
SPIRV_TOOLS_EXPORT spv_reducer_options spvReducerOptionsCreate() {
return new spv_reducer_options_t();
@ -42,3 +44,8 @@ SPIRV_TOOLS_EXPORT void spvReducerOptionsSetFailOnValidationError(
spv_reducer_options options, bool fail_on_validation_error) {
options->fail_on_validation_error = fail_on_validation_error;
}
SPIRV_TOOLS_EXPORT void spvReducerOptionsSetTargetFunction(
spv_reducer_options options, uint32_t target_function) {
options->target_function = target_function;
}

View File

@ -30,6 +30,9 @@ struct spv_reducer_options_t {
// See spvReducerOptionsSetFailOnValidationError.
bool fail_on_validation_error;
// See spvReducerOptionsSetTargetFunction.
uint32_t target_function;
};
#endif // SOURCE_SPIRV_REDUCER_OPTIONS_H_

View File

@ -80,7 +80,7 @@ TEST(ConditionalBranchToSimpleConditionalBranchTest, Diamond) {
CheckValid(kEnv, context.get());
auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(2, ops.size());
@ -125,7 +125,7 @@ TEST(ConditionalBranchToSimpleConditionalBranchTest, Diamond) {
}
ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(0, ops.size());
// Start again, and apply the other op.
@ -134,7 +134,7 @@ TEST(ConditionalBranchToSimpleConditionalBranchTest, Diamond) {
CheckValid(kEnv, context.get());
ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(2, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -225,7 +225,7 @@ TEST(ConditionalBranchToSimpleConditionalBranchTest, AlreadySimplified) {
CheckValid(kEnv, context.get());
auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -276,7 +276,7 @@ TEST(ConditionalBranchToSimpleConditionalBranchTest, DontRemoveBackEdge) {
CheckValid(kEnv, context.get());
auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -313,7 +313,7 @@ TEST(ConditionalBranchToSimpleConditionalBranchTest, DontRemoveBackEdge) {
CheckEqual(kEnv, after, context.get());
ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -361,7 +361,7 @@ TEST(ConditionalBranchToSimpleConditionalBranchTest,
CheckValid(kEnv, context.get());
auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -396,7 +396,7 @@ TEST(ConditionalBranchToSimpleConditionalBranchTest,
CheckEqual(kEnv, after, context.get());
ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -453,7 +453,7 @@ TEST(ConditionalBranchToSimpleConditionalBranchTest, BackEdgeUnreachable) {
CheckValid(kEnv, context.get());
auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -492,7 +492,7 @@ TEST(ConditionalBranchToSimpleConditionalBranchTest, BackEdgeUnreachable) {
CheckEqual(kEnv, after, context.get());
ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(0, ops.size());
}

View File

@ -66,7 +66,7 @@ TEST(MergeBlocksReductionPassTest, BasicCheck) {
BuildModule(env, consumer, shader, kReduceAssembleOption);
const auto ops =
MergeBlocksReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(5, ops.size());
// Try order 3, 0, 2, 4, 1
@ -356,7 +356,7 @@ TEST(MergeBlocksReductionPassTest, Loops) {
BuildModule(env, consumer, shader, kReduceAssembleOption);
const auto ops =
MergeBlocksReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(11, ops.size());
for (auto& ri : ops) {
@ -474,7 +474,7 @@ TEST(MergeBlocksReductionPassTest, MergeWithOpPhi) {
BuildModule(env, consumer, shader, kReduceAssembleOption);
const auto ops =
MergeBlocksReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -556,7 +556,7 @@ void MergeBlocksReductionPassTest_LoopReturn_Helper(bool reverse) {
ASSERT_NE(context.get(), nullptr);
auto opportunities =
MergeBlocksReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
// A->B and B->C
ASSERT_EQ(opportunities.size(), 2);

View File

@ -101,7 +101,7 @@ TEST(OperandToConstantReductionPassTest, BasicCheck) {
BuildModule(env, consumer, original, kReduceAssembleOption);
const auto ops =
OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(17, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
@ -151,10 +151,158 @@ TEST(OperandToConstantReductionPassTest, WithCalledFunction) {
BuildModule(env, consumer, shader, kReduceAssembleOption);
const auto ops =
OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(0, ops.size());
}
TEST(OperandToConstantReductionPassTest, TargetSpecificFunction) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %6 %7
%17 = OpConstant %6 1
%20 = OpConstant %6 2
%23 = OpConstant %6 0
%24 = OpTypeBool
%35 = OpConstant %6 3
%53 = OpConstant %6 10
%4 = OpFunction %2 None %3
%5 = OpLabel
%65 = OpVariable %7 Function
%68 = OpVariable %7 Function
%73 = OpVariable %7 Function
OpStore %65 %35
%66 = OpLoad %6 %65
%67 = OpIAdd %6 %66 %17
OpStore %65 %67
%69 = OpLoad %6 %65
OpStore %68 %69
%70 = OpFunctionCall %6 %13 %68
%71 = OpLoad %6 %65
%72 = OpIAdd %6 %71 %70
OpStore %65 %72
%74 = OpLoad %6 %65
OpStore %73 %74
%75 = OpFunctionCall %6 %10 %73
%76 = OpLoad %6 %65
%77 = OpIAdd %6 %76 %75
OpStore %65 %77
OpReturn
OpFunctionEnd
%10 = OpFunction %6 None %8
%9 = OpFunctionParameter %7
%11 = OpLabel
%15 = OpVariable %7 Function
%16 = OpLoad %6 %9
%18 = OpIAdd %6 %16 %17
OpStore %15 %18
%19 = OpLoad %6 %15
%21 = OpIAdd %6 %19 %20
OpStore %15 %21
%22 = OpLoad %6 %15
%25 = OpSGreaterThan %24 %22 %23
OpSelectionMerge %27 None
OpBranchConditional %25 %26 %27
%26 = OpLabel
%28 = OpLoad %6 %9
OpReturnValue %28
%27 = OpLabel
%30 = OpLoad %6 %9
%31 = OpIAdd %6 %30 %17
OpReturnValue %31
OpFunctionEnd
%13 = OpFunction %6 None %8
%12 = OpFunctionParameter %7
%14 = OpLabel
%41 = OpVariable %7 Function
%46 = OpVariable %7 Function
%55 = OpVariable %7 Function
%34 = OpLoad %6 %12
%36 = OpIEqual %24 %34 %35
OpSelectionMerge %38 None
OpBranchConditional %36 %37 %38
%37 = OpLabel
%39 = OpLoad %6 %12
%40 = OpIMul %6 %20 %39
OpStore %41 %40
%42 = OpFunctionCall %6 %10 %41
OpReturnValue %42
%38 = OpLabel
%44 = OpLoad %6 %12
%45 = OpIAdd %6 %44 %17
OpStore %12 %45
OpStore %46 %23
OpBranch %47
%47 = OpLabel
OpLoopMerge %49 %50 None
OpBranch %51
%51 = OpLabel
%52 = OpLoad %6 %46
%54 = OpSLessThan %24 %52 %53
OpBranchConditional %54 %48 %49
%48 = OpLabel
%56 = OpLoad %6 %12
OpStore %55 %56
%57 = OpFunctionCall %6 %10 %55
%58 = OpLoad %6 %12
%59 = OpIAdd %6 %58 %57
OpStore %12 %59
OpBranch %50
%50 = OpLabel
%60 = OpLoad %6 %46
%61 = OpIAdd %6 %60 %17
OpStore %46 %61
OpBranch %47
%49 = OpLabel
%62 = OpLoad %6 %12
OpReturnValue %62
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, shader, kReduceAssembleOption);
// Targeting all functions, there are quite a few opportunities. To avoid
// making the test too sensitive, we check that there are more than a number
// somewhat lower than the real number.
const auto all_ops =
OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
context.get(), 0);
ASSERT_TRUE(all_ops.size() > 100);
// Targeting individual functions, there are fewer opportunities. Again, we
// avoid checking against an exact number so that the test is not too
// sensitive.
const auto ops_for_function_4 =
OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
context.get(), 4);
const auto ops_for_function_10 =
OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
context.get(), 10);
const auto ops_for_function_13 =
OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
context.get(), 13);
ASSERT_TRUE(ops_for_function_4.size() < 60);
ASSERT_TRUE(ops_for_function_10.size() < 50);
ASSERT_TRUE(ops_for_function_13.size() < 80);
// The total number of opportunities should be the sum of the per-function
// opportunities.
ASSERT_EQ(all_ops.size(), ops_for_function_4.size() +
ops_for_function_10.size() +
ops_for_function_13.size());
}
} // namespace
} // namespace reduce
} // namespace spvtools

View File

@ -56,7 +56,7 @@ TEST(OperandToDominatingIdReductionPassTest, BasicCheck) {
const auto context =
BuildModule(env, consumer, original, kReduceAssembleOption);
const auto ops = OperandToDominatingIdReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(10, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();

View File

@ -167,7 +167,7 @@ TEST(OperandToUndefReductionPassTest, BasicCheck) {
BuildModule(env, consumer, original, kReduceAssembleOption);
const auto ops =
OperandToUndefReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(10, ops.size());
@ -221,7 +221,7 @@ TEST(OperandToUndefReductionPassTest, WithCalledFunction) {
BuildModule(env, consumer, shader, kReduceAssembleOption);
const auto ops =
OperandToUndefReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(0, ops.size());
}

View File

@ -14,6 +14,8 @@
#include "source/reduce/reducer.h"
#include <unordered_map>
#include "source/opt/build_module.h"
#include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h"
@ -23,11 +25,8 @@ 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;
const MessageConsumer kMessageConsumer = NopDiagnostic;
// 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
@ -42,7 +41,7 @@ class PingPongInteresting {
always_interesting_after_(always_interesting_after),
count_(0) {}
bool IsInteresting(const std::vector<uint32_t>&) {
bool IsInteresting() {
bool result;
if (count_ > always_interesting_after_) {
result = true;
@ -194,10 +193,10 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
Reducer reducer(kEnv);
PingPongInteresting ping_pong_interesting(10);
reducer.SetMessageConsumer(NopDiagnostic);
reducer.SetMessageConsumer(kMessageConsumer);
reducer.SetInterestingnessFunction(
[&](const std::vector<uint32_t>& binary, uint32_t) -> bool {
return ping_pong_interesting.IsInteresting(binary);
[&ping_pong_interesting](const std::vector<uint32_t>&, uint32_t) -> bool {
return ping_pong_interesting.IsInteresting();
});
reducer.AddReductionPass(
MakeUnique<RemoveUnusedInstructionReductionOpportunityFinder>(false));
@ -230,13 +229,14 @@ bool InterestingWhileOpcodeExists(const std::vector<uint32_t>& binary,
DumpShader(binary, ss.str().c_str());
}
std::unique_ptr<IRContext> context =
std::unique_ptr<opt::IRContext> context =
BuildModule(kEnv, kMessageConsumer, binary.data(), binary.size());
assert(context);
bool interesting = false;
for (auto& function : *context->module()) {
context->cfg()->ForEachBlockInPostOrder(
&*function.begin(), [opcode, &interesting](BasicBlock* block) -> void {
&*function.begin(),
[opcode, &interesting](opt::BasicBlock* block) -> void {
for (auto& inst : *block) {
if (inst.opcode() == opcode) {
interesting = true;
@ -369,6 +369,147 @@ const std::string kShaderWithLoopsDivAndMul = R"(
OpFunctionEnd
)";
// The shader below comes from the following GLSL.
// #version 320 es
//
// int baz(int x) {
// int y = x + 1;
// y = y + 2;
// if (y > 0) {
// return x;
// }
// return x + 1;
// }
//
// int bar(int a) {
// if (a == 3) {
// return baz(2*a);
// }
// a = a + 1;
// for (int i = 0; i < 10; i++) {
// a += baz(a);
// }
// return a;
// }
//
// void main() {
// int x;
// x = 3;
// x += 1;
// x += bar(x);
// x += baz(x);
// }
const std::string kShaderWithMultipleFunctions = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %6 %7
%17 = OpConstant %6 1
%20 = OpConstant %6 2
%23 = OpConstant %6 0
%24 = OpTypeBool
%35 = OpConstant %6 3
%53 = OpConstant %6 10
%4 = OpFunction %2 None %3
%5 = OpLabel
%65 = OpVariable %7 Function
%68 = OpVariable %7 Function
%73 = OpVariable %7 Function
OpStore %65 %35
%66 = OpLoad %6 %65
%67 = OpIAdd %6 %66 %17
OpStore %65 %67
%69 = OpLoad %6 %65
OpStore %68 %69
%70 = OpFunctionCall %6 %13 %68
%71 = OpLoad %6 %65
%72 = OpIAdd %6 %71 %70
OpStore %65 %72
%74 = OpLoad %6 %65
OpStore %73 %74
%75 = OpFunctionCall %6 %10 %73
%76 = OpLoad %6 %65
%77 = OpIAdd %6 %76 %75
OpStore %65 %77
OpReturn
OpFunctionEnd
%10 = OpFunction %6 None %8
%9 = OpFunctionParameter %7
%11 = OpLabel
%15 = OpVariable %7 Function
%16 = OpLoad %6 %9
%18 = OpIAdd %6 %16 %17
OpStore %15 %18
%19 = OpLoad %6 %15
%21 = OpIAdd %6 %19 %20
OpStore %15 %21
%22 = OpLoad %6 %15
%25 = OpSGreaterThan %24 %22 %23
OpSelectionMerge %27 None
OpBranchConditional %25 %26 %27
%26 = OpLabel
%28 = OpLoad %6 %9
OpReturnValue %28
%27 = OpLabel
%30 = OpLoad %6 %9
%31 = OpIAdd %6 %30 %17
OpReturnValue %31
OpFunctionEnd
%13 = OpFunction %6 None %8
%12 = OpFunctionParameter %7
%14 = OpLabel
%41 = OpVariable %7 Function
%46 = OpVariable %7 Function
%55 = OpVariable %7 Function
%34 = OpLoad %6 %12
%36 = OpIEqual %24 %34 %35
OpSelectionMerge %38 None
OpBranchConditional %36 %37 %38
%37 = OpLabel
%39 = OpLoad %6 %12
%40 = OpIMul %6 %20 %39
OpStore %41 %40
%42 = OpFunctionCall %6 %10 %41
OpReturnValue %42
%38 = OpLabel
%44 = OpLoad %6 %12
%45 = OpIAdd %6 %44 %17
OpStore %12 %45
OpStore %46 %23
OpBranch %47
%47 = OpLabel
OpLoopMerge %49 %50 None
OpBranch %51
%51 = OpLabel
%52 = OpLoad %6 %46
%54 = OpSLessThan %24 %52 %53
OpBranchConditional %54 %48 %49
%48 = OpLabel
%56 = OpLoad %6 %12
OpStore %55 %56
%57 = OpFunctionCall %6 %10 %55
%58 = OpLoad %6 %12
%59 = OpIAdd %6 %58 %57
OpStore %12 %59
OpBranch %50
%50 = OpLabel
%60 = OpLoad %6 %46
%61 = OpIAdd %6 %60 %17
OpStore %46 %61
OpBranch %47
%49 = OpLabel
%62 = OpLoad %6 %12
OpReturnValue %62
OpFunctionEnd
)";
TEST(ReducerTest, ShaderReduceWhileMulReachable) {
Reducer reducer(kEnv);
@ -417,6 +558,70 @@ TEST(ReducerTest, ShaderReduceWhileDivReachable) {
ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
}
// Computes an instruction count for each function in the module represented by
// |binary|.
std::unordered_map<uint32_t, uint32_t> GetFunctionInstructionCount(
const std::vector<uint32_t>& binary) {
std::unique_ptr<opt::IRContext> context =
BuildModule(kEnv, kMessageConsumer, binary.data(), binary.size());
assert(context != nullptr && "Failed to build module.");
std::unordered_map<uint32_t, uint32_t> result;
for (auto& function : *context->module()) {
uint32_t& count = result[function.result_id()] = 0;
function.ForEachInst([&count](opt::Instruction*) { count++; });
}
return result;
}
TEST(ReducerTest, SingleFunctionReduction) {
Reducer reducer(kEnv);
PingPongInteresting ping_pong_interesting(4);
reducer.SetInterestingnessFunction(
[&ping_pong_interesting](const std::vector<uint32_t>&, uint32_t) -> bool {
return ping_pong_interesting.IsInteresting();
});
reducer.AddDefaultReductionPasses();
reducer.SetMessageConsumer(kMessageConsumer);
std::vector<uint32_t> binary_in;
SpirvTools t(kEnv);
ASSERT_TRUE(t.Assemble(kShaderWithMultipleFunctions, &binary_in,
kReduceAssembleOption));
auto original_instruction_count = GetFunctionInstructionCount(binary_in);
std::vector<uint32_t> binary_out;
spvtools::ReducerOptions reducer_options;
reducer_options.set_step_limit(500);
reducer_options.set_fail_on_validation_error(true);
// Instruct the reducer to only target function 13.
reducer_options.set_target_function(13);
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);
auto final_instruction_count = GetFunctionInstructionCount(binary_out);
// Nothing should have been removed from these functions.
ASSERT_EQ(original_instruction_count.at(4), final_instruction_count.at(4));
ASSERT_EQ(original_instruction_count.at(10), final_instruction_count.at(10));
// Function 13 should have been reduced to these five instructions:
// OpFunction
// OpFunctionParameter
// OpLabel
// OpReturnValue
// OpFunctionEnd
ASSERT_EQ(5, final_instruction_count.at(13));
}
} // namespace
} // namespace reduce
} // namespace spvtools

View File

@ -66,7 +66,7 @@ TEST(RemoveBlockReductionPassTest, BasicCheck) {
BuildModule(env, consumer, shader, kReduceAssembleOption);
const auto ops =
RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(2, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -181,7 +181,7 @@ TEST(RemoveBlockReductionPassTest, UnreachableContinueAndMerge) {
BuildModule(env, consumer, shader, kReduceAssembleOption);
const auto ops =
RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -209,7 +209,7 @@ TEST(RemoveBlockReductionPassTest, OneBlock) {
BuildModule(env, consumer, shader, kReduceAssembleOption);
const auto ops =
RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -247,7 +247,7 @@ TEST(RemoveBlockReductionPassTest, UnreachableBlocksWithOutsideIdUses) {
BuildModule(env, consumer, shader, kReduceAssembleOption);
const auto ops =
RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -287,7 +287,7 @@ TEST(RemoveBlockReductionPassTest, UnreachableBlocksWithInsideIdUses) {
const auto context =
BuildModule(env, consumer, shader, kReduceAssembleOption);
auto ops = RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -323,7 +323,7 @@ TEST(RemoveBlockReductionPassTest, UnreachableBlocksWithInsideIdUses) {
// removed.
ops = RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(1, ops.size());

View File

@ -67,7 +67,7 @@ TEST(RemoveFunctionTest, BasicCheck) {
auto ops =
RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -97,7 +97,7 @@ TEST(RemoveFunctionTest, BasicCheck) {
CheckEqual(env, after_first, context.get());
ops = RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -156,7 +156,7 @@ TEST(RemoveFunctionTest, NothingToRemove) {
BuildModule(env, consumer, shader, kReduceAssembleOption);
auto ops =
RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -193,7 +193,7 @@ TEST(RemoveFunctionTest, TwoRemovableFunctions) {
auto ops =
RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(2, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -254,7 +254,7 @@ TEST(RemoveFunctionTest, NoRemovalsDueToOpName) {
BuildModule(env, consumer, shader, kReduceAssembleOption);
auto ops =
RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -286,7 +286,7 @@ TEST(RemoveFunctionTest, NoRemovalDueToLinkageDecoration) {
BuildModule(env, consumer, shader, kReduceAssembleOption);
auto ops =
RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(0, ops.size());
}

View File

@ -62,7 +62,7 @@ TEST(RemoveSelectionTest, OpportunityBecauseSameTargetBlock) {
auto ops =
RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -96,7 +96,7 @@ TEST(RemoveSelectionTest, OpportunityBecauseSameTargetBlock) {
CheckEqual(env, after, context.get());
ops = RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -136,7 +136,7 @@ TEST(RemoveSelectionTest, OpportunityBecauseSameTargetBlockMerge) {
auto ops =
RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -168,7 +168,7 @@ TEST(RemoveSelectionTest, OpportunityBecauseSameTargetBlockMerge) {
CheckEqual(env, after, context.get());
ops = RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -212,7 +212,7 @@ TEST(RemoveSelectionTest, NoOpportunityBecauseDifferentTargetBlocksOneMerge) {
auto ops =
RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -258,7 +258,7 @@ TEST(RemoveSelectionTest, NoOpportunityBecauseDifferentTargetBlocks) {
auto ops =
RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -306,7 +306,7 @@ TEST(RemoveSelectionTest, NoOpportunityBecauseMergeUsed) {
auto ops =
RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -384,7 +384,7 @@ TEST(RemoveSelectionTest, OpportunityBecauseLoopMergeUsed) {
auto ops =
RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -427,7 +427,7 @@ TEST(RemoveSelectionTest, OpportunityBecauseLoopMergeUsed) {
CheckEqual(env, after, context.get());
ops = RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -505,7 +505,7 @@ TEST(RemoveSelectionTest, OpportunityBecauseLoopContinueUsed) {
auto ops =
RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -548,7 +548,7 @@ TEST(RemoveSelectionTest, OpportunityBecauseLoopContinueUsed) {
CheckEqual(env, after, context.get());
ops = RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
context.get(), 0);
ASSERT_EQ(0, ops.size());
}

View File

@ -72,7 +72,7 @@ TEST(RemoveUnusedInstructionReductionPassTest, RemoveStores) {
CheckValid(kEnv, context.get());
auto ops = finder.GetAvailableOpportunities(context.get());
auto ops = finder.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(10, ops.size());
@ -108,7 +108,7 @@ TEST(RemoveUnusedInstructionReductionPassTest, RemoveStores) {
CheckEqual(kEnv, step_2, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ops = finder.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(7, ops.size());
@ -137,7 +137,7 @@ TEST(RemoveUnusedInstructionReductionPassTest, RemoveStores) {
CheckEqual(kEnv, step_3, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ops = finder.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -165,7 +165,7 @@ TEST(RemoveUnusedInstructionReductionPassTest, RemoveStores) {
CheckEqual(kEnv, step_4, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ops = finder.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -192,7 +192,7 @@ TEST(RemoveUnusedInstructionReductionPassTest, RemoveStores) {
CheckEqual(kEnv, step_5, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ops = finder.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -218,7 +218,7 @@ TEST(RemoveUnusedInstructionReductionPassTest, RemoveStores) {
CheckEqual(kEnv, step_6, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ops = finder.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -258,7 +258,7 @@ TEST(RemoveUnusedInstructionReductionPassTest, Referenced) {
CheckValid(kEnv, context.get());
auto ops = finder.GetAvailableOpportunities(context.get());
auto ops = finder.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(6, ops.size());
@ -289,7 +289,7 @@ TEST(RemoveUnusedInstructionReductionPassTest, Referenced) {
CheckEqual(kEnv, after, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ops = finder.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(3, ops.size());
@ -317,7 +317,7 @@ TEST(RemoveUnusedInstructionReductionPassTest, Referenced) {
CheckEqual(kEnv, after_2, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ops = finder.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -344,7 +344,7 @@ TEST(RemoveUnusedInstructionReductionPassTest, Referenced) {
CheckEqual(kEnv, after_3, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ops = finder.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -370,7 +370,7 @@ TEST(RemoveUnusedInstructionReductionPassTest, Referenced) {
CheckEqual(kEnv, after_4, context.get());
ops = finder.GetAvailableOpportunities(context.get());
ops = finder.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -438,7 +438,7 @@ TEST(RemoveUnusedResourceVariableTest, RemoveUnusedResourceVariables) {
BuildModule(env, consumer, shader, kReduceAssembleOption);
auto ops = RemoveUnusedInstructionReductionOpportunityFinder(true)
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(7, ops.size());
for (auto& op : ops) {
@ -487,7 +487,7 @@ TEST(RemoveUnusedResourceVariableTest, RemoveUnusedResourceVariables) {
CheckEqual(env, expected_1, context.get());
ops = RemoveUnusedInstructionReductionOpportunityFinder(true)
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(6, ops.size());
for (auto& op : ops) {
@ -530,7 +530,7 @@ TEST(RemoveUnusedResourceVariableTest, RemoveUnusedResourceVariables) {
CheckEqual(env, expected_2, context.get());
ops = RemoveUnusedInstructionReductionOpportunityFinder(true)
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(6, ops.size());
for (auto& op : ops) {

View File

@ -61,7 +61,7 @@ TEST(RemoveUnusedStructMemberTest, RemoveOneMember) {
BuildModule(env, consumer, shader, kReduceAssembleOption);
auto ops = RemoveUnusedStructMemberReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
@ -143,7 +143,7 @@ TEST(RemoveUnusedStructMemberTest, RemoveUniformBufferMember) {
BuildModule(env, consumer, shader, kReduceAssembleOption);
auto ops = RemoveUnusedStructMemberReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
@ -229,7 +229,7 @@ TEST(RemoveUnusedStructMemberTest, DoNotRemoveNamedMemberRemoveOneMember) {
BuildModule(env, consumer, shader, kReduceAssembleOption);
auto ops = RemoveUnusedStructMemberReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(0, ops.size());
}

View File

@ -73,7 +73,7 @@ TEST(SimpleConditionalBranchToBranchTest, Diamond) {
CheckValid(kEnv, context.get());
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -122,7 +122,7 @@ TEST(SimpleConditionalBranchToBranchTest, DiamondNoSelection) {
CheckValid(kEnv, context.get());
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -161,7 +161,7 @@ TEST(SimpleConditionalBranchToBranchTest, DiamondNoSelection) {
CheckEqual(kEnv, after, context.get());
ops = SimpleConditionalBranchToBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -217,7 +217,7 @@ TEST(SimpleConditionalBranchToBranchTest, ConditionalBranchesButNotSimple) {
CheckValid(kEnv, context.get());
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -266,7 +266,7 @@ TEST(SimpleConditionalBranchToBranchTest, SimplifyBackEdge) {
CheckValid(kEnv, context.get());
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -303,7 +303,7 @@ TEST(SimpleConditionalBranchToBranchTest, SimplifyBackEdge) {
CheckEqual(kEnv, after, context.get());
ops = SimpleConditionalBranchToBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -349,7 +349,7 @@ TEST(SimpleConditionalBranchToBranchTest,
CheckValid(kEnv, context.get());
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -384,7 +384,7 @@ TEST(SimpleConditionalBranchToBranchTest,
CheckEqual(kEnv, after, context.get());
ops = SimpleConditionalBranchToBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -438,7 +438,7 @@ TEST(SimpleConditionalBranchToBranchTest, BackEdgeUnreachable) {
CheckValid(kEnv, context.get());
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
@ -477,7 +477,7 @@ TEST(SimpleConditionalBranchToBranchTest, BackEdgeUnreachable) {
CheckEqual(kEnv, after, context.get());
ops = SimpleConditionalBranchToBranchOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(0, ops.size());
}

View File

@ -65,7 +65,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, LoopyShader1) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -211,7 +211,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, LoopyShader2) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(4, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -680,7 +680,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, LoopyShader3) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(0, ops.size());
}
@ -758,7 +758,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, LoopyShader4) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
// Initially there are two opportunities.
ASSERT_EQ(2, ops.size());
@ -881,7 +881,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, ConditionalBreak1) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -956,7 +956,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, ConditionalBreak2) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -1024,7 +1024,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, UnconditionalBreak) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -1224,7 +1224,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, Complex) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(2, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -1691,7 +1691,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, ComplexOptimized) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(2, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -2008,7 +2008,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, DominanceIssue) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -2156,7 +2156,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, AccessChainIssue) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -2313,7 +2313,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, DominanceAndPhiIssue) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -2421,7 +2421,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, OpLineBeforeOpPhi) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -2511,7 +2511,7 @@ TEST(StructuredLoopToSelectionReductionPassTest,
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
// There should be no opportunities.
ASSERT_EQ(0, ops.size());
@ -2555,7 +2555,7 @@ TEST(StructuredLoopToSelectionReductionPassTest,
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
// There should be no opportunities.
ASSERT_EQ(0, ops.size());
@ -2595,7 +2595,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, ContinueTargetIsSwitchTarget) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -2670,7 +2670,7 @@ TEST(StructuredLoopToSelectionReductionPassTest,
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -2733,7 +2733,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, LoopBranchesStraightToMerge) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -2790,7 +2790,7 @@ TEST(StructuredLoopToSelectionReductionPassTest,
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -2874,7 +2874,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, MultipleAccessChains) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -2970,7 +2970,7 @@ TEST(StructuredLoopToSelectionReductionPassTest,
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(2, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -3089,7 +3089,7 @@ TEST(StructuredLoopToSelectionReductionPassTest,
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(2, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -3209,7 +3209,7 @@ TEST(StructuredLoopToSelectionReductionPassTest,
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
// We cannot transform the inner loop due to its header jumping straight to
// the outer loop merge (the inner loop's merge does not post-dominate its
@ -3254,7 +3254,7 @@ TEST(StructuredLoopToSelectionReductionPassTest,
// Now look again for more opportunities.
ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
// What was the inner loop should now be transformable, as the jump to the
// outer loop's merge has been redirected.
@ -3422,7 +3422,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, LongAccessChains) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -3513,7 +3513,7 @@ TEST(StructuredLoopToSelectionReductionPassTest, LoopyShaderWithOpDecorate) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@ -3619,7 +3619,7 @@ TEST(StructuredLoopToSelectionReductionPassTest,
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
.GetAvailableOpportunities(context.get(), 0);
ASSERT_EQ(0, ops.size());
}

View File

@ -22,8 +22,8 @@ namespace reduce {
namespace {
using opt::Function;
using opt::Instruction;
using opt::IRContext;
using opt::Instruction;
// A reduction opportunity finder that finds opportunities to remove global
// values regardless of whether they are referenced. This is very likely to make
@ -43,7 +43,7 @@ class BlindlyRemoveGlobalValuesReductionOpportunityFinder
// referenced (directly or indirectly) from elsewhere in the module, each such
// opportunity will make the module invalid.
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
IRContext* context) const final {
IRContext* context, uint32_t /*unused*/) const final {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
for (auto& inst : context->module()->types_values()) {
if (inst.HasResultId()) {
@ -101,7 +101,7 @@ class OpVariableDuplicatorReductionOpportunityFinder
}
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
IRContext* context) const final {
IRContext* context, uint32_t /*unused*/) const final {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
for (auto& function : *context->module()) {
Instruction* first_instruction = &*function.begin()[0].begin();

View File

@ -16,6 +16,7 @@
#include <cerrno>
#include <cstring>
#include <functional>
#include <sstream>
#include "source/opt/build_module.h"
#include "source/opt/ir_context.h"
@ -102,6 +103,14 @@ Options (in lexicographical order):
--step-limit=
32-bit unsigned integer specifying maximum number of steps the
reducer will take before giving up.
--target-function=
32-bit unsigned integer specifying the id of a function in the
input module. The reducer will restrict attention to this
function, and will not make changes to other functions or to
instructions outside of functions, except that some global
instructions may be added in support of reducing the target
function. If 0 is specified (the default) then all functions are
reduced.
--temp-file-prefix=
Specifies a temporary file prefix that will be used to output
temporary shader files during reduction. A number and .spv
@ -169,6 +178,15 @@ ReduceStatus ParseFlags(int argc, const char** argv,
static_cast<uint32_t>(strtol(split_flag.second.c_str(), &end, 10));
assert(end != split_flag.second.c_str() && errno == 0);
reducer_options->set_step_limit(step_limit);
} else if (0 == strncmp(cur_arg, "--target-function=",
sizeof("--target-function=") - 1)) {
const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
char* end = nullptr;
errno = 0;
const auto target_function =
static_cast<uint32_t>(strtol(split_flag.second.c_str(), &end, 10));
assert(end != split_flag.second.c_str() && errno == 0);
reducer_options->set_target_function(target_function);
} else if (0 == strcmp(cur_arg, "--fail-on-validation-error")) {
reducer_options->set_fail_on_validation_error(true);
} else if (0 == strcmp(cur_arg, "--before-hlsl-legalization")) {
@ -304,6 +322,29 @@ int main(int argc, const char** argv) {
return 1;
}
const uint32_t target_function = (*reducer_options).target_function;
if (target_function) {
// A target function was specified; check that it exists.
std::unique_ptr<spvtools::opt::IRContext> context = spvtools::BuildModule(
kDefaultEnvironment, spvtools::utils::CLIMessageConsumer,
binary_in.data(), binary_in.size());
bool found_target_function = false;
for (auto& function : *context->module()) {
if (function.result_id() == target_function) {
found_target_function = true;
break;
}
}
if (!found_target_function) {
std::stringstream strstr;
strstr << "Target function with id " << target_function
<< " was requested, but not found in the module; stopping.";
spvtools::utils::CLIMessageConsumer(SPV_MSG_ERROR, nullptr, {},
strstr.str().c_str());
return 1;
}
}
std::vector<uint32_t> binary_out;
const auto reduction_status = reducer.Run(std::move(binary_in), &binary_out,
reducer_options, validator_options);