From 71aa48f91dfa6690fa2da2fb6d4d3bf4f2753d16 Mon Sep 17 00:00:00 2001 From: Paul Thomson Date: Wed, 19 Dec 2018 13:25:56 +0000 Subject: [PATCH] spirv-reduce: add OperandToUndefReductionPass (#2200) * Add OperandToUndefReductionPass. Fixes #2115. Also added some tests that are similar to those in OperandToConstantReductionPassTest. In addition, refactor FindOrCreateGlobalUndef into reduction_util.cpp. Fixes #2184. Removed many documentation comments that were identical or very similar to the overridden function's documentation comment. --- source/reduce/CMakeLists.txt | 6 + .../change_operand_reduction_opportunity.h | 7 +- ...operand_to_undef_reduction_opportunity.cpp | 41 ++++ ...e_operand_to_undef_reduction_opportunity.h | 53 ++++ .../operand_to_const_reduction_pass.cpp | 5 +- .../reduce/operand_to_const_reduction_pass.h | 7 +- .../operand_to_dominating_id_reduction_pass.h | 3 - .../operand_to_undef_reduction_pass.cpp | 94 ++++++++ .../reduce/operand_to_undef_reduction_pass.h | 45 ++++ source/reduce/reduction_opportunity.h | 15 +- source/reduce/reduction_pass.h | 10 +- source/reduce/reduction_util.cpp | 44 ++++ source/reduce/reduction_util.h | 33 +++ ...remove_instruction_reduction_opportunity.h | 5 +- ...remove_opname_instruction_reduction_pass.h | 8 +- ..._unreferenced_instruction_reduction_pass.h | 5 +- ...oop_to_selection_reduction_opportunity.cpp | 29 +-- ..._loop_to_selection_reduction_opportunity.h | 17 +- ...uctured_loop_to_selection_reduction_pass.h | 3 - test/reduce/CMakeLists.txt | 1 + .../operand_to_undef_reduction_pass_test.cpp | 226 ++++++++++++++++++ tools/reduce/reduce.cpp | 3 + 22 files changed, 581 insertions(+), 79 deletions(-) create mode 100644 source/reduce/change_operand_to_undef_reduction_opportunity.cpp create mode 100644 source/reduce/change_operand_to_undef_reduction_opportunity.h create mode 100644 source/reduce/operand_to_undef_reduction_pass.cpp create mode 100644 source/reduce/operand_to_undef_reduction_pass.h create mode 100644 source/reduce/reduction_util.cpp create mode 100644 source/reduce/reduction_util.h create mode 100644 test/reduce/operand_to_undef_reduction_pass_test.cpp diff --git a/source/reduce/CMakeLists.txt b/source/reduce/CMakeLists.txt index 57475ac5e..0a6bce99c 100644 --- a/source/reduce/CMakeLists.txt +++ b/source/reduce/CMakeLists.txt @@ -13,11 +13,14 @@ # limitations under the License. set(SPIRV_TOOLS_REDUCE_SOURCES change_operand_reduction_opportunity.h + change_operand_to_undef_reduction_opportunity.h operand_to_const_reduction_pass.h + operand_to_undef_reduction_pass.h operand_to_dominating_id_reduction_pass.h reducer.h reduction_opportunity.h reduction_pass.h + reduction_util.h remove_instruction_reduction_opportunity.h remove_opname_instruction_reduction_pass.h remove_unreferenced_instruction_reduction_pass.h @@ -25,11 +28,14 @@ set(SPIRV_TOOLS_REDUCE_SOURCES structured_loop_to_selection_reduction_pass.h change_operand_reduction_opportunity.cpp + change_operand_to_undef_reduction_opportunity.cpp operand_to_const_reduction_pass.cpp + operand_to_undef_reduction_pass.cpp operand_to_dominating_id_reduction_pass.cpp reducer.cpp reduction_opportunity.cpp reduction_pass.cpp + reduction_util.cpp remove_instruction_reduction_opportunity.cpp remove_unreferenced_instruction_reduction_pass.cpp remove_opname_instruction_reduction_pass.cpp diff --git a/source/reduce/change_operand_reduction_opportunity.h b/source/reduce/change_operand_reduction_opportunity.h index 9e43ec969..7e1fc8e3b 100644 --- a/source/reduce/change_operand_reduction_opportunity.h +++ b/source/reduce/change_operand_reduction_opportunity.h @@ -24,8 +24,7 @@ namespace reduce { using namespace opt; -// Captures the opportunity to change an id operand of an instruction to some -// other id. +// An opportunity to replace an id operand of an instruction with some other id. class ChangeOperandReductionOpportunity : public ReductionOpportunity { public: // Constructs the opportunity to replace operand |operand_index| of |inst| @@ -38,13 +37,9 @@ class ChangeOperandReductionOpportunity : public ReductionOpportunity { 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: diff --git a/source/reduce/change_operand_to_undef_reduction_opportunity.cpp b/source/reduce/change_operand_to_undef_reduction_opportunity.cpp new file mode 100644 index 000000000..8e33da661 --- /dev/null +++ b/source/reduce/change_operand_to_undef_reduction_opportunity.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/change_operand_to_undef_reduction_opportunity.h" + +#include "source/opt/ir_context.h" +#include "source/reduce/reduction_util.h" + +namespace spvtools { +namespace reduce { + +bool ChangeOperandToUndefReductionOpportunity::PreconditionHolds() { + // Check that the instruction still has the original operand. + return inst_->NumOperands() > operand_index_ && + inst_->GetOperand(operand_index_).words[0] == original_id_; +} + +void ChangeOperandToUndefReductionOpportunity::Apply() { + auto operand = inst_->GetOperand(operand_index_); + auto operand_id = operand.words[0]; + auto operand_id_def = context_->get_def_use_mgr()->GetDef(operand_id); + auto operand_type_id = operand_id_def->type_id(); + // The opportunity should not exist unless this holds. + assert(operand_type_id); + auto undef_id = FindOrCreateGlobalUndef(context_, operand_type_id); + inst_->SetOperand(operand_index_, {undef_id}); +} + +} // namespace reduce +} // namespace spvtools diff --git a/source/reduce/change_operand_to_undef_reduction_opportunity.h b/source/reduce/change_operand_to_undef_reduction_opportunity.h new file mode 100644 index 000000000..ffd3155b0 --- /dev/null +++ b/source/reduce/change_operand_to_undef_reduction_opportunity.h @@ -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_CHANGE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_CHANGE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/instruction.h" +#include "source/reduce/reduction_opportunity.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to replace an id operand of an instruction with undef. +class ChangeOperandToUndefReductionOpportunity : public ReductionOpportunity { + public: + // Constructs the opportunity to replace operand |operand_index| of |inst| + // with undef. + ChangeOperandToUndefReductionOpportunity(opt::IRContext* context, + opt::Instruction* inst, + uint32_t operand_index) + : context_(context), + inst_(inst), + operand_index_(operand_index), + original_id_(inst->GetOperand(operand_index).words[0]) {} + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::IRContext* context_; + opt::Instruction* const inst_; + const uint32_t operand_index_; + const uint32_t original_id_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_CHANGE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_H_ diff --git a/source/reduce/operand_to_const_reduction_pass.cpp b/source/reduce/operand_to_const_reduction_pass.cpp index 35c0f4bee..4d04506e1 100644 --- a/source/reduce/operand_to_const_reduction_pass.cpp +++ b/source/reduce/operand_to_const_reduction_pass.cpp @@ -12,9 +12,10 @@ // 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/reduce/operand_to_const_reduction_pass.h" + #include "source/opt/instruction.h" +#include "source/reduce/change_operand_reduction_opportunity.h" namespace spvtools { namespace reduce { diff --git a/source/reduce/operand_to_const_reduction_pass.h b/source/reduce/operand_to_const_reduction_pass.h index fd7cfa006..4e7381e93 100644 --- a/source/reduce/operand_to_const_reduction_pass.h +++ b/source/reduce/operand_to_const_reduction_pass.h @@ -15,12 +15,12 @@ #ifndef SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_PASS_H_ #define SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_PASS_H_ -#include "reduction_pass.h" +#include "source/reduce/reduction_pass.h" namespace spvtools { namespace reduce { -// A reduction pass for turning id operands of instructions into ids of +// A reduction pass for replacing id operands of instructions with 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. @@ -33,12 +33,9 @@ class OperandToConstReductionPass : public ReductionPass { ~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> GetAvailableOpportunities( opt::IRContext* context) const final; diff --git a/source/reduce/operand_to_dominating_id_reduction_pass.h b/source/reduce/operand_to_dominating_id_reduction_pass.h index b62b6ae32..36bb20112 100644 --- a/source/reduce/operand_to_dominating_id_reduction_pass.h +++ b/source/reduce/operand_to_dominating_id_reduction_pass.h @@ -39,12 +39,9 @@ class OperandToDominatingIdReductionPass : public ReductionPass { ~OperandToDominatingIdReductionPass() override = default; - // The name of this pass. std::string GetName() const final; protected: - // Finds all opportunities for replacing an operand with a dominating - // instruction in a given module. std::vector> GetAvailableOpportunities( opt::IRContext* context) const final; diff --git a/source/reduce/operand_to_undef_reduction_pass.cpp b/source/reduce/operand_to_undef_reduction_pass.cpp new file mode 100644 index 000000000..e3d8a8ea3 --- /dev/null +++ b/source/reduce/operand_to_undef_reduction_pass.cpp @@ -0,0 +1,94 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/operand_to_undef_reduction_pass.h" + +#include "source/opt/instruction.h" +#include "source/reduce/change_operand_to_undef_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +using namespace opt; + +std::vector> +OperandToUndefReductionPass::GetAvailableOpportunities( + IRContext* context) const { + std::vector> result; + + for (auto& function : *context->module()) { + for (auto& block : function) { + for (auto& inst : block) { + // Skip instructions that result in a pointer type. + auto type_id = inst.type_id(); + if (type_id) { + auto type_id_def = context->get_def_use_mgr()->GetDef(type_id); + if (type_id_def->opcode() == SpvOpTypePointer) { + continue; + } + } + + // 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 ChangeOperandToUndefReductionOpportunity + for (uint32_t index = 0; index < inst.NumOperands(); index++) { + const auto& operand = inst.GetOperand(index); + + if (spvIsInIdType(operand.type)) { + const auto operand_id = operand.words[0]; + auto operand_id_def = + context->get_def_use_mgr()->GetDef(operand_id); + + // Skip constant and undef operands. + // We always want the reducer to make the module "smaller", which + // ensures termination. + // Therefore, we assume: id > undef id > constant id. + if (spvOpcodeIsConstantOrUndef(operand_id_def->opcode())) { + continue; + } + + // Don't replace function operands with undef. + if (operand_id_def->opcode() == SpvOpFunction) { + continue; + } + + // Only consider operands that have a type. + auto operand_type_id = operand_id_def->type_id(); + if (operand_type_id) { + auto operand_type_id_def = + context->get_def_use_mgr()->GetDef(operand_type_id); + + // Skip pointer operands. + if (operand_type_id_def->opcode() == SpvOpTypePointer) { + continue; + } + + result.push_back( + MakeUnique( + context, &inst, index)); + } + } + } + } + } + } + return result; +} + +std::string OperandToUndefReductionPass::GetName() const { + return "OperandToUndefReductionPass"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/source/reduce/operand_to_undef_reduction_pass.h b/source/reduce/operand_to_undef_reduction_pass.h new file mode 100644 index 000000000..e4ec603fb --- /dev/null +++ b/source/reduce/operand_to_undef_reduction_pass.h @@ -0,0 +1,45 @@ +// 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_UNDEF_REDUCTION_PASS_H_ +#define SOURCE_REDUCE_OPERAND_TO_UNDEF_REDUCTION_PASS_H_ + +#include "source/reduce/reduction_pass.h" + +namespace spvtools { +namespace reduce { + +// A reduction pass for replacing id operands of instructions with ids of undef. +class OperandToUndefReductionPass : public ReductionPass { + public: + // Creates the reduction pass in the context of the given target environment + // |target_env| + explicit OperandToUndefReductionPass(const spv_target_env target_env) + : ReductionPass(target_env) {} + + ~OperandToUndefReductionPass() override = default; + + std::string GetName() const final; + + protected: + std::vector> GetAvailableOpportunities( + opt::IRContext* context) const final; + + private: +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_OPERAND_TO_UNDEF_REDUCTION_PASS_H_ diff --git a/source/reduce/reduction_opportunity.h b/source/reduce/reduction_opportunity.h index da382a2f4..703a50a46 100644 --- a/source/reduce/reduction_opportunity.h +++ b/source/reduce/reduction_opportunity.h @@ -20,23 +20,24 @@ namespace spvtools { namespace reduce { -// Abstract class capturing an opportunity to apply a reducing transformation. +// Abstract class: 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. + // Returns true if this opportunity has not been disabled by the application + // of another conflicting opportunity. virtual bool PreconditionHolds() = 0; - // A no-op if PreconditoinHolds() returns false; otherwise applies the - // opportunity. + // Applies the opportunity, mutating the module from which the opportunity was + // created. It is a no-op if PreconditionHolds() returns false. void TryToApply(); protected: - // Apply the reduction opportunity. + // Applies the opportunity, mutating the module from which the opportunity was + // created. + // Precondition: PreconditionHolds() must return true. virtual void Apply() = 0; }; diff --git a/source/reduce/reduction_pass.h b/source/reduce/reduction_pass.h index afb95cc42..57e1c5f66 100644 --- a/source/reduce/reduction_pass.h +++ b/source/reduce/reduction_pass.h @@ -39,13 +39,13 @@ class ReductionPass { virtual ~ReductionPass() = default; - // Apply the reduction pass to the given binary. + // Applies the reduction pass to the given binary. std::vector TryApplyReduction(const std::vector& binary); - // Set a consumer to which relevant messages will be directed. + // Sets a consumer to which relevant messages will be directed. void SetMessageConsumer(MessageConsumer consumer); - // Determines whether the granularity with which reduction opportunities are + // Returns true if the granularity with which reduction opportunities are // applied has reached a minimum. bool ReachedMinimumGranularity() const; @@ -54,8 +54,8 @@ class ReductionPass { 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. + // Finds and returns the reduction opportunities relevant to this pass that + // could be applied to the given SPIR-V module. virtual std::vector> GetAvailableOpportunities(opt::IRContext* context) const = 0; diff --git a/source/reduce/reduction_util.cpp b/source/reduce/reduction_util.cpp new file mode 100644 index 000000000..103d63f19 --- /dev/null +++ b/source/reduce/reduction_util.cpp @@ -0,0 +1,44 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/reduction_util.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace reduce { + +using namespace opt; + +uint32_t FindOrCreateGlobalUndef(IRContext* context, uint32_t type_id) { + for (auto& inst : context->module()->types_values()) { + if (inst.opcode() != SpvOpUndef) { + continue; + } + if (inst.type_id() == type_id) { + return inst.result_id(); + } + } + // TODO(2182): this is adapted from MemPass::Type2Undef. In due course it + // would be good to factor out this duplication. + const uint32_t undef_id = context->TakeNextId(); + std::unique_ptr undef_inst( + new Instruction(context, SpvOpUndef, type_id, undef_id, {})); + assert(undef_id == undef_inst->result_id()); + context->module()->AddGlobalValue(std::move(undef_inst)); + return undef_id; +} + +} // namespace reduce +} // namespace spvtools diff --git a/source/reduce/reduction_util.h b/source/reduce/reduction_util.h new file mode 100644 index 000000000..d8efc9705 --- /dev/null +++ b/source/reduce/reduction_util.h @@ -0,0 +1,33 @@ +// 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_UTIL_H_ +#define SOURCE_REDUCE_REDUCTION_UTIL_H_ + +#include "spirv-tools/libspirv.hpp" + +#include "source/opt/ir_context.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// Returns an OpUndef id from the global value list that is of the given type, +// adding one if it does not exist. +uint32_t FindOrCreateGlobalUndef(opt::IRContext* context, uint32_t type_id); + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REDUCTION_UTIL_H_ diff --git a/source/reduce/remove_instruction_reduction_opportunity.h b/source/reduce/remove_instruction_reduction_opportunity.h index 471ff15e3..e9f442e3f 100644 --- a/source/reduce/remove_instruction_reduction_opportunity.h +++ b/source/reduce/remove_instruction_reduction_opportunity.h @@ -23,18 +23,17 @@ namespace reduce { using namespace opt; -// Captures the opportunity to remove an instruction from the SPIR-V module. +// An 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. + // Always returns true, as this opportunity can always be applied. bool PreconditionHolds() override; protected: - // Remove the instruction. void Apply() override; private: diff --git a/source/reduce/remove_opname_instruction_reduction_pass.h b/source/reduce/remove_opname_instruction_reduction_pass.h index d20b6e182..2990a49cc 100644 --- a/source/reduce/remove_opname_instruction_reduction_pass.h +++ b/source/reduce/remove_opname_instruction_reduction_pass.h @@ -21,8 +21,9 @@ namespace spvtools { namespace reduce { // A reduction pass for removing OpName instructions. As well as making the -// module smaller, removing an OpName instruction may create opportunities to -// remove the instruction that create the id to which the OpName applies. +// module smaller, removing an OpName instruction may create opportunities +// for subsequently removing the instructions that create the ids to which the +// OpName applies. class RemoveOpNameInstructionReductionPass : public ReductionPass { public: // Creates the reduction pass in the context of the given target environment @@ -32,12 +33,9 @@ class RemoveOpNameInstructionReductionPass : public ReductionPass { ~RemoveOpNameInstructionReductionPass() override = default; - // The name of this pass. std::string GetName() const final; protected: - // Finds all opportunities for removing opName instructions in the - // given module. std::vector> GetAvailableOpportunities( opt::IRContext* context) const final; diff --git a/source/reduce/remove_unreferenced_instruction_reduction_pass.h b/source/reduce/remove_unreferenced_instruction_reduction_pass.h index 27c3fc2d4..44a0d55e9 100644 --- a/source/reduce/remove_unreferenced_instruction_reduction_pass.h +++ b/source/reduce/remove_unreferenced_instruction_reduction_pass.h @@ -22,7 +22,7 @@ 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 +// module smaller, removing an instruction that references particular ids may // create opportunities for subsequently removing the instructions that // generated those ids. class RemoveUnreferencedInstructionReductionPass : public ReductionPass { @@ -35,12 +35,9 @@ class RemoveUnreferencedInstructionReductionPass : public ReductionPass { ~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> GetAvailableOpportunities( opt::IRContext* context) const final; diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp index 679cfc1a6..bf0e085ad 100644 --- a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp +++ b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "structured_loop_to_selection_reduction_opportunity.h" +#include "source/reduce/structured_loop_to_selection_reduction_opportunity.h" + #include "source/opt/aggressive_dead_code_elim_pass.h" #include "source/opt/ir_context.h" +#include "source/reduce/reduction_util.h" namespace spvtools { namespace reduce { @@ -191,7 +193,7 @@ void StructuredLoopToSelectionReductionOpportunity:: to_block->ForEachPhiInst([this, &from_id](Instruction* phi_inst) { // Add to the phi operand an (undef, from_id) pair to reflect the added // edge. - auto undef_id = FindOrCreateGlobalUndef(phi_inst->type_id()); + auto undef_id = FindOrCreateGlobalUndef(context_, phi_inst->type_id()); phi_inst->AddOperand(Operand(SPV_OPERAND_TYPE_ID, {undef_id})); phi_inst->AddOperand(Operand(SPV_OPERAND_TYPE_ID, {from_id})); }); @@ -273,7 +275,8 @@ void StructuredLoopToSelectionReductionOpportunity::FixNonDominatedIdUses() { break; } } else { - use->SetOperand(index, {FindOrCreateGlobalUndef(def.type_id())}); + use->SetOperand(index, + {FindOrCreateGlobalUndef(context_, def.type_id())}); } } }); @@ -296,26 +299,6 @@ bool StructuredLoopToSelectionReductionOpportunity:: ->Dominates(def, use); } -uint32_t StructuredLoopToSelectionReductionOpportunity::FindOrCreateGlobalUndef( - uint32_t type_id) { - for (auto& inst : context_->module()->types_values()) { - if (inst.opcode() != SpvOpUndef) { - continue; - } - if (inst.type_id() == type_id) { - return inst.result_id(); - } - } - // TODO(2182): this is adapted from MemPass::Type2Undef. In due course it - // would be good to factor out this duplication. - const uint32_t undef_id = context_->TakeNextId(); - std::unique_ptr undef_inst( - new Instruction(context_, SpvOpUndef, type_id, undef_id, {})); - assert(undef_id == undef_inst->result_id()); - context_->module()->AddGlobalValue(std::move(undef_inst)); - return undef_id; -} - uint32_t StructuredLoopToSelectionReductionOpportunity::FindOrCreateGlobalVariable( uint32_t pointer_type_id) { diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity.h b/source/reduce/structured_loop_to_selection_reduction_opportunity.h index b13901683..71b0f0eb4 100644 --- a/source/reduce/structured_loop_to_selection_reduction_opportunity.h +++ b/source/reduce/structured_loop_to_selection_reduction_opportunity.h @@ -15,17 +15,17 @@ #ifndef SOURCE_REDUCE_CUT_LOOP_REDUCTION_OPPORTUNITY_H_ #define SOURCE_REDUCE_CUT_LOOP_REDUCTION_OPPORTUNITY_H_ -#include -#include "reduction_opportunity.h" +#include "source/opt/def_use_manager.h" #include "source/opt/dominator_analysis.h" #include "source/opt/function.h" +#include "source/reduce/reduction_opportunity.h" namespace spvtools { namespace reduce { using namespace opt; -// Captures an opportunity to replace a structured loop with a selection. +// An opportunity to replace a structured loop with a selection. class StructuredLoopToSelectionReductionOpportunity : public ReductionOpportunity { public: @@ -38,13 +38,12 @@ class StructuredLoopToSelectionReductionOpportunity loop_construct_header_(loop_construct_header), enclosing_function_(enclosing_function) {} - // We require the loop header to be reachable. A structured loop might + // Returns true if the loop header is reachable. A structured loop might // become unreachable as a result of turning another structured loop into // a selection. bool PreconditionHolds() override; protected: - // Perform the structured loop to selection transformation. void Apply() override; private: @@ -92,14 +91,6 @@ class StructuredLoopToSelectionReductionOpportunity uint32_t use_index, BasicBlock& def_block); - // Checks whether the global value list has an OpUndef of the given type, - // adding one if not, and returns the id of such an OpUndef. - // - // TODO(2184): This will likely be used by other reduction passes, so should - // be factored out in due course. Parts of the spirv-opt framework provide - // similar functionality, so there may be a case for further refactoring. - uint32_t FindOrCreateGlobalUndef(uint32_t type_id); - // Checks whether the global value list has an OpVariable of the given pointer // type, adding one if not, and returns the id of such an OpVariable. // diff --git a/source/reduce/structured_loop_to_selection_reduction_pass.h b/source/reduce/structured_loop_to_selection_reduction_pass.h index 9c4d4caee..a1f88bc54 100644 --- a/source/reduce/structured_loop_to_selection_reduction_pass.h +++ b/source/reduce/structured_loop_to_selection_reduction_pass.h @@ -46,12 +46,9 @@ class StructuredLoopToSelectionReductionPass : public ReductionPass { ~StructuredLoopToSelectionReductionPass() override = default; - // The name of this pass. std::string GetName() const final; protected: - // Finds all opportunities for transforming a structured loop to a selection - // in the given module. std::vector> GetAvailableOpportunities( opt::IRContext* context) const final; diff --git a/test/reduce/CMakeLists.txt b/test/reduce/CMakeLists.txt index cdd27b9e1..b35cdb260 100644 --- a/test/reduce/CMakeLists.txt +++ b/test/reduce/CMakeLists.txt @@ -14,6 +14,7 @@ add_spvtools_unittest(TARGET reduce SRCS operand_to_constant_reduction_pass_test.cpp + operand_to_undef_reduction_pass_test.cpp operand_to_dominating_id_reduction_pass_test.cpp reduce_test_util.cpp reduce_test_util.h diff --git a/test/reduce/operand_to_undef_reduction_pass_test.cpp b/test/reduce/operand_to_undef_reduction_pass_test.cpp new file mode 100644 index 000000000..71bf96cf4 --- /dev/null +++ b/test/reduce/operand_to_undef_reduction_pass_test.cpp @@ -0,0 +1,226 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/operand_to_undef_reduction_pass.h" +#include "source/opt/build_module.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +TEST(OperandToUndefReductionPassTest, BasicCheck) { + // The following shader has 10 opportunities for replacing with undef. + + // #version 310 es + // + // precision highp float; + // + // layout(location=0) out vec4 _GLF_color; + // + // layout(set = 0, binding = 0) uniform buf0 { + // vec2 uniform1; + // }; + // + // void main() + // { + // _GLF_color = + // vec4( // opportunity + // uniform1.x / 2.0, // opportunity x2 (2.0 is const) + // uniform1.y / uniform1.x, // opportunity x3 + // uniform1.x + uniform1.x, // opportunity x3 + // uniform1.y); // opportunity + // } + + std::string original = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "_GLF_color" + OpName %11 "buf0" + OpMemberName %11 0 "uniform1" + OpName %13 "" + OpDecorate %9 Location 0 + OpMemberDecorate %11 0 Offset 0 + OpDecorate %11 Block + OpDecorate %13 DescriptorSet 0 + OpDecorate %13 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypeVector %6 2 + %11 = OpTypeStruct %10 + %12 = OpTypePointer Uniform %11 + %13 = OpVariable %12 Uniform + %14 = OpTypeInt 32 1 + %15 = OpConstant %14 0 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 0 + %18 = OpTypePointer Uniform %6 + %21 = OpConstant %6 2 + %23 = OpConstant %16 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %19 = OpAccessChain %18 %13 %15 %17 + %20 = OpLoad %6 %19 + %22 = OpFDiv %6 %20 %21 ; opportunity %20 (%21 is const) + %24 = OpAccessChain %18 %13 %15 %23 + %25 = OpLoad %6 %24 + %26 = OpAccessChain %18 %13 %15 %17 + %27 = OpLoad %6 %26 + %28 = OpFDiv %6 %25 %27 ; opportunity %25 %27 + %29 = OpAccessChain %18 %13 %15 %17 + %30 = OpLoad %6 %29 + %31 = OpAccessChain %18 %13 %15 %17 + %32 = OpLoad %6 %31 + %33 = OpFAdd %6 %30 %32 ; opportunity %30 %32 + %34 = OpAccessChain %18 %13 %15 %23 + %35 = OpLoad %6 %34 + %36 = OpCompositeConstruct %7 %22 %28 %33 %35 ; opportunity %22 %28 %33 %35 + OpStore %9 %36 ; opportunity %36 + OpReturn + OpFunctionEnd + )"; + + // This is the same as original, except where noted. + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "_GLF_color" + OpName %11 "buf0" + OpMemberName %11 0 "uniform1" + OpName %13 "" + OpDecorate %9 Location 0 + OpMemberDecorate %11 0 Offset 0 + OpDecorate %11 Block + OpDecorate %13 DescriptorSet 0 + OpDecorate %13 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypeVector %6 2 + %11 = OpTypeStruct %10 + %12 = OpTypePointer Uniform %11 + %13 = OpVariable %12 Uniform + %14 = OpTypeInt 32 1 + %15 = OpConstant %14 0 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 0 + %18 = OpTypePointer Uniform %6 + %21 = OpConstant %6 2 + %23 = OpConstant %16 1 + %37 = OpUndef %6 ; Added undef float as %37 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %19 = OpAccessChain %18 %13 %15 %17 + %20 = OpLoad %6 %19 + %22 = OpFDiv %6 %37 %21 ; Replaced with %37 + %24 = OpAccessChain %18 %13 %15 %23 + %25 = OpLoad %6 %24 + %26 = OpAccessChain %18 %13 %15 %17 + %27 = OpLoad %6 %26 + %28 = OpFDiv %6 %37 %37 ; Replaced with %37 twice + %29 = OpAccessChain %18 %13 %15 %17 + %30 = OpLoad %6 %29 + %31 = OpAccessChain %18 %13 %15 %17 + %32 = OpLoad %6 %31 + %33 = OpFAdd %6 %30 %32 + %34 = OpAccessChain %18 %13 %15 %23 + %35 = OpLoad %6 %34 + %36 = OpCompositeConstruct %7 %22 %28 %33 %35 + OpStore %9 %36 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, original, kReduceAssembleOption); + const auto pass = TestSubclass(env); + const auto ops = pass.WrapGetAvailableOpportunities(context.get()); + + ASSERT_EQ(10, ops.size()); + + // Apply first three opportunities. + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + ASSERT_TRUE(ops[2]->PreconditionHolds()); + ops[2]->TryToApply(); + + CheckEqual(env, expected, context.get()); +} + +TEST(OperandToUndefReductionPassTest, WithCalledFunction) { + // The following shader has no opportunities. + // Most importantly, the noted function operand is not changed. + + 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 ; do not replace %16 with undef + 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(env); + const auto ops = pass.WrapGetAvailableOpportunities(context.get()); + ASSERT_EQ(0, ops.size()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/tools/reduce/reduce.cpp b/tools/reduce/reduce.cpp index 390724a6d..65325f745 100644 --- a/tools/reduce/reduce.cpp +++ b/tools/reduce/reduce.cpp @@ -22,6 +22,7 @@ #include "source/opt/log.h" #include "source/reduce/operand_to_const_reduction_pass.h" #include "source/reduce/operand_to_dominating_id_reduction_pass.h" +#include "source/reduce/operand_to_undef_reduction_pass.h" #include "source/reduce/reducer.h" #include "source/reduce/remove_opname_instruction_reduction_pass.h" #include "source/reduce/remove_unreferenced_instruction_reduction_pass.h" @@ -207,6 +208,8 @@ int main(int argc, const char** argv) { reducer.AddReductionPass( spvtools::MakeUnique(target_env)); + reducer.AddReductionPass( + spvtools::MakeUnique(target_env)); reducer.AddReductionPass( spvtools::MakeUnique(target_env)); reducer.AddReductionPass(