Skip to content

Commit

Permalink
spirv-reduce: Support reducing a specific function (#3774)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
afd authored Sep 11, 2020
1 parent de7d579 commit 5dcb576
Show file tree
Hide file tree
Showing 49 changed files with 717 additions and 219 deletions.
7 changes: 7 additions & 0 deletions include/spirv-tools/libspirv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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|.
Expand Down
5 changes: 5 additions & 0 deletions include/spirv-tools/libspirv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
};
Expand Down
1 change: 1 addition & 0 deletions source/reduce/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
8 changes: 4 additions & 4 deletions source/reduce/merge_blocks_reduction_opportunity_finder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion source/reduce/merge_blocks_reduction_opportunity_finder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
};
Expand Down
3 changes: 2 additions & 1 deletion source/reduce/reducer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
34 changes: 34 additions & 0 deletions source/reduce/reduction_opportunity_finder.cpp
Original file line number Diff line number Diff line change
@@ -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
19 changes: 17 additions & 2 deletions source/reduce/reduction_opportunity_finder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions source/reduce/reduction_pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down
7 changes: 6 additions & 1 deletion source/reduce/reduction_pass.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 13 additions & 13 deletions source/reduce/remove_block_reduction_opportunity_finder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,38 +25,38 @@ 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));
}
}
}
return result;
}

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;
}

Expand Down
6 changes: 3 additions & 3 deletions source/reduce/remove_block_reduction_opportunity_finder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
};
Expand Down
Loading

0 comments on commit 5dcb576

Please sign in to comment.