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.
diff --git a/source/reduce/CMakeLists.txt b/source/reduce/CMakeLists.txt
index 57475ac..0a6bce9 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 @@
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 9e43ec9..7e1fc8e 100644
--- a/source/reduce/change_operand_reduction_opportunity.h
+++ b/source/reduce/change_operand_reduction_opportunity.h
@@ -24,8 +24,7 @@
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 @@
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 0000000..8e33da6
--- /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 0000000..ffd3155
--- /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 35c0f4b..4d04506 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 fd7cfa0..4e7381e 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 @@
~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;
diff --git a/source/reduce/operand_to_dominating_id_reduction_pass.h b/source/reduce/operand_to_dominating_id_reduction_pass.h
index b62b6ae..36bb201 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 @@
~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<std::unique_ptr<ReductionOpportunity>> 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 0000000..e3d8a8e
--- /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<std::unique_ptr<ReductionOpportunity>>
+OperandToUndefReductionPass::GetAvailableOpportunities(
+ IRContext* context) const {
+ std::vector<std::unique_ptr<ReductionOpportunity>> 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<ChangeOperandToUndefReductionOpportunity>(
+ 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 0000000..e4ec603
--- /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<std::unique_ptr<ReductionOpportunity>> 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 da382a2..703a50a 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 afb95cc..57e1c5f 100644
--- a/source/reduce/reduction_pass.h
+++ b/source/reduce/reduction_pass.h
@@ -39,13 +39,13 @@
virtual ~ReductionPass() = default;
- // Apply the reduction pass to the given binary.
+ // Applies 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.
+ // 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 @@
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<std::unique_ptr<ReductionOpportunity>>
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 0000000..103d63f
--- /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<Instruction> 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 0000000..d8efc97
--- /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 471ff15..e9f442e 100644
--- a/source/reduce/remove_instruction_reduction_opportunity.h
+++ b/source/reduce/remove_instruction_reduction_opportunity.h
@@ -23,18 +23,17 @@
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 d20b6e1..2990a49 100644
--- a/source/reduce/remove_opname_instruction_reduction_pass.h
+++ b/source/reduce/remove_opname_instruction_reduction_pass.h
@@ -21,8 +21,9 @@
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 @@
~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<std::unique_ptr<ReductionOpportunity>> 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 27c3fc2..44a0d55 100644
--- a/source/reduce/remove_unreferenced_instruction_reduction_pass.h
+++ b/source/reduce/remove_unreferenced_instruction_reduction_pass.h
@@ -22,7 +22,7 @@
// 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 @@
~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;
diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp
index 679cfc1..bf0e085 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 @@
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 @@
break;
}
} else {
- use->SetOperand(index, {FindOrCreateGlobalUndef(def.type_id())});
+ use->SetOperand(index,
+ {FindOrCreateGlobalUndef(context_, def.type_id())});
}
}
});
@@ -296,26 +299,6 @@
->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<Instruction> 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 b139016..71b0f0e 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 <source/opt/def_use_manager.h>
-#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 @@
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 @@
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 9c4d4ca..a1f88bc 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 @@
~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<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
diff --git a/test/reduce/CMakeLists.txt b/test/reduce/CMakeLists.txt
index cdd27b9..b35cdb2 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 0000000..71bf96c
--- /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<OperandToUndefReductionPass>(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<OperandToUndefReductionPass>(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 390724a..65325f7 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"
@@ -208,6 +209,8 @@
reducer.AddReductionPass(
spvtools::MakeUnique<RemoveOpNameInstructionReductionPass>(target_env));
reducer.AddReductionPass(
+ spvtools::MakeUnique<OperandToUndefReductionPass>(target_env));
+ reducer.AddReductionPass(
spvtools::MakeUnique<OperandToConstReductionPass>(target_env));
reducer.AddReductionPass(
spvtools::MakeUnique<OperandToDominatingIdReductionPass>(target_env));