mirror of
https://github.com/RPCSX/SPIRV-Tools.git
synced 2024-11-23 11:29:44 +00:00
Initial commit for spirv-reduce. (#2056)
Creates a new tool that can be used to reduce failing testcases, similar to creduce.
This commit is contained in:
parent
fe39a6f342
commit
f3acb955c2
4
.gitignore
vendored
4
.gitignore
vendored
@ -19,3 +19,7 @@ compile_commands.json
|
||||
# Vim
|
||||
[._]*.s[a-w][a-z]
|
||||
*~
|
||||
|
||||
# C-Lion
|
||||
.idea
|
||||
cmake-build-debug
|
@ -368,6 +368,8 @@ typedef struct spv_validator_options_t spv_validator_options_t;
|
||||
|
||||
typedef struct spv_optimizer_options_t spv_optimizer_options_t;
|
||||
|
||||
typedef struct spv_reducer_options_t spv_reducer_options_t;
|
||||
|
||||
// Type Definitions
|
||||
|
||||
typedef spv_const_binary_t* spv_const_binary;
|
||||
@ -381,6 +383,8 @@ typedef spv_validator_options_t* spv_validator_options;
|
||||
typedef const spv_validator_options_t* spv_const_validator_options;
|
||||
typedef spv_optimizer_options_t* spv_optimizer_options;
|
||||
typedef const spv_optimizer_options_t* spv_const_optimizer_options;
|
||||
typedef spv_reducer_options_t* spv_reducer_options;
|
||||
typedef const spv_reducer_options_t* spv_const_reducer_options;
|
||||
|
||||
// Platform API
|
||||
|
||||
@ -541,6 +545,23 @@ SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetValidatorOptions(
|
||||
SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetMaxIdBound(
|
||||
spv_optimizer_options options, uint32_t val);
|
||||
|
||||
// Creates a reducer options object with default options. Returns a valid
|
||||
// options object. The object remains valid until it is passed into
|
||||
// |spvReducerOptionsDestroy|.
|
||||
SPIRV_TOOLS_EXPORT spv_reducer_options spvReducerOptionsCreate();
|
||||
|
||||
// Destroys the given reducer options object.
|
||||
SPIRV_TOOLS_EXPORT void spvReducerOptionsDestroy(spv_reducer_options options);
|
||||
|
||||
// Records the maximum number of reduction steps that should run before the
|
||||
// reducer gives up.
|
||||
SPIRV_TOOLS_EXPORT void spvReducerOptionsSetStepLimit(
|
||||
spv_reducer_options options, uint32_t step_limit);
|
||||
|
||||
// Sets seed for random number generation.
|
||||
SPIRV_TOOLS_EXPORT void spvReducerOptionsSetSeed(spv_reducer_options options,
|
||||
uint32_t seed);
|
||||
|
||||
// Encodes the given SPIR-V assembly text to its binary representation. The
|
||||
// length parameter specifies the number of bytes for text. Encoded binary will
|
||||
// be stored into *binary. Any error will be written into *diagnostic if
|
||||
|
@ -144,6 +144,28 @@ class OptimizerOptions {
|
||||
spv_optimizer_options options_;
|
||||
};
|
||||
|
||||
// A C++ wrapper around a reducer options object.
|
||||
class ReducerOptions {
|
||||
public:
|
||||
ReducerOptions() : options_(spvReducerOptionsCreate()) {}
|
||||
~ReducerOptions() { spvReducerOptionsDestroy(options_); }
|
||||
|
||||
// Allow implicit conversion to the underlying object.
|
||||
operator spv_reducer_options() const { return options_; }
|
||||
|
||||
// Records the maximum number of reduction steps that should
|
||||
// run before the reducer gives up.
|
||||
void set_step_limit(uint32_t step_limit) {
|
||||
spvReducerOptionsSetStepLimit(options_, step_limit);
|
||||
}
|
||||
|
||||
// Sets a seed to be used for random number generation.
|
||||
void set_seed(uint32_t seed) { spvReducerOptionsSetSeed(options_, seed); }
|
||||
|
||||
private:
|
||||
spv_reducer_options options_;
|
||||
};
|
||||
|
||||
// C++ interface for SPIRV-Tools functionalities. It wraps the context
|
||||
// (including target environment and the corresponding SPIR-V grammar) and
|
||||
// provides methods for assembling, disassembling, and validating.
|
||||
|
@ -216,6 +216,7 @@ set_source_files_properties(
|
||||
|
||||
add_subdirectory(comp)
|
||||
add_subdirectory(opt)
|
||||
add_subdirectory(reduce)
|
||||
add_subdirectory(link)
|
||||
|
||||
set(SPIRV_SOURCES
|
||||
@ -253,6 +254,7 @@ set(SPIRV_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/spirv_definition.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/spirv_endian.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/spirv_optimizer_options.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/spirv_reducer_options.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/spirv_validator_options.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/table.h
|
||||
@ -280,6 +282,7 @@ set(SPIRV_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/software_version.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/spirv_endian.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/spirv_optimizer_options.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/spirv_reducer_options.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/spirv_validator_options.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/table.cpp
|
||||
|
60
source/reduce/CMakeLists.txt
Normal file
60
source/reduce/CMakeLists.txt
Normal file
@ -0,0 +1,60 @@
|
||||
# Copyright (c) 2018 Google LLC
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
set(SPIRV_TOOLS_REDUCE_SOURCES
|
||||
change_operand_reduction_opportunity.h
|
||||
operand_to_const_reduction_pass.h
|
||||
reducer.h
|
||||
reduction_opportunity.h
|
||||
reduction_pass.h
|
||||
remove_instruction_reduction_opportunity.h
|
||||
remove_unreferenced_instruction_reduction_pass.h
|
||||
|
||||
change_operand_reduction_opportunity.cpp
|
||||
operand_to_const_reduction_pass.cpp
|
||||
reducer.cpp
|
||||
reduction_opportunity.cpp
|
||||
reduction_pass.cpp
|
||||
remove_instruction_reduction_opportunity.cpp
|
||||
remove_unreferenced_instruction_reduction_pass.cpp
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
# Enable parallel builds across four cores for this lib
|
||||
add_definitions(/MP4)
|
||||
endif()
|
||||
|
||||
spvtools_pch(SPIRV_TOOLS_REDUCE_SOURCES pch_source_reduce)
|
||||
|
||||
add_library(SPIRV-Tools-reduce ${SPIRV_TOOLS_REDUCE_SOURCES})
|
||||
|
||||
spvtools_default_compile_options(SPIRV-Tools-reduce)
|
||||
target_include_directories(SPIRV-Tools-reduce
|
||||
PUBLIC ${spirv-tools_SOURCE_DIR}/include
|
||||
PUBLIC ${SPIRV_HEADER_INCLUDE_DIR}
|
||||
PRIVATE ${spirv-tools_BINARY_DIR}
|
||||
)
|
||||
# The reducer reuses a lot of functionality from the SPIRV-Tools library.
|
||||
target_link_libraries(SPIRV-Tools-reduce
|
||||
PUBLIC ${SPIRV_TOOLS}
|
||||
PUBLIC SPIRV-Tools-opt)
|
||||
|
||||
set_property(TARGET SPIRV-Tools-reduce PROPERTY FOLDER "SPIRV-Tools libraries")
|
||||
spvtools_check_symbol_exports(SPIRV-Tools-reduce)
|
||||
|
||||
if(ENABLE_SPIRV_TOOLS_INSTALL)
|
||||
install(TARGETS SPIRV-Tools-reduce
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif(ENABLE_SPIRV_TOOLS_INSTALL)
|
32
source/reduce/change_operand_reduction_opportunity.cpp
Normal file
32
source/reduce/change_operand_reduction_opportunity.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "change_operand_reduction_opportunity.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
bool ChangeOperandReductionOpportunity::PreconditionHolds() {
|
||||
// Check that the instruction still has the original operand.
|
||||
return inst_->NumOperands() > operand_index_ &&
|
||||
inst_->GetOperand(operand_index_).words[0] == original_id_ &&
|
||||
inst_->GetOperand(operand_index_).type == original_type_;
|
||||
}
|
||||
|
||||
void ChangeOperandReductionOpportunity::Apply() {
|
||||
inst_->SetOperand(operand_index_, {new_id_});
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
61
source/reduce/change_operand_reduction_opportunity.h
Normal file
61
source/reduce/change_operand_reduction_opportunity.h
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_REDUCE_CHANGE_OPERAND_REDUCTION_OPPORTUNITY_H_
|
||||
#define SOURCE_REDUCE_CHANGE_OPERAND_REDUCTION_OPPORTUNITY_H_
|
||||
|
||||
#include "reduction_opportunity.h"
|
||||
#include "source/opt/instruction.h"
|
||||
#include "spirv-tools/libspirv.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
using namespace opt;
|
||||
|
||||
// Captures the opportunity to change an id operand of an instruction to some
|
||||
// other id.
|
||||
class ChangeOperandReductionOpportunity : public ReductionOpportunity {
|
||||
public:
|
||||
// Constructs the opportunity to replace operand |operand_index| of |inst|
|
||||
// with |new_id|.
|
||||
ChangeOperandReductionOpportunity(Instruction* inst, uint32_t operand_index,
|
||||
uint32_t new_id)
|
||||
: inst_(inst),
|
||||
operand_index_(operand_index),
|
||||
original_id_(inst_->GetOperand(operand_index_).words[0]),
|
||||
original_type_(inst->GetOperand(operand_index).type),
|
||||
new_id_(new_id) {}
|
||||
|
||||
// Determines whether the opportunity can be applied; it may have been viable
|
||||
// when discovered but later disabled by the application of some other
|
||||
// reduction opportunity.
|
||||
bool PreconditionHolds() override;
|
||||
|
||||
protected:
|
||||
// Apply the change of operand.
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
Instruction* const inst_;
|
||||
const uint32_t operand_index_;
|
||||
const uint32_t original_id_;
|
||||
const spv_operand_type_t original_type_;
|
||||
const uint32_t new_id_;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_CHANGE_OPERAND_REDUCTION_OPPORTUNITY_H_
|
86
source/reduce/operand_to_const_reduction_pass.cpp
Normal file
86
source/reduce/operand_to_const_reduction_pass.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "operand_to_const_reduction_pass.h"
|
||||
#include "change_operand_reduction_opportunity.h"
|
||||
#include "source/opt/instruction.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
using namespace opt;
|
||||
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>>
|
||||
OperandToConstReductionPass::GetAvailableOpportunities(
|
||||
opt::IRContext* context) const {
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> result;
|
||||
assert(result.empty());
|
||||
|
||||
// We first loop over all constants. This means that all the reduction
|
||||
// opportunities to replace an operand with a particular constant will be
|
||||
// contiguous, and in particular it means that multiple, incompatible
|
||||
// reduction opportunities that try to replace the same operand with distinct
|
||||
// constants are likely to be discontiguous. This is good because the
|
||||
// reducer works in the spirit of delta debugging and tries applying large
|
||||
// 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& 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
|
||||
// of a ChangeOperandReductionOpportunity
|
||||
for (uint32_t index = 0; index < inst.NumOperands(); index++) {
|
||||
const auto& operand = inst.GetOperand(index);
|
||||
if (spvIsIdType(operand.type)) {
|
||||
if (operand.type == SPV_OPERAND_TYPE_RESULT_ID ||
|
||||
operand.type == SPV_OPERAND_TYPE_TYPE_ID) {
|
||||
continue;
|
||||
}
|
||||
const auto id = operand.words[0];
|
||||
auto def = context->get_def_use_mgr()->GetDef(id);
|
||||
if (spvOpcodeIsConstant(def->opcode())) {
|
||||
// The argument is already a constant.
|
||||
continue;
|
||||
}
|
||||
if (def->opcode() == SpvOpFunction) {
|
||||
// The argument refers to a function, e.g. the function called
|
||||
// by OpFunctionCall; avoid replacing this with a constant of
|
||||
// the function's return type.
|
||||
continue;
|
||||
}
|
||||
auto type_id = def->type_id();
|
||||
if (type_id) {
|
||||
if (constant->type_id() == type_id) {
|
||||
result.push_back(
|
||||
MakeUnique<ChangeOperandReductionOpportunity>(
|
||||
&inst, index, constant->result_id()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string OperandToConstReductionPass::GetName() const {
|
||||
return "OperandToConstReductionPass";
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
51
source/reduce/operand_to_const_reduction_pass.h
Normal file
51
source/reduce/operand_to_const_reduction_pass.h
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_PASS_H_
|
||||
#define SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_PASS_H_
|
||||
|
||||
#include "reduction_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// A reduction pass for turning id operands of instructions into ids of
|
||||
// constants. This reduces the extent to which ids of non-constants are used,
|
||||
// paving the way for instructions that generate them to be eliminated by other
|
||||
// passes.
|
||||
class OperandToConstReductionPass : public ReductionPass {
|
||||
public:
|
||||
// Creates the reduction pass in the context of the given target environment
|
||||
// |target_env|
|
||||
explicit OperandToConstReductionPass(const spv_target_env target_env)
|
||||
: ReductionPass(target_env) {}
|
||||
|
||||
~OperandToConstReductionPass() override = default;
|
||||
|
||||
// The name of this pass.
|
||||
std::string GetName() const final;
|
||||
|
||||
protected:
|
||||
// Finds all opportunities for replacing an operand with a constant in the
|
||||
// given module.
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
|
||||
opt::IRContext* context) const final;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_PASS_H_
|
146
source/reduce/reducer.cpp
Normal file
146
source/reduce/reducer.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
|
||||
#include "source/spirv_reducer_options.h"
|
||||
|
||||
#include "reducer.h"
|
||||
#include "reduction_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
struct Reducer::Impl {
|
||||
explicit Impl(spv_target_env env) : target_env(env) {}
|
||||
|
||||
bool ReachedStepLimit(uint32_t current_step,
|
||||
spv_const_reducer_options options);
|
||||
|
||||
const spv_target_env target_env; // Target environment.
|
||||
MessageConsumer consumer; // Message consumer.
|
||||
InterestingnessFunction interestingness_function;
|
||||
std::vector<std::unique_ptr<ReductionPass>> passes;
|
||||
};
|
||||
|
||||
Reducer::Reducer(spv_target_env env) : impl_(MakeUnique<Impl>(env)) {}
|
||||
|
||||
Reducer::~Reducer() = default;
|
||||
|
||||
void Reducer::SetMessageConsumer(MessageConsumer c) {
|
||||
for (auto& pass : impl_->passes) {
|
||||
pass->SetMessageConsumer(c);
|
||||
}
|
||||
impl_->consumer = std::move(c);
|
||||
}
|
||||
|
||||
void Reducer::SetInterestingnessFunction(
|
||||
Reducer::InterestingnessFunction interestingness_function) {
|
||||
impl_->interestingness_function = std::move(interestingness_function);
|
||||
}
|
||||
|
||||
Reducer::ReductionResultStatus Reducer::Run(
|
||||
std::vector<uint32_t>&& binary_in, std::vector<uint32_t>* binary_out,
|
||||
spv_const_reducer_options options) const {
|
||||
std::vector<uint32_t> current_binary = binary_in;
|
||||
|
||||
// Keeps track of how many reduction attempts have been tried. Reduction
|
||||
// bails out if this reaches a given limit.
|
||||
uint32_t reductions_applied = 0;
|
||||
|
||||
// Initial state should be interesting.
|
||||
if (!impl_->interestingness_function(current_binary, reductions_applied)) {
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Initial state was not interesting; stopping.");
|
||||
return Reducer::ReductionResultStatus::kInitialStateNotInteresting;
|
||||
}
|
||||
|
||||
// Determines whether, on completing one round of reduction passes, it is
|
||||
// worthwhile trying a further round.
|
||||
bool another_round_worthwhile = true;
|
||||
|
||||
// Apply round after round of reduction passes until we hit the reduction
|
||||
// step limit, or deem that another round is not going to be worthwhile.
|
||||
while (!impl_->ReachedStepLimit(reductions_applied, options) &&
|
||||
another_round_worthwhile) {
|
||||
// At the start of a round of reduction passes, assume another round will
|
||||
// not be worthwhile unless we find evidence to the contrary.
|
||||
another_round_worthwhile = false;
|
||||
|
||||
// Iterate through the available passes
|
||||
for (auto& pass : impl_->passes) {
|
||||
// Keep applying this pass at its current granularity until it stops
|
||||
// working or we hit the reduction step limit.
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
("Trying pass " + pass->GetName() + ".").c_str());
|
||||
do {
|
||||
auto maybe_result = pass->TryApplyReduction(current_binary);
|
||||
if (maybe_result.empty()) {
|
||||
// This pass did not have any impact, so move on to the next pass.
|
||||
// If this pass hasn't reached its minimum granularity then it's
|
||||
// worth eventually doing another round of reductions, in order to
|
||||
// try this pass at a finer granularity.
|
||||
impl_->consumer(
|
||||
SPV_MSG_INFO, nullptr, {},
|
||||
("Pass " + pass->GetName() + " did not make a reduction step.")
|
||||
.c_str());
|
||||
another_round_worthwhile |= !pass->ReachedMinimumGranularity();
|
||||
break;
|
||||
}
|
||||
std::stringstream stringstream;
|
||||
reductions_applied++;
|
||||
stringstream << "Pass " << pass->GetName() << " made reduction step "
|
||||
<< reductions_applied << ".";
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
(stringstream.str().c_str()));
|
||||
if (impl_->interestingness_function(maybe_result, reductions_applied)) {
|
||||
// Success! The binary produced by this reduction step is
|
||||
// interesting, so make it the binary of interest henceforth, and
|
||||
// note that it's worth doing another round of reduction passes.
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Reduction step succeeded.");
|
||||
current_binary = std::move(maybe_result);
|
||||
another_round_worthwhile = true;
|
||||
}
|
||||
// Bail out if the reduction step limit has been reached.
|
||||
} while (!impl_->ReachedStepLimit(reductions_applied, options));
|
||||
}
|
||||
}
|
||||
|
||||
*binary_out = std::move(current_binary);
|
||||
|
||||
// Report whether reduction completed, or bailed out early due to reaching
|
||||
// the step limit.
|
||||
if (impl_->ReachedStepLimit(reductions_applied, options)) {
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Reached reduction step limit; stopping.");
|
||||
return Reducer::ReductionResultStatus::kReachedStepLimit;
|
||||
}
|
||||
impl_->consumer(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping.");
|
||||
return Reducer::ReductionResultStatus::kComplete;
|
||||
}
|
||||
|
||||
void Reducer::AddReductionPass(
|
||||
std::unique_ptr<ReductionPass>&& reduction_pass) {
|
||||
impl_->passes.push_back(std::move(reduction_pass));
|
||||
}
|
||||
|
||||
bool Reducer::Impl::ReachedStepLimit(uint32_t current_step,
|
||||
spv_const_reducer_options options) {
|
||||
return current_step >= options->step_limit;
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
97
source/reduce/reducer.h
Normal file
97
source/reduce/reducer.h
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_REDUCE_REDUCER_H_
|
||||
#define SOURCE_REDUCE_REDUCER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
|
||||
#include "reduction_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// This class manages the process of applying a reduction -- parameterized by a
|
||||
// number of reduction passes and an interestingness test, to a SPIR-V binary.
|
||||
class Reducer {
|
||||
public:
|
||||
// Possible statuses that can result from running a reduction.
|
||||
enum ReductionResultStatus {
|
||||
kInitialStateNotInteresting,
|
||||
kReachedStepLimit,
|
||||
kComplete
|
||||
};
|
||||
|
||||
// The type for a function that will take a binary and return true if and
|
||||
// only if the binary is deemed interesting. (The function also takes an
|
||||
// integer argument that will be incremented each time the function is
|
||||
// called; this is for debugging purposes).
|
||||
//
|
||||
// The notion of "interesting" depends on what properties of the binary or
|
||||
// tools that process the binary we are trying to maintain during reduction.
|
||||
using InterestingnessFunction =
|
||||
std::function<bool(const std::vector<uint32_t>&, uint32_t)>;
|
||||
|
||||
// Constructs an instance with the given target |env|, which is used to
|
||||
// decode the binary to be reduced later.
|
||||
//
|
||||
// The constructed instance will have an empty message consumer, which just
|
||||
// ignores all messages from the library. Use SetMessageConsumer() to supply
|
||||
// one if messages are of concern.
|
||||
//
|
||||
// The constructed instance also needs to have an interestingness function
|
||||
// set and some reduction passes added to it in order to be useful.
|
||||
explicit Reducer(spv_target_env env);
|
||||
|
||||
// Disables copy/move constructor/assignment operations.
|
||||
Reducer(const Reducer&) = delete;
|
||||
Reducer(Reducer&&) = delete;
|
||||
Reducer& operator=(const Reducer&) = delete;
|
||||
Reducer& operator=(Reducer&&) = delete;
|
||||
|
||||
// Destructs this instance.
|
||||
~Reducer();
|
||||
|
||||
// Sets the message consumer to the given |consumer|. The |consumer| will be
|
||||
// invoked once for each message communicated from the library.
|
||||
void SetMessageConsumer(MessageConsumer consumer);
|
||||
|
||||
// Sets the function that will be used to decide whether a reduced binary
|
||||
// turned out to be interesting.
|
||||
void SetInterestingnessFunction(
|
||||
InterestingnessFunction interestingness_function);
|
||||
|
||||
// Adds a reduction pass to the sequence of passes that will be iterated
|
||||
// over.
|
||||
void AddReductionPass(std::unique_ptr<ReductionPass>&& reduction_pass);
|
||||
|
||||
// Reduces the given SPIR-V module |binary_out|.
|
||||
// The reduced binary ends up in |binary_out|.
|
||||
// A status is returned.
|
||||
ReductionResultStatus Run(std::vector<uint32_t>&& binary_in,
|
||||
std::vector<uint32_t>* binary_out,
|
||||
spv_const_reducer_options options) const;
|
||||
|
||||
private:
|
||||
struct Impl; // Opaque struct for holding internal data.
|
||||
std::unique_ptr<Impl> impl_; // Unique pointer to internal data.
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_REDUCER_H_
|
27
source/reduce/reduction_opportunity.cpp
Normal file
27
source/reduce/reduction_opportunity.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "reduction_opportunity.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
void ReductionOpportunity::TryToApply() {
|
||||
if (PreconditionHolds()) {
|
||||
Apply();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
46
source/reduce/reduction_opportunity.h
Normal file
46
source/reduce/reduction_opportunity.h
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_REDUCE_REDUCTION_OPPORTUNITY_H_
|
||||
#define SOURCE_REDUCE_REDUCTION_OPPORTUNITY_H_
|
||||
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// Abstract class capturing an opportunity to apply a reducing transformation.
|
||||
class ReductionOpportunity {
|
||||
public:
|
||||
ReductionOpportunity() = default;
|
||||
virtual ~ReductionOpportunity() = default;
|
||||
|
||||
// Determines whether the opportunity can be applied; it may have been viable
|
||||
// when discovered but later disabled by the application of some other
|
||||
// reduction opportunity.
|
||||
virtual bool PreconditionHolds() = 0;
|
||||
|
||||
// A no-op if PreconditoinHolds() returns false; otherwise applies the
|
||||
// opportunity.
|
||||
void TryToApply();
|
||||
|
||||
protected:
|
||||
// Apply the reduction opportunity.
|
||||
virtual void Apply() = 0;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_REDUCTION_OPPORTUNITY_H_
|
86
source/reduce/reduction_pass.cpp
Normal file
86
source/reduce/reduction_pass.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "reduction_pass.h"
|
||||
|
||||
#include "source/opt/build_module.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
std::vector<uint32_t> ReductionPass::TryApplyReduction(
|
||||
const std::vector<uint32_t>& binary) {
|
||||
// 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
|
||||
// module as if the reduction step proves to be uninteresting we need to
|
||||
// backtrack; re-parsing from binary provides a very clean way of cloning the
|
||||
// module.
|
||||
std::unique_ptr<opt::IRContext> context =
|
||||
BuildModule(target_env_, consumer_, binary.data(), binary.size());
|
||||
assert(context);
|
||||
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> opportunities =
|
||||
GetAvailableOpportunities(context.get());
|
||||
|
||||
if (!is_initialized_) {
|
||||
is_initialized_ = true;
|
||||
index_ = 0;
|
||||
granularity_ = (uint32_t)opportunities.size();
|
||||
}
|
||||
|
||||
if (opportunities.empty()) {
|
||||
granularity_ = 1;
|
||||
return std::vector<uint32_t>();
|
||||
}
|
||||
|
||||
assert(granularity_ > 0);
|
||||
|
||||
if (index_ >= opportunities.size()) {
|
||||
index_ = 0;
|
||||
granularity_ = std::max((uint32_t)1, granularity_ / 2);
|
||||
return std::vector<uint32_t>();
|
||||
}
|
||||
|
||||
for (uint32_t i = index_;
|
||||
i < std::min(index_ + granularity_, (uint32_t)opportunities.size());
|
||||
++i) {
|
||||
opportunities[i]->TryToApply();
|
||||
}
|
||||
|
||||
index_ += granularity_;
|
||||
|
||||
std::vector<uint32_t> result;
|
||||
context->module()->ToBinary(&result, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
void ReductionPass::SetMessageConsumer(MessageConsumer consumer) {
|
||||
consumer_ = std::move(consumer);
|
||||
}
|
||||
|
||||
bool ReductionPass::ReachedMinimumGranularity() const {
|
||||
if (!is_initialized_) {
|
||||
// Conceptually we can think that if the pass has not yet been initialized,
|
||||
// it is operating at unbounded granularity.
|
||||
return false;
|
||||
}
|
||||
assert(granularity_ != 0);
|
||||
return granularity_ == 1;
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
73
source/reduce/reduction_pass.h
Normal file
73
source/reduce/reduction_pass.h
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_REDUCE_REDUCTION_PASS_H_
|
||||
#define SOURCE_REDUCE_REDUCTION_PASS_H_
|
||||
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
|
||||
#include "reduction_opportunity.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// Abstract class representing a reduction pass, which can be repeatedly
|
||||
// invoked to find and apply particular reduction opportunities to a SPIR-V
|
||||
// binary. In the spirit of delta debugging, a pass initially tries to apply
|
||||
// large chunks of reduction opportunities, iterating through available
|
||||
// opportunities at a given granularity. When an iteration over available
|
||||
// opportunities completes, the granularity is reduced and iteration starts
|
||||
// again, until the minimum granularity is reached.
|
||||
class ReductionPass {
|
||||
public:
|
||||
// Constructs a reduction pass with a given target environment, |target_env|.
|
||||
// Initially the pass is uninitialized.
|
||||
explicit ReductionPass(const spv_target_env target_env)
|
||||
: target_env_(target_env), is_initialized_(false) {}
|
||||
|
||||
virtual ~ReductionPass() = default;
|
||||
|
||||
// Apply the reduction pass to the given binary.
|
||||
std::vector<uint32_t> TryApplyReduction(const std::vector<uint32_t>& binary);
|
||||
|
||||
// Set a consumer to which relevant messages will be directed.
|
||||
void SetMessageConsumer(MessageConsumer consumer);
|
||||
|
||||
// Determines whether the granularity with which reduction opportunities are
|
||||
// applied has reached a minimum.
|
||||
bool ReachedMinimumGranularity() const;
|
||||
|
||||
// Returns the name of the reduction pass (useful for monitoring reduction
|
||||
// progress).
|
||||
virtual std::string GetName() const = 0;
|
||||
|
||||
protected:
|
||||
// Finds the reduction opportunities relevant to this pass that could be
|
||||
// applied to a given SPIR-V module.
|
||||
virtual std::vector<std::unique_ptr<ReductionOpportunity>>
|
||||
GetAvailableOpportunities(opt::IRContext* context) const = 0;
|
||||
|
||||
private:
|
||||
const spv_target_env target_env_;
|
||||
MessageConsumer consumer_;
|
||||
bool is_initialized_;
|
||||
uint32_t index_;
|
||||
uint32_t granularity_;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_REDUCTION_PASS_H_
|
29
source/reduce/remove_instruction_reduction_opportunity.cpp
Normal file
29
source/reduce/remove_instruction_reduction_opportunity.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
#include "remove_instruction_reduction_opportunity.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
bool RemoveInstructionReductionOpportunity::PreconditionHolds() { return true; }
|
||||
|
||||
void RemoveInstructionReductionOpportunity::Apply() {
|
||||
inst_->context()->KillInst(inst_);
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
47
source/reduce/remove_instruction_reduction_opportunity.h
Normal file
47
source/reduce/remove_instruction_reduction_opportunity.h
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_REDUCE_REMOVE_INSTRUCTION_REDUCTION_OPPORTUNITY_H_
|
||||
#define SOURCE_REDUCE_REMOVE_INSTRUCTION_REDUCTION_OPPORTUNITY_H_
|
||||
|
||||
#include "reduction_opportunity.h"
|
||||
#include "source/opt/instruction.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
using namespace opt;
|
||||
|
||||
// Captures the opportunity to remove an instruction from the SPIR-V module.
|
||||
class RemoveInstructionReductionOpportunity : public ReductionOpportunity {
|
||||
public:
|
||||
// Constructs the opportunity to remove |inst|.
|
||||
explicit RemoveInstructionReductionOpportunity(Instruction* inst)
|
||||
: inst_(inst) {}
|
||||
|
||||
// This kind of opportunity can be unconditionally applied.
|
||||
bool PreconditionHolds() override;
|
||||
|
||||
protected:
|
||||
// Remove the instruction.
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
Instruction* inst_;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_REMOVE_INSTRUCTION_REDUCTION_OPPORTUNITY_H_
|
@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2018 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "remove_unreferenced_instruction_reduction_pass.h"
|
||||
#include "remove_instruction_reduction_opportunity.h"
|
||||
#include "source/opcode.h"
|
||||
#include "source/opt/instruction.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
using namespace opt;
|
||||
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>>
|
||||
RemoveUnreferencedInstructionReductionPass::GetAvailableOpportunities(
|
||||
opt::IRContext* context) const {
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> result;
|
||||
|
||||
for (auto& function : *context->module()) {
|
||||
for (auto& block : function) {
|
||||
for (auto& inst : block) {
|
||||
if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
|
||||
continue;
|
||||
}
|
||||
if (spvOpcodeIsBlockTerminator(inst.opcode()) ||
|
||||
inst.opcode() == SpvOpSelectionMerge ||
|
||||
inst.opcode() == SpvOpLoopMerge) {
|
||||
// In this reduction pass we do not want to affect static control
|
||||
// flow.
|
||||
continue;
|
||||
}
|
||||
// Given that we're in a block, we should only get here if the
|
||||
// instruction is not directly related to control flow; i.e., it's
|
||||
// some straightforward instruction with an unused result, like an
|
||||
// arithmetic operation or function call.
|
||||
result.push_back(
|
||||
MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string RemoveUnreferencedInstructionReductionPass::GetName() const {
|
||||
return "RemoveUnreferencedInstructionReductionPass";
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_PASS_H_
|
||||
#define SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_PASS_H_
|
||||
|
||||
#include "reduction_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// A reduction pass for removing non-control-flow instructions in blocks in
|
||||
// cases where the instruction's id is not referenced. As well as making the
|
||||
// module smaller, removing an instruction that referenced particular ids may
|
||||
// create opportunities for subsequently removing the instructions that
|
||||
// generated those ids.
|
||||
class RemoveUnreferencedInstructionReductionPass : public ReductionPass {
|
||||
public:
|
||||
// Creates the reduction pass in the context of the given target environment
|
||||
// |target_env|
|
||||
explicit RemoveUnreferencedInstructionReductionPass(
|
||||
const spv_target_env target_env)
|
||||
: ReductionPass(target_env) {}
|
||||
|
||||
~RemoveUnreferencedInstructionReductionPass() override = default;
|
||||
|
||||
// The name of this pass.
|
||||
std::string GetName() const final;
|
||||
|
||||
protected:
|
||||
// Finds all opportunities for removing unreferenced instructions in the
|
||||
// given module.
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
|
||||
opt::IRContext* context) const final;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_PASS_H_
|
31
source/spirv_reducer_options.cpp
Normal file
31
source/spirv_reducer_options.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#include "source/spirv_reducer_options.h"
|
||||
|
||||
SPIRV_TOOLS_EXPORT spv_reducer_options spvReducerOptionsCreate() {
|
||||
return new spv_reducer_options_t();
|
||||
}
|
||||
|
||||
SPIRV_TOOLS_EXPORT void spvReducerOptionsDestroy(spv_reducer_options options) {
|
||||
delete options;
|
||||
}
|
||||
|
||||
SPIRV_TOOLS_EXPORT void spvReducerOptionsSetStepLimit(
|
||||
spv_reducer_options options, uint32_t step_limit) {
|
||||
options->step_limit = step_limit;
|
||||
}
|
35
source/spirv_reducer_options.h
Normal file
35
source/spirv_reducer_options.h
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_SPIRV_REDUCER_OPTIONS_H_
|
||||
#define SOURCE_SPIRV_REDUCER_OPTIONS_H_
|
||||
|
||||
#include "spirv-tools/libspirv.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
// The default maximum number of steps for the reducer to run before giving up.
|
||||
const uint32_t kDefaultStepLimit = 250;
|
||||
|
||||
// Manages command line options passed to the SPIR-V Reducer. New struct
|
||||
// members may be added for any new option.
|
||||
struct spv_reducer_options_t {
|
||||
spv_reducer_options_t() : step_limit(kDefaultStepLimit) {}
|
||||
|
||||
// The number of steps the reducer will run for before giving up.
|
||||
uint32_t step_limit;
|
||||
};
|
||||
|
||||
#endif // SOURCE_SPIRV_REDUCER_OPTIONS_H_
|
@ -207,6 +207,7 @@ add_spvtools_unittest(
|
||||
add_subdirectory(comp)
|
||||
add_subdirectory(link)
|
||||
add_subdirectory(opt)
|
||||
add_subdirectory(reduce)
|
||||
add_subdirectory(stats)
|
||||
add_subdirectory(tools)
|
||||
add_subdirectory(util)
|
||||
|
23
test/reduce/CMakeLists.txt
Normal file
23
test/reduce/CMakeLists.txt
Normal file
@ -0,0 +1,23 @@
|
||||
# Copyright (c) 2018 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
add_spvtools_unittest(TARGET reduce
|
||||
SRCS operand_to_constant_reduction_pass_test.cpp
|
||||
reduce_test_util.cpp
|
||||
reduce_test_util.h
|
||||
reducer_test.cpp
|
||||
remove_unreferenced_instruction_reduction_pass_test.cpp
|
||||
LIBS SPIRV-Tools-reduce
|
||||
)
|
||||
|
156
test/reduce/operand_to_constant_reduction_pass_test.cpp
Normal file
156
test/reduce/operand_to_constant_reduction_pass_test.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "reduce_test_util.h"
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/reduce/operand_to_const_reduction_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
namespace {
|
||||
|
||||
TEST(OperandToConstantReductionPassTest, BasicCheck) {
|
||||
std::string prologue = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main" %37
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
OpName %9 "buf1"
|
||||
OpMemberName %9 0 "f"
|
||||
OpName %11 ""
|
||||
OpName %24 "buf2"
|
||||
OpMemberName %24 0 "i"
|
||||
OpName %26 ""
|
||||
OpName %37 "_GLF_color"
|
||||
OpMemberDecorate %9 0 Offset 0
|
||||
OpDecorate %9 Block
|
||||
OpDecorate %11 DescriptorSet 0
|
||||
OpDecorate %11 Binding 1
|
||||
OpMemberDecorate %24 0 Offset 0
|
||||
OpDecorate %24 Block
|
||||
OpDecorate %26 DescriptorSet 0
|
||||
OpDecorate %26 Binding 2
|
||||
OpDecorate %37 Location 0
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFloat 32
|
||||
%9 = OpTypeStruct %6
|
||||
%10 = OpTypePointer Uniform %9
|
||||
%11 = OpVariable %10 Uniform
|
||||
%12 = OpTypeInt 32 1
|
||||
%13 = OpConstant %12 0
|
||||
%14 = OpTypePointer Uniform %6
|
||||
%20 = OpConstant %6 2
|
||||
%24 = OpTypeStruct %12
|
||||
%25 = OpTypePointer Uniform %24
|
||||
%26 = OpVariable %25 Uniform
|
||||
%27 = OpTypePointer Uniform %12
|
||||
%33 = OpConstant %12 3
|
||||
%35 = OpTypeVector %6 4
|
||||
%36 = OpTypePointer Output %35
|
||||
%37 = OpVariable %36 Output
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%15 = OpAccessChain %14 %11 %13
|
||||
%16 = OpLoad %6 %15
|
||||
%19 = OpFAdd %6 %16 %16
|
||||
%21 = OpFAdd %6 %19 %20
|
||||
%28 = OpAccessChain %27 %26 %13
|
||||
%29 = OpLoad %12 %28
|
||||
)";
|
||||
|
||||
std::string epilogue = R"(
|
||||
%45 = OpConvertSToF %6 %34
|
||||
%46 = OpCompositeConstruct %35 %16 %21 %43 %45
|
||||
OpStore %37 %46
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
std::string original = prologue + R"(
|
||||
%32 = OpIAdd %12 %29 %29
|
||||
%34 = OpIAdd %12 %32 %33
|
||||
%43 = OpConvertSToF %6 %29
|
||||
)" + epilogue;
|
||||
|
||||
std::string expected = prologue + R"(
|
||||
%32 = OpIAdd %12 %13 %13 ; %29 -> %13 x 2
|
||||
%34 = OpIAdd %12 %13 %33 ; %32 -> %13
|
||||
%43 = OpConvertSToF %6 %13 ; %29 -> %13
|
||||
)" + epilogue;
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context =
|
||||
BuildModule(env, consumer, original, kReduceAssembleOption);
|
||||
const auto pass = TestSubclass<OperandToConstReductionPass>(env);
|
||||
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(17, ops.size());
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
ASSERT_TRUE(ops[1]->PreconditionHolds());
|
||||
ops[1]->TryToApply();
|
||||
ASSERT_TRUE(ops[2]->PreconditionHolds());
|
||||
ops[2]->TryToApply();
|
||||
ASSERT_TRUE(ops[3]->PreconditionHolds());
|
||||
ops[3]->TryToApply();
|
||||
|
||||
CheckEqual(env, expected, context.get());
|
||||
}
|
||||
|
||||
TEST(OperandToConstantReductionPassTest, WithCalledFunction) {
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main" %10 %12
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFloat 32
|
||||
%7 = OpTypeVector %6 4
|
||||
%8 = OpTypeFunction %7
|
||||
%9 = OpTypePointer Output %7
|
||||
%10 = OpVariable %9 Output
|
||||
%11 = OpTypePointer Input %7
|
||||
%12 = OpVariable %11 Input
|
||||
%13 = OpConstant %6 0
|
||||
%14 = OpConstantComposite %7 %13 %13 %13 %13
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%15 = OpFunctionCall %7 %16
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%16 = OpFunction %7 None %8
|
||||
%17 = OpLabel
|
||||
OpReturnValue %14
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context =
|
||||
BuildModule(env, consumer, shader, kReduceAssembleOption);
|
||||
const auto pass = TestSubclass<OperandToConstReductionPass>(env);
|
||||
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
52
test/reduce/reduce_test_util.cpp
Normal file
52
test/reduce/reduce_test_util.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "reduce_test_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
void CheckEqual(const spv_target_env env,
|
||||
const std::vector<uint32_t>& expected_binary,
|
||||
const std::vector<uint32_t>& actual_binary) {
|
||||
if (expected_binary != actual_binary) {
|
||||
SpirvTools t(env);
|
||||
std::string expected_disassembled;
|
||||
std::string actual_disassembled;
|
||||
ASSERT_TRUE(t.Disassemble(expected_binary, &expected_disassembled,
|
||||
kReduceDisassembleOption));
|
||||
ASSERT_TRUE(t.Disassemble(actual_binary, &actual_disassembled,
|
||||
kReduceDisassembleOption));
|
||||
ASSERT_EQ(expected_disassembled, actual_disassembled);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckEqual(const spv_target_env env, const std::string& expected_text,
|
||||
const std::vector<uint32_t>& actual_binary) {
|
||||
std::vector<uint32_t> expected_binary;
|
||||
SpirvTools t(env);
|
||||
ASSERT_TRUE(
|
||||
t.Assemble(expected_text, &expected_binary, kReduceAssembleOption));
|
||||
CheckEqual(env, expected_binary, actual_binary);
|
||||
}
|
||||
|
||||
void CheckEqual(const spv_target_env env, const std::string& expected_text,
|
||||
const opt::IRContext* actual_ir) {
|
||||
std::vector<uint32_t> actual_binary;
|
||||
actual_ir->module()->ToBinary(&actual_binary, false);
|
||||
CheckEqual(env, expected_text, actual_binary);
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
70
test/reduce/reduce_test_util.h
Normal file
70
test/reduce/reduce_test_util.h
Normal file
@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef TEST_REDUCE_REDUCE_TEST_UTIL_H_
|
||||
#define TEST_REDUCE_REDUCE_TEST_UTIL_H_
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/reduce/reduction_opportunity.h"
|
||||
#include "spirv-tools/libspirv.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// A helper class that subclasses a given reduction pass class in order to
|
||||
// provide a wrapper for its protected methods.
|
||||
template <class ReductionPassT>
|
||||
class TestSubclass : public ReductionPassT {
|
||||
public:
|
||||
// Creates an instance of the reduction pass subclass with respect to target
|
||||
// environment |env|.
|
||||
explicit TestSubclass(const spv_target_env env) : ReductionPassT(env) {}
|
||||
~TestSubclass() = default;
|
||||
|
||||
// A wrapper for GetAvailableOpportunities(...)
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>>
|
||||
WrapGetAvailableOpportunities(opt::IRContext* context) const {
|
||||
return ReductionPassT::GetAvailableOpportunities(context);
|
||||
}
|
||||
};
|
||||
|
||||
// Checks whether the given binaries are bit-wise equal.
|
||||
void CheckEqual(spv_target_env env,
|
||||
const std::vector<uint32_t>& expected_binary,
|
||||
const std::vector<uint32_t>& actual_binary);
|
||||
|
||||
// Assembles the given text and check whether the resulting binary is bit-wise
|
||||
// equal to the given binary.
|
||||
void CheckEqual(spv_target_env env, const std::string& expected_text,
|
||||
const std::vector<uint32_t>& actual_binary);
|
||||
|
||||
// Assembles the given text and turns the given IR into binary, then checks
|
||||
// whether the resulting binaries are bit-wise equal.
|
||||
void CheckEqual(spv_target_env env, const std::string& expected_text,
|
||||
const opt::IRContext* actual_ir);
|
||||
|
||||
// Assembly options for writing reduction tests. It simplifies matters if
|
||||
// numeric ids do not change.
|
||||
const uint32_t kReduceAssembleOption =
|
||||
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS;
|
||||
// Disassembly options for writing reduction tests.
|
||||
const uint32_t kReduceDisassembleOption =
|
||||
SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | SPV_BINARY_TO_TEXT_OPTION_INDENT;
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // TEST_REDUCE_REDUCE_TEST_UTIL_H_
|
243
test/reduce/reducer_test.cpp
Normal file
243
test/reduce/reducer_test.cpp
Normal file
@ -0,0 +1,243 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "reduce_test_util.h"
|
||||
|
||||
#include "source/reduce/operand_to_const_reduction_pass.h"
|
||||
#include "source/reduce/reducer.h"
|
||||
#include "source/reduce/remove_unreferenced_instruction_reduction_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
namespace {
|
||||
|
||||
// Don't print reducer info during testing.
|
||||
void NopDiagnostic(spv_message_level_t /*level*/, const char* /*source*/,
|
||||
const spv_position_t& /*position*/,
|
||||
const char* /*message*/) {}
|
||||
|
||||
// This changes is its mind each time IsInteresting is invoked as to whether the
|
||||
// binary is interesting, until some limit is reached after which the binary is
|
||||
// always deemed interesting. This is useful to test that reduction passes
|
||||
// interleave in interesting ways for a while, and then always succeed after
|
||||
// some point; the latter is important to end up with a predictable final
|
||||
// reduced binary for tests.
|
||||
class PingPongInteresting {
|
||||
public:
|
||||
explicit PingPongInteresting(uint32_t always_interesting_after)
|
||||
: is_interesting_(true),
|
||||
always_interesting_after_(always_interesting_after),
|
||||
count_(0) {}
|
||||
|
||||
bool IsInteresting(const std::vector<uint32_t>&) {
|
||||
bool result;
|
||||
if (count_ > always_interesting_after_) {
|
||||
result = true;
|
||||
} else {
|
||||
result = is_interesting_;
|
||||
is_interesting_ = !is_interesting_;
|
||||
}
|
||||
count_++;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_interesting_;
|
||||
const uint32_t always_interesting_after_;
|
||||
uint32_t count_;
|
||||
};
|
||||
|
||||
TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
|
||||
std::string original = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main" %60
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
OpName %16 "buf2"
|
||||
OpMemberName %16 0 "i"
|
||||
OpName %18 ""
|
||||
OpName %25 "buf1"
|
||||
OpMemberName %25 0 "f"
|
||||
OpName %27 ""
|
||||
OpName %60 "_GLF_color"
|
||||
OpMemberDecorate %16 0 Offset 0
|
||||
OpDecorate %16 Block
|
||||
OpDecorate %18 DescriptorSet 0
|
||||
OpDecorate %18 Binding 2
|
||||
OpMemberDecorate %25 0 Offset 0
|
||||
OpDecorate %25 Block
|
||||
OpDecorate %27 DescriptorSet 0
|
||||
OpDecorate %27 Binding 1
|
||||
OpDecorate %60 Location 0
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%9 = OpConstant %6 0
|
||||
%16 = OpTypeStruct %6
|
||||
%17 = OpTypePointer Uniform %16
|
||||
%18 = OpVariable %17 Uniform
|
||||
%19 = OpTypePointer Uniform %6
|
||||
%22 = OpTypeBool
|
||||
%100 = OpConstantTrue %22
|
||||
%24 = OpTypeFloat 32
|
||||
%25 = OpTypeStruct %24
|
||||
%26 = OpTypePointer Uniform %25
|
||||
%27 = OpVariable %26 Uniform
|
||||
%28 = OpTypePointer Uniform %24
|
||||
%31 = OpConstant %24 2
|
||||
%56 = OpConstant %6 1
|
||||
%58 = OpTypeVector %24 4
|
||||
%59 = OpTypePointer Output %58
|
||||
%60 = OpVariable %59 Output
|
||||
%72 = OpUndef %24
|
||||
%74 = OpUndef %6
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
%73 = OpPhi %6 %74 %5 %77 %34
|
||||
%71 = OpPhi %24 %72 %5 %76 %34
|
||||
%70 = OpPhi %6 %9 %5 %57 %34
|
||||
%20 = OpAccessChain %19 %18 %9
|
||||
%21 = OpLoad %6 %20
|
||||
%23 = OpSLessThan %22 %70 %21
|
||||
OpLoopMerge %12 %34 None
|
||||
OpBranchConditional %23 %11 %12
|
||||
%11 = OpLabel
|
||||
%29 = OpAccessChain %28 %27 %9
|
||||
%30 = OpLoad %24 %29
|
||||
%32 = OpFOrdGreaterThan %22 %30 %31
|
||||
OpSelectionMerge %34 None
|
||||
OpBranchConditional %32 %33 %46
|
||||
%33 = OpLabel
|
||||
%40 = OpFAdd %24 %71 %30
|
||||
%45 = OpISub %6 %73 %21
|
||||
OpBranch %34
|
||||
%46 = OpLabel
|
||||
%50 = OpFMul %24 %71 %30
|
||||
%54 = OpSDiv %6 %73 %21
|
||||
OpBranch %34
|
||||
%34 = OpLabel
|
||||
%77 = OpPhi %6 %45 %33 %54 %46
|
||||
%76 = OpPhi %24 %40 %33 %50 %46
|
||||
%57 = OpIAdd %6 %70 %56
|
||||
OpBranch %10
|
||||
%12 = OpLabel
|
||||
%61 = OpAccessChain %28 %27 %9
|
||||
%62 = OpLoad %24 %61
|
||||
%66 = OpConvertSToF %24 %21
|
||||
%68 = OpConvertSToF %24 %73
|
||||
%69 = OpCompositeConstruct %58 %62 %71 %66 %68
|
||||
OpStore %60 %69
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
std::string expected = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main" %60
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
OpName %16 "buf2"
|
||||
OpMemberName %16 0 "i"
|
||||
OpName %18 ""
|
||||
OpName %25 "buf1"
|
||||
OpMemberName %25 0 "f"
|
||||
OpName %27 ""
|
||||
OpName %60 "_GLF_color"
|
||||
OpMemberDecorate %16 0 Offset 0
|
||||
OpDecorate %16 Block
|
||||
OpDecorate %18 DescriptorSet 0
|
||||
OpDecorate %18 Binding 2
|
||||
OpMemberDecorate %25 0 Offset 0
|
||||
OpDecorate %25 Block
|
||||
OpDecorate %27 DescriptorSet 0
|
||||
OpDecorate %27 Binding 1
|
||||
OpDecorate %60 Location 0
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%9 = OpConstant %6 0
|
||||
%16 = OpTypeStruct %6
|
||||
%17 = OpTypePointer Uniform %16
|
||||
%18 = OpVariable %17 Uniform
|
||||
%19 = OpTypePointer Uniform %6
|
||||
%22 = OpTypeBool
|
||||
%100 = OpConstantTrue %22
|
||||
%24 = OpTypeFloat 32
|
||||
%25 = OpTypeStruct %24
|
||||
%26 = OpTypePointer Uniform %25
|
||||
%27 = OpVariable %26 Uniform
|
||||
%28 = OpTypePointer Uniform %24
|
||||
%31 = OpConstant %24 2
|
||||
%56 = OpConstant %6 1
|
||||
%58 = OpTypeVector %24 4
|
||||
%59 = OpTypePointer Output %58
|
||||
%60 = OpVariable %59 Output
|
||||
%72 = OpUndef %24
|
||||
%74 = OpUndef %6
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %12 %34 None
|
||||
OpBranchConditional %100 %11 %12
|
||||
%11 = OpLabel
|
||||
OpSelectionMerge %34 None
|
||||
OpBranchConditional %100 %33 %46
|
||||
%33 = OpLabel
|
||||
OpBranch %34
|
||||
%46 = OpLabel
|
||||
OpBranch %34
|
||||
%34 = OpLabel
|
||||
OpBranch %10
|
||||
%12 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
|
||||
Reducer reducer(env);
|
||||
PingPongInteresting ping_pong_interesting(10);
|
||||
reducer.SetMessageConsumer(NopDiagnostic);
|
||||
reducer.SetInterestingnessFunction(
|
||||
[&](const std::vector<uint32_t>& binary, uint32_t) -> bool {
|
||||
return ping_pong_interesting.IsInteresting(binary);
|
||||
});
|
||||
reducer.AddReductionPass(MakeUnique<OperandToConstReductionPass>(env));
|
||||
reducer.AddReductionPass(
|
||||
MakeUnique<RemoveUnreferencedInstructionReductionPass>(env));
|
||||
|
||||
std::vector<uint32_t> binary_in;
|
||||
SpirvTools t(env);
|
||||
|
||||
ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
|
||||
std::vector<uint32_t> binary_out;
|
||||
spvtools::ReducerOptions reducer_options;
|
||||
reducer_options.set_step_limit(500);
|
||||
|
||||
reducer.Run(std::move(binary_in), &binary_out, reducer_options);
|
||||
|
||||
CheckEqual(env, expected, binary_out);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
@ -0,0 +1,230 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "reduce_test_util.h"
|
||||
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/reduce/reduction_opportunity.h"
|
||||
#include "source/reduce/remove_unreferenced_instruction_reduction_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
namespace {
|
||||
|
||||
TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) {
|
||||
const std::string prologue = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
OpName %8 "a"
|
||||
OpName %10 "b"
|
||||
OpName %12 "c"
|
||||
OpName %14 "d"
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 10
|
||||
%11 = OpConstant %6 20
|
||||
%13 = OpConstant %6 30
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
%10 = OpVariable %7 Function
|
||||
%12 = OpVariable %7 Function
|
||||
%14 = OpVariable %7 Function
|
||||
)";
|
||||
|
||||
const std::string epilogue = R"(
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string original = prologue + R"(
|
||||
OpStore %8 %9
|
||||
OpStore %10 %11
|
||||
OpStore %12 %13
|
||||
%15 = OpLoad %6 %8
|
||||
OpStore %14 %15
|
||||
)" + epilogue;
|
||||
|
||||
const std::string expected = prologue + R"(
|
||||
OpStore %12 %13
|
||||
%15 = OpLoad %6 %8
|
||||
OpStore %14 %15
|
||||
)" + epilogue;
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context =
|
||||
BuildModule(env, consumer, original, kReduceAssembleOption);
|
||||
const auto pass =
|
||||
TestSubclass<RemoveUnreferencedInstructionReductionPass>(env);
|
||||
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(4, ops.size());
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
ASSERT_TRUE(ops[1]->PreconditionHolds());
|
||||
ops[1]->TryToApply();
|
||||
|
||||
CheckEqual(env, expected, context.get());
|
||||
}
|
||||
|
||||
TEST(RemoveUnreferencedInstructionReductionPassTest, ApplyReduction) {
|
||||
const std::string prologue = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
OpName %8 "a"
|
||||
OpName %10 "b"
|
||||
OpName %12 "c"
|
||||
OpName %14 "d"
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 10
|
||||
%11 = OpConstant %6 20
|
||||
%13 = OpConstant %6 30
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
%10 = OpVariable %7 Function
|
||||
%12 = OpVariable %7 Function
|
||||
%14 = OpVariable %7 Function
|
||||
)";
|
||||
|
||||
const std::string epilogue = R"(
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string original = prologue + R"(
|
||||
OpStore %8 %9
|
||||
OpStore %10 %11
|
||||
OpStore %12 %13
|
||||
%15 = OpLoad %6 %8
|
||||
OpStore %14 %15
|
||||
)" + epilogue;
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
|
||||
std::vector<uint32_t> binary;
|
||||
SpirvTools t(env);
|
||||
ASSERT_TRUE(t.Assemble(original, &binary, kReduceAssembleOption));
|
||||
|
||||
auto pass = TestSubclass<RemoveUnreferencedInstructionReductionPass>(env);
|
||||
|
||||
{
|
||||
// Attempt 1 should remove everything removable.
|
||||
const std::string expected_reduced = prologue + R"(
|
||||
%15 = OpLoad %6 %8
|
||||
)" + epilogue;
|
||||
auto reduced_binary = pass.TryApplyReduction(binary);
|
||||
CheckEqual(env, expected_reduced, reduced_binary);
|
||||
}
|
||||
|
||||
// Attempt 2 should fail as pass with granularity 4 got to end.
|
||||
ASSERT_EQ(0, pass.TryApplyReduction(binary).size());
|
||||
|
||||
{
|
||||
// Attempt 3 should remove first two removable statements.
|
||||
const std::string expected_reduced = prologue + R"(
|
||||
OpStore %12 %13
|
||||
%15 = OpLoad %6 %8
|
||||
OpStore %14 %15
|
||||
)" + epilogue;
|
||||
auto reduced_binary = pass.TryApplyReduction(binary);
|
||||
CheckEqual(env, expected_reduced, reduced_binary);
|
||||
}
|
||||
|
||||
{
|
||||
// Attempt 4 should remove last two removable statements.
|
||||
const std::string expected_reduced = prologue + R"(
|
||||
OpStore %8 %9
|
||||
OpStore %10 %11
|
||||
%15 = OpLoad %6 %8
|
||||
)" + epilogue;
|
||||
auto reduced_binary = pass.TryApplyReduction(binary);
|
||||
CheckEqual(env, expected_reduced, reduced_binary);
|
||||
}
|
||||
|
||||
// Attempt 5 should fail as pass with granularity 2 got to end.
|
||||
ASSERT_EQ(0, pass.TryApplyReduction(binary).size());
|
||||
|
||||
{
|
||||
// Attempt 6 should remove first removable statement.
|
||||
const std::string expected_reduced = prologue + R"(
|
||||
OpStore %10 %11
|
||||
OpStore %12 %13
|
||||
%15 = OpLoad %6 %8
|
||||
OpStore %14 %15
|
||||
)" + epilogue;
|
||||
auto reduced_binary = pass.TryApplyReduction(binary);
|
||||
CheckEqual(env, expected_reduced, reduced_binary);
|
||||
}
|
||||
|
||||
{
|
||||
// Attempt 7 should remove second removable statement.
|
||||
const std::string expected_reduced = prologue + R"(
|
||||
OpStore %8 %9
|
||||
OpStore %12 %13
|
||||
%15 = OpLoad %6 %8
|
||||
OpStore %14 %15
|
||||
)" + epilogue;
|
||||
auto reduced_binary = pass.TryApplyReduction(binary);
|
||||
CheckEqual(env, expected_reduced, reduced_binary);
|
||||
}
|
||||
|
||||
{
|
||||
// Attempt 8 should remove third removable statement.
|
||||
const std::string expected_reduced = prologue + R"(
|
||||
OpStore %8 %9
|
||||
OpStore %10 %11
|
||||
%15 = OpLoad %6 %8
|
||||
OpStore %14 %15
|
||||
)" + epilogue;
|
||||
auto reduced_binary = pass.TryApplyReduction(binary);
|
||||
CheckEqual(env, expected_reduced, reduced_binary);
|
||||
}
|
||||
|
||||
{
|
||||
// Attempt 9 should remove fourth removable statement.
|
||||
const std::string expected_reduced = prologue + R"(
|
||||
OpStore %8 %9
|
||||
OpStore %10 %11
|
||||
OpStore %12 %13
|
||||
%15 = OpLoad %6 %8
|
||||
)" + epilogue;
|
||||
auto reduced_binary = pass.TryApplyReduction(binary);
|
||||
CheckEqual(env, expected_reduced, reduced_binary);
|
||||
}
|
||||
|
||||
// Attempt 10 should fail as pass with granularity 1 got to end.
|
||||
ASSERT_EQ(0, pass.TryApplyReduction(binary).size());
|
||||
|
||||
ASSERT_TRUE(pass.ReachedMinimumGranularity());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
@ -42,6 +42,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES})
|
||||
add_spvtools_tool(TARGET spirv-dis SRCS dis/dis.cpp LIBS ${SPIRV_TOOLS})
|
||||
add_spvtools_tool(TARGET spirv-val SRCS val/val.cpp util/cli_consumer.cpp LIBS ${SPIRV_TOOLS})
|
||||
add_spvtools_tool(TARGET spirv-opt SRCS opt/opt.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS})
|
||||
add_spvtools_tool(TARGET spirv-reduce SRCS reduce/reduce.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-reduce ${SPIRV_TOOLS})
|
||||
add_spvtools_tool(TARGET spirv-link SRCS link/linker.cpp LIBS SPIRV-Tools-link ${SPIRV_TOOLS})
|
||||
add_spvtools_tool(TARGET spirv-stats
|
||||
SRCS stats/stats.cpp
|
||||
@ -61,7 +62,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES})
|
||||
${SPIRV_HEADER_INCLUDE_DIR})
|
||||
|
||||
set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val spirv-opt spirv-stats
|
||||
spirv-cfg spirv-link)
|
||||
spirv-cfg spirv-link spirv-reduce)
|
||||
|
||||
if(SPIRV_BUILD_COMPRESSION)
|
||||
add_spvtools_tool(TARGET spirv-markv
|
||||
|
230
tools/reduce/reduce.cpp
Normal file
230
tools/reduce/reduce.cpp
Normal file
@ -0,0 +1,230 @@
|
||||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/opt/log.h"
|
||||
#include "source/reduce/operand_to_const_reduction_pass.h"
|
||||
#include "source/reduce/reducer.h"
|
||||
#include "source/reduce/remove_unreferenced_instruction_reduction_pass.h"
|
||||
#include "source/spirv_reducer_options.h"
|
||||
#include "source/util/make_unique.h"
|
||||
#include "source/util/string_utils.h"
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
#include "tools/io.h"
|
||||
#include "tools/util/cli_consumer.h"
|
||||
|
||||
using namespace spvtools::reduce;
|
||||
|
||||
namespace {
|
||||
|
||||
using ErrorOrInt = std::pair<std::string, int>;
|
||||
|
||||
// Check that the std::system function can actually be used.
|
||||
bool CheckExecuteCommand() {
|
||||
int res = std::system(nullptr);
|
||||
return res != 0;
|
||||
}
|
||||
|
||||
// Execute a command using the shell.
|
||||
// Returns true if and only if the command's exit status was 0.
|
||||
bool ExecuteCommand(const std::string& command) {
|
||||
errno = 0;
|
||||
int status = std::system(command.c_str());
|
||||
assert(errno == 0 && "failed to execute command");
|
||||
// The result returned by 'system' is implementation-defined, but is
|
||||
// usually the case that the returned value is 0 when the command's exit
|
||||
// code was 0. We are assuming that here, and that's all we depend on.
|
||||
return status == 0;
|
||||
}
|
||||
|
||||
// Status and actions to perform after parsing command-line arguments.
|
||||
enum ReduceActions { REDUCE_CONTINUE, REDUCE_STOP };
|
||||
|
||||
struct ReduceStatus {
|
||||
ReduceActions action;
|
||||
int code;
|
||||
};
|
||||
|
||||
void PrintUsage(const char* program) {
|
||||
// NOTE: Please maintain flags in lexicographical order.
|
||||
printf(
|
||||
R"(%s - Reduce a SPIR-V binary file with respect to a user-provided
|
||||
interestingness test.
|
||||
|
||||
USAGE: %s [options] <input> <interestingness-test>
|
||||
|
||||
The SPIR-V binary is read from <input>.
|
||||
|
||||
Whether a binary is interesting is determined by <interestingness-test>, which
|
||||
is typically a script.
|
||||
|
||||
NOTE: The reducer is a work in progress.
|
||||
|
||||
Options (in lexicographical order):
|
||||
-h, --help
|
||||
Print this help.
|
||||
--step-limit
|
||||
32-bit unsigned integer specifying maximum number of
|
||||
steps the reducer will take before giving up.
|
||||
--version
|
||||
Display reducer version information.
|
||||
)",
|
||||
program, program);
|
||||
}
|
||||
|
||||
// Message consumer for this tool. Used to emit diagnostics during
|
||||
// initialization and setup. Note that |source| and |position| are irrelevant
|
||||
// here because we are still not processing a SPIR-V input file.
|
||||
void ReduceDiagnostic(spv_message_level_t level, const char* /*source*/,
|
||||
const spv_position_t& /*position*/, const char* message) {
|
||||
if (level == SPV_MSG_ERROR) {
|
||||
fprintf(stderr, "error: ");
|
||||
}
|
||||
fprintf(stderr, "%s\n", message);
|
||||
}
|
||||
|
||||
ReduceStatus ParseFlags(int argc, const char** argv, const char** in_file,
|
||||
const char** interestingness_test,
|
||||
spvtools::ReducerOptions* reducer_options) {
|
||||
uint32_t positional_arg_index = 0;
|
||||
|
||||
for (int argi = 1; argi < argc; ++argi) {
|
||||
const char* cur_arg = argv[argi];
|
||||
if ('-' == cur_arg[0]) {
|
||||
if (0 == strcmp(cur_arg, "--version")) {
|
||||
spvtools::Logf(ReduceDiagnostic, SPV_MSG_INFO, nullptr, {}, "%s\n",
|
||||
spvSoftwareVersionDetailsString());
|
||||
return {REDUCE_STOP, 0};
|
||||
} else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
|
||||
PrintUsage(argv[0]);
|
||||
return {REDUCE_STOP, 0};
|
||||
} else if ('\0' == cur_arg[1]) {
|
||||
// We do not support reduction from standard input. We could support
|
||||
// this if there was a compelling use case.
|
||||
PrintUsage(argv[0]);
|
||||
return {REDUCE_STOP, 0};
|
||||
} else if (0 == strncmp(cur_arg,
|
||||
"--step-limit=", sizeof("--step-limit=") - 1)) {
|
||||
const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
|
||||
char* end = nullptr;
|
||||
errno = 0;
|
||||
const auto step_limit =
|
||||
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 (positional_arg_index == 0) {
|
||||
// Input file name
|
||||
assert(!*in_file);
|
||||
*in_file = cur_arg;
|
||||
positional_arg_index++;
|
||||
} else if (positional_arg_index == 1) {
|
||||
assert(!*interestingness_test);
|
||||
*interestingness_test = cur_arg;
|
||||
positional_arg_index++;
|
||||
} else {
|
||||
spvtools::Error(ReduceDiagnostic, nullptr, {},
|
||||
"Too many positional arguments specified");
|
||||
return {REDUCE_STOP, 1};
|
||||
}
|
||||
}
|
||||
|
||||
if (!*in_file) {
|
||||
spvtools::Error(ReduceDiagnostic, nullptr, {}, "No input file specified");
|
||||
return {REDUCE_STOP, 1};
|
||||
}
|
||||
|
||||
if (!*interestingness_test) {
|
||||
spvtools::Error(ReduceDiagnostic, nullptr, {},
|
||||
"No interestingness test specified");
|
||||
return {REDUCE_STOP, 1};
|
||||
}
|
||||
|
||||
return {REDUCE_CONTINUE, 0};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_3;
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
const char* in_file = nullptr;
|
||||
const char* interestingness_test = nullptr;
|
||||
|
||||
spv_target_env target_env = kDefaultEnvironment;
|
||||
spvtools::ReducerOptions reducer_options;
|
||||
|
||||
ReduceStatus status =
|
||||
ParseFlags(argc, argv, &in_file, &interestingness_test, &reducer_options);
|
||||
|
||||
if (status.action == REDUCE_STOP) {
|
||||
return status.code;
|
||||
}
|
||||
|
||||
if (!CheckExecuteCommand()) {
|
||||
std::cerr << "could not find shell interpreter for executing a command"
|
||||
<< std::endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
Reducer reducer(target_env);
|
||||
|
||||
reducer.SetInterestingnessFunction(
|
||||
[interestingness_test](std::vector<uint32_t> binary,
|
||||
uint32_t reductions_applied) -> bool {
|
||||
std::stringstream ss;
|
||||
ss << "temp_" << std::setw(4) << std::setfill('0') << reductions_applied
|
||||
<< ".spv";
|
||||
const auto spv_file = ss.str();
|
||||
const std::string command =
|
||||
std::string(interestingness_test) + " " + spv_file;
|
||||
auto write_file_succeeded =
|
||||
WriteFile(spv_file.c_str(), "wb", &binary[0], binary.size());
|
||||
(void)(write_file_succeeded);
|
||||
assert(write_file_succeeded);
|
||||
return ExecuteCommand(command);
|
||||
});
|
||||
|
||||
reducer.AddReductionPass(
|
||||
spvtools::MakeUnique<OperandToConstReductionPass>(target_env));
|
||||
reducer.AddReductionPass(
|
||||
spvtools::MakeUnique<RemoveUnreferencedInstructionReductionPass>(
|
||||
target_env));
|
||||
|
||||
reducer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
|
||||
|
||||
std::vector<uint32_t> binary_in;
|
||||
if (!ReadFile<uint32_t>(in_file, "rb", &binary_in)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> binary_out;
|
||||
const auto reduction_status =
|
||||
reducer.Run(std::move(binary_in), &binary_out, reducer_options);
|
||||
|
||||
if (reduction_status ==
|
||||
Reducer::ReductionResultStatus::kInitialStateNotInteresting ||
|
||||
!WriteFile<uint32_t>("_reduced_final.spv", "wb", binary_out.data(),
|
||||
binary_out.size())) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user