mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-22 19:50:05 +00:00
reduce: Add two branch reduction passes (#2507)
* Fix #2320. `conditional_branch_to_simple_conditional_branch` reduction pass changes conditional branches so both targets point to the same block id (creating a "simple" conditional branch). * Fix #2501. `simple_conditional_branch_to_branch` reduction pass changes "simple" conditional branches to branches. * Fix #2503. `conditional_branch_to_simple_conditional_branch` proper handling of back-edges.
This commit is contained in:
parent
102e430a88
commit
3335c61147
@ -35,6 +35,10 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
|
||||
remove_unreferenced_instruction_reduction_opportunity_finder.h
|
||||
structured_loop_to_selection_reduction_opportunity.h
|
||||
structured_loop_to_selection_reduction_opportunity_finder.h
|
||||
conditional_branch_to_simple_conditional_branch_opportunity_finder.h
|
||||
conditional_branch_to_simple_conditional_branch_reduction_opportunity.h
|
||||
simple_conditional_branch_to_branch_opportunity_finder.h
|
||||
simple_conditional_branch_to_branch_reduction_opportunity.h
|
||||
|
||||
change_operand_reduction_opportunity.cpp
|
||||
change_operand_to_undef_reduction_opportunity.cpp
|
||||
@ -58,6 +62,10 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
|
||||
remove_opname_instruction_reduction_opportunity_finder.cpp
|
||||
structured_loop_to_selection_reduction_opportunity.cpp
|
||||
structured_loop_to_selection_reduction_opportunity_finder.cpp
|
||||
conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp
|
||||
conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp
|
||||
simple_conditional_branch_to_branch_opportunity_finder.cpp
|
||||
simple_conditional_branch_to_branch_reduction_opportunity.cpp
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
|
@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2019 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/conditional_branch_to_simple_conditional_branch_opportunity_finder.h"
|
||||
|
||||
#include "source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h"
|
||||
#include "source/reduce/reduction_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
using opt::IRContext;
|
||||
using opt::Instruction;
|
||||
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>>
|
||||
ConditionalBranchToSimpleConditionalBranchOpportunityFinder::
|
||||
GetAvailableOpportunities(IRContext* context) const {
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> result;
|
||||
|
||||
// Find the opportunities for redirecting all false targets before the
|
||||
// opportunities for redirecting all true targets because the former
|
||||
// opportunities disable the latter, and vice versa, and the efficiency of the
|
||||
// reducer is improved by avoiding contiguous opportunities that disable one
|
||||
// another.
|
||||
for (bool redirect_to_true : {true, false}) {
|
||||
// Consider every function.
|
||||
for (auto& function : *context->module()) {
|
||||
// Consider every block in the function.
|
||||
for (auto& block : function) {
|
||||
// The terminator must be SpvOpBranchConditional.
|
||||
Instruction* terminator = block.terminator();
|
||||
if (terminator->opcode() != SpvOpBranchConditional) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t true_block_id =
|
||||
terminator->GetSingleWordInOperand(kTrueBranchOperandIndex);
|
||||
uint32_t false_block_id =
|
||||
terminator->GetSingleWordInOperand(kFalseBranchOperandIndex);
|
||||
|
||||
// The conditional branch must not already be simplified.
|
||||
if (true_block_id == false_block_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The redirected target must not be a back-edge to a structured loop
|
||||
// header.
|
||||
uint32_t redirected_block_id =
|
||||
redirect_to_true ? false_block_id : true_block_id;
|
||||
uint32_t containing_loop_header =
|
||||
context->GetStructuredCFGAnalysis()->ContainingLoop(block.id());
|
||||
// The structured CFG analysis does not include a loop header as part
|
||||
// of the loop construct, but we want to include it, so handle this
|
||||
// special case:
|
||||
if (block.GetLoopMergeInst() != nullptr) {
|
||||
containing_loop_header = block.id();
|
||||
}
|
||||
if (redirected_block_id == containing_loop_header) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push_back(
|
||||
MakeUnique<
|
||||
ConditionalBranchToSimpleConditionalBranchReductionOpportunity>(
|
||||
block.terminator(), redirect_to_true));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string
|
||||
ConditionalBranchToSimpleConditionalBranchOpportunityFinder::GetName() const {
|
||||
return "ConditionalBranchToSimpleConditionalBranchOpportunityFinder";
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2019 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_SIMPLIFY_SELECTION_OPPORTUNITY_FINDER_H_
|
||||
#define SOURCE_REDUCE_SIMPLIFY_SELECTION_OPPORTUNITY_FINDER_H_
|
||||
|
||||
#include "source/reduce/reduction_opportunity_finder.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// A finder for opportunities to simplify conditional branches into simple
|
||||
// conditional branches (conditional branches with one target).
|
||||
class ConditionalBranchToSimpleConditionalBranchOpportunityFinder
|
||||
: public ReductionOpportunityFinder {
|
||||
public:
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
|
||||
opt::IRContext* context) const override;
|
||||
|
||||
std::string GetName() const override;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_SIMPLIFY_SELECTION_OPPORTUNITY_FINDER_H_
|
@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2019 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/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h"
|
||||
|
||||
#include "source/reduce/reduction_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
using opt::Instruction;
|
||||
|
||||
ConditionalBranchToSimpleConditionalBranchReductionOpportunity::
|
||||
ConditionalBranchToSimpleConditionalBranchReductionOpportunity(
|
||||
Instruction* conditional_branch_instruction, bool redirect_to_true)
|
||||
: conditional_branch_instruction_(conditional_branch_instruction),
|
||||
redirect_to_true_(redirect_to_true) {}
|
||||
|
||||
bool ConditionalBranchToSimpleConditionalBranchReductionOpportunity::
|
||||
PreconditionHolds() {
|
||||
// Another opportunity may have already simplified this conditional branch,
|
||||
// which should disable this opportunity.
|
||||
return conditional_branch_instruction_->GetSingleWordInOperand(
|
||||
kTrueBranchOperandIndex) !=
|
||||
conditional_branch_instruction_->GetSingleWordInOperand(
|
||||
kFalseBranchOperandIndex);
|
||||
}
|
||||
|
||||
void ConditionalBranchToSimpleConditionalBranchReductionOpportunity::Apply() {
|
||||
uint32_t operand_to_modify =
|
||||
redirect_to_true_ ? kFalseBranchOperandIndex : kTrueBranchOperandIndex;
|
||||
uint32_t operand_to_copy =
|
||||
redirect_to_true_ ? kTrueBranchOperandIndex : kFalseBranchOperandIndex;
|
||||
|
||||
// Do the branch redirection.
|
||||
conditional_branch_instruction_->SetInOperand(
|
||||
operand_to_modify,
|
||||
{conditional_branch_instruction_->GetSingleWordInOperand(
|
||||
operand_to_copy)});
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2019 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_SIMPLIFY_CONDITIONAL_BRANCH_REDUCTION_OPPORTUNITY_H_
|
||||
#define SOURCE_REDUCE_SIMPLIFY_CONDITIONAL_BRANCH_REDUCTION_OPPORTUNITY_H_
|
||||
|
||||
#include "source/opt/basic_block.h"
|
||||
#include "source/reduce/reduction_opportunity.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// An opportunity to simplify a conditional branch to a simple conditional
|
||||
// branch (a conditional branch with one target).
|
||||
class ConditionalBranchToSimpleConditionalBranchReductionOpportunity
|
||||
: public ReductionOpportunity {
|
||||
public:
|
||||
// Constructs an opportunity to simplify |conditional_branch_instruction|. If
|
||||
// |redirect_to_true| is true, the false target will be changed to also point
|
||||
// to the true target; otherwise, the true target will be changed to also
|
||||
// point to the false target.
|
||||
explicit ConditionalBranchToSimpleConditionalBranchReductionOpportunity(
|
||||
opt::Instruction* conditional_branch_instruction, bool redirect_to_true);
|
||||
|
||||
bool PreconditionHolds() override;
|
||||
|
||||
protected:
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
opt::Instruction* conditional_branch_instruction_;
|
||||
|
||||
// If true, the false target will be changed to point to the true target;
|
||||
// otherwise, the true target will be changed to point to the false target.
|
||||
bool redirect_to_true_;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_SIMPLIFY_CONDITIONAL_BRANCH_REDUCTION_OPPORTUNITY_H_
|
@ -17,6 +17,7 @@
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
|
||||
#include "source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h"
|
||||
#include "source/reduce/merge_blocks_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h"
|
||||
@ -26,6 +27,7 @@
|
||||
#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/remove_selection_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
|
||||
#include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h"
|
||||
#include "source/spirv_reducer_options.h"
|
||||
|
||||
@ -191,6 +193,11 @@ void Reducer::AddDefaultReductionPasses() {
|
||||
spvtools::MakeUnique<RemoveBlockReductionOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
spvtools::MakeUnique<RemoveSelectionReductionOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
spvtools::MakeUnique<
|
||||
ConditionalBranchToSimpleConditionalBranchOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
spvtools::MakeUnique<SimpleConditionalBranchToBranchOpportunityFinder>());
|
||||
}
|
||||
|
||||
void Reducer::AddReductionPass(
|
||||
|
@ -22,6 +22,9 @@ namespace reduce {
|
||||
using opt::IRContext;
|
||||
using opt::Instruction;
|
||||
|
||||
const uint32_t kTrueBranchOperandIndex = 1;
|
||||
const uint32_t kFalseBranchOperandIndex = 2;
|
||||
|
||||
uint32_t FindOrCreateGlobalUndef(IRContext* context, uint32_t type_id) {
|
||||
for (auto& inst : context->module()->types_values()) {
|
||||
if (inst.opcode() != SpvOpUndef) {
|
||||
|
@ -23,6 +23,9 @@
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
extern const uint32_t kTrueBranchOperandIndex;
|
||||
extern const uint32_t kFalseBranchOperandIndex;
|
||||
|
||||
// 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);
|
||||
|
@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2019 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/simple_conditional_branch_to_branch_opportunity_finder.h"
|
||||
|
||||
#include "source/reduce/reduction_util.h"
|
||||
#include "source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
using opt::IRContext;
|
||||
using opt::Instruction;
|
||||
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>>
|
||||
SimpleConditionalBranchToBranchOpportunityFinder::GetAvailableOpportunities(
|
||||
IRContext* context) const {
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> result;
|
||||
|
||||
// Consider every function.
|
||||
for (auto& function : *context->module()) {
|
||||
// Consider every block in the function.
|
||||
for (auto& block : function) {
|
||||
// The terminator must be SpvOpBranchConditional.
|
||||
Instruction* terminator = block.terminator();
|
||||
if (terminator->opcode() != SpvOpBranchConditional) {
|
||||
continue;
|
||||
}
|
||||
// It must not be a selection header, as these cannot be followed by
|
||||
// OpBranch.
|
||||
if (block.GetMergeInst() &&
|
||||
block.GetMergeInst()->opcode() == SpvOpSelectionMerge) {
|
||||
continue;
|
||||
}
|
||||
// The conditional branch must be simplified.
|
||||
if (terminator->GetSingleWordInOperand(kTrueBranchOperandIndex) !=
|
||||
terminator->GetSingleWordInOperand(kFalseBranchOperandIndex)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push_back(
|
||||
MakeUnique<SimpleConditionalBranchToBranchReductionOpportunity>(
|
||||
block.terminator()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string SimpleConditionalBranchToBranchOpportunityFinder::GetName() const {
|
||||
return "SimpleConditionalBranchToBranchOpportunityFinder";
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2019 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_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_OPPORTUNITY_FINDER_H_
|
||||
#define SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_OPPORTUNITY_FINDER_H_
|
||||
|
||||
#include "source/reduce/reduction_opportunity_finder.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// A finder for opportunities to change simple conditional branches (conditional
|
||||
// branches with one target) to an OpBranch.
|
||||
class SimpleConditionalBranchToBranchOpportunityFinder
|
||||
: public ReductionOpportunityFinder {
|
||||
public:
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
|
||||
opt::IRContext* context) const override;
|
||||
|
||||
std::string GetName() const override;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_OPPORTUNITY_FINDER_H_
|
@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2019 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/simple_conditional_branch_to_branch_reduction_opportunity.h"
|
||||
|
||||
#include "source/reduce/reduction_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
using namespace opt;
|
||||
|
||||
SimpleConditionalBranchToBranchReductionOpportunity::
|
||||
SimpleConditionalBranchToBranchReductionOpportunity(
|
||||
Instruction* conditional_branch_instruction)
|
||||
: conditional_branch_instruction_(conditional_branch_instruction) {}
|
||||
|
||||
bool SimpleConditionalBranchToBranchReductionOpportunity::PreconditionHolds() {
|
||||
// We find at most one opportunity per conditional branch and simplifying
|
||||
// another branch cannot disable this opportunity.
|
||||
return true;
|
||||
}
|
||||
|
||||
void SimpleConditionalBranchToBranchReductionOpportunity::Apply() {
|
||||
assert(conditional_branch_instruction_->opcode() == SpvOpBranchConditional &&
|
||||
"SimpleConditionalBranchToBranchReductionOpportunity: branch was not "
|
||||
"a conditional branch");
|
||||
|
||||
assert(conditional_branch_instruction_->GetSingleWordInOperand(
|
||||
kTrueBranchOperandIndex) ==
|
||||
conditional_branch_instruction_->GetSingleWordInOperand(
|
||||
kFalseBranchOperandIndex) &&
|
||||
"SimpleConditionalBranchToBranchReductionOpportunity: branch was not "
|
||||
"simple");
|
||||
|
||||
// OpBranchConditional %condition %block_id %block_id ...
|
||||
// ->
|
||||
// OpBranch %block_id
|
||||
|
||||
conditional_branch_instruction_->SetOpcode(SpvOpBranch);
|
||||
conditional_branch_instruction_->ReplaceOperands(
|
||||
{{SPV_OPERAND_TYPE_ID,
|
||||
{conditional_branch_instruction_->GetSingleWordInOperand(
|
||||
kTrueBranchOperandIndex)}}});
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2019 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_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_REDUCTION_OPPORTUNITY_H_
|
||||
#define SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_REDUCTION_OPPORTUNITY_H_
|
||||
|
||||
#include "source/opt/instruction.h"
|
||||
#include "source/reduce/reduction_opportunity.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// An opportunity to change simple conditional branches (conditional branches
|
||||
// with one target) to an OpBranch.
|
||||
class SimpleConditionalBranchToBranchReductionOpportunity
|
||||
: public ReductionOpportunity {
|
||||
public:
|
||||
// Constructs an opportunity to simplify |conditional_branch_instruction|.
|
||||
explicit SimpleConditionalBranchToBranchReductionOpportunity(
|
||||
opt::Instruction* conditional_branch_instruction);
|
||||
|
||||
bool PreconditionHolds() override;
|
||||
|
||||
protected:
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
opt::Instruction* conditional_branch_instruction_;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_REDUCTION_OPPORTUNITY_H_
|
@ -13,7 +13,8 @@
|
||||
# limitations under the License.
|
||||
|
||||
add_spvtools_unittest(TARGET reduce
|
||||
SRCS merge_blocks_test.cpp
|
||||
SRCS
|
||||
merge_blocks_test.cpp
|
||||
operand_to_constant_test.cpp
|
||||
operand_to_undef_test.cpp
|
||||
operand_to_dominating_id_test.cpp
|
||||
@ -27,6 +28,8 @@ add_spvtools_unittest(TARGET reduce
|
||||
remove_unreferenced_instruction_test.cpp
|
||||
structured_loop_to_selection_test.cpp
|
||||
validation_during_reduction_test.cpp
|
||||
conditional_branch_to_simple_conditional_branch_test.cpp
|
||||
simple_conditional_branch_to_branch_test.cpp
|
||||
LIBS SPIRV-Tools-reduce
|
||||
)
|
||||
|
||||
|
@ -0,0 +1,501 @@
|
||||
// Copyright (c) 2019 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/conditional_branch_to_simple_conditional_branch_opportunity_finder.h"
|
||||
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/reduce/reduction_opportunity.h"
|
||||
#include "source/reduce/reduction_pass.h"
|
||||
#include "test/reduce/reduce_test_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
namespace {
|
||||
|
||||
const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3;
|
||||
|
||||
TEST(ConditionalBranchToSimpleConditionalBranchTest, Diamond) {
|
||||
// A test with the following structure.
|
||||
//
|
||||
// selection header
|
||||
// OpBranchConditional
|
||||
// | |
|
||||
// b b
|
||||
// | |
|
||||
// selection merge
|
||||
//
|
||||
// There should be two opportunities for redirecting the OpBranchConditional
|
||||
// targets: redirecting the true to false, and vice-versa. E.g. false to true:
|
||||
//
|
||||
// selection header
|
||||
// OpBranchConditional
|
||||
// ||
|
||||
// b b
|
||||
// | |
|
||||
// selection merge
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpSelectionMerge %11 None
|
||||
OpBranchConditional %8 %12 %13
|
||||
%12 = OpLabel
|
||||
OpBranch %11
|
||||
%13 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
)";
|
||||
|
||||
auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(2, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ASSERT_TRUE(ops[1]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
// The other opportunity should now be disabled.
|
||||
ASSERT_FALSE(ops[1]->PreconditionHolds());
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
{
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpSelectionMerge %11 None
|
||||
OpBranchConditional %8 %12 %12
|
||||
%12 = OpLabel
|
||||
OpBranch %11
|
||||
%13 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
}
|
||||
|
||||
ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
|
||||
// Start again, and apply the other op.
|
||||
context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(2, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ASSERT_TRUE(ops[1]->PreconditionHolds());
|
||||
ops[1]->TryToApply();
|
||||
// The other opportunity should now be disabled.
|
||||
ASSERT_FALSE(ops[0]->PreconditionHolds());
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
{
|
||||
std::string after2 = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpSelectionMerge %11 None
|
||||
OpBranchConditional %8 %13 %13
|
||||
%12 = OpLabel
|
||||
OpBranch %11
|
||||
%13 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(kEnv, after2, context.get());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ConditionalBranchToSimpleConditionalBranchTest, AlreadySimplified) {
|
||||
// A test with the following structure.
|
||||
//
|
||||
// selection header
|
||||
// OpBranchConditional
|
||||
// ||
|
||||
// b b
|
||||
// | |
|
||||
// selection merge
|
||||
//
|
||||
// There should be no opportunities for redirecting the OpBranchConditional
|
||||
// as it is already simplified.
|
||||
//
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpSelectionMerge %11 None
|
||||
OpBranchConditional %8 %12 %12
|
||||
%12 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
)";
|
||||
|
||||
auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(ConditionalBranchToSimpleConditionalBranchTest, DontRemoveBackEdge) {
|
||||
// A test with the following structure. The loop has a continue construct that
|
||||
// ends with OpBranchConditional. The OpBranchConditional can be simplified,
|
||||
// but only to point to the loop header, otherwise we have removed the
|
||||
// back-edge. Thus, there should be one opportunity instead of two.
|
||||
//
|
||||
// loop header
|
||||
// |
|
||||
// loop continue target and back-edge block
|
||||
// OpBranchConditional
|
||||
// | |
|
||||
// loop merge (to loop header^)
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpBranchConditional %8 %11 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto context =
|
||||
BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpBranchConditional %8 %10 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
|
||||
ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(ConditionalBranchToSimpleConditionalBranchTest,
|
||||
DontRemoveBackEdgeCombinedHeaderContinue) {
|
||||
// A test with the following structure.
|
||||
//
|
||||
// loop header and continue target and back-edge block
|
||||
// OpBranchConditional
|
||||
// | |
|
||||
// loop merge (to loop header^)
|
||||
//
|
||||
// The OpBranchConditional-to-header edge must not be removed, so there should
|
||||
// only be one opportunity. It should change both targets to be to the loop
|
||||
// header.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %10 None
|
||||
OpBranchConditional %8 %11 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto context =
|
||||
BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %10 None
|
||||
OpBranchConditional %8 %10 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
|
||||
ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(ConditionalBranchToSimpleConditionalBranchTest, BackEdgeUnreachable) {
|
||||
// A test with the following structure. I.e. a loop with an unreachable
|
||||
// continue construct that ends with OpBranchConditional.
|
||||
//
|
||||
// loop header
|
||||
// |
|
||||
// | loop continue target (unreachable)
|
||||
// | |
|
||||
// | back-edge block (unreachable)
|
||||
// | OpBranchConditional
|
||||
// | | |
|
||||
// loop merge (to loop header^)
|
||||
//
|
||||
// The branch to the loop header must not be removed, even though the continue
|
||||
// construct is unreachable. So there should only be one opportunity to make
|
||||
// the true and false targets of the OpBranchConditional to point to the loop
|
||||
// header.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %11
|
||||
%12 = OpLabel
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
OpBranchConditional %8 %11 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto context =
|
||||
BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %11
|
||||
%12 = OpLabel
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
OpBranchConditional %8 %10 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
|
||||
ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
486
test/reduce/simple_conditional_branch_to_branch_test.cpp
Normal file
486
test/reduce/simple_conditional_branch_to_branch_test.cpp
Normal file
@ -0,0 +1,486 @@
|
||||
// Copyright (c) 2019 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/simple_conditional_branch_to_branch_opportunity_finder.h"
|
||||
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/reduce/reduction_opportunity.h"
|
||||
#include "source/reduce/reduction_pass.h"
|
||||
#include "test/reduce/reduce_test_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
namespace {
|
||||
|
||||
const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3;
|
||||
|
||||
TEST(SimpleConditionalBranchToBranchTest, Diamond) {
|
||||
// A test with the following structure.
|
||||
//
|
||||
// selection header
|
||||
// OpBranchConditional
|
||||
// ||
|
||||
// b b
|
||||
// | |
|
||||
// selection merge
|
||||
//
|
||||
// The conditional branch cannot be simplified because selection headers
|
||||
// cannot end with OpBranch.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpSelectionMerge %11 None
|
||||
OpBranchConditional %8 %12 %12
|
||||
%12 = OpLabel
|
||||
OpBranch %11
|
||||
%13 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
)";
|
||||
|
||||
auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(SimpleConditionalBranchToBranchTest, DiamondNoSelection) {
|
||||
// A test with the following structure.
|
||||
//
|
||||
// OpBranchConditional
|
||||
// ||
|
||||
// b b
|
||||
// | /
|
||||
// b
|
||||
//
|
||||
// The conditional branch can be simplified.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpBranchConditional %8 %12 %12
|
||||
%12 = OpLabel
|
||||
OpBranch %11
|
||||
%13 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpBranch %11
|
||||
%13 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
|
||||
ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(SimpleConditionalBranchToBranchTest, ConditionalBranchesButNotSimple) {
|
||||
// A test with the following structure.
|
||||
//
|
||||
// selection header
|
||||
// OpBranchConditional
|
||||
// | |
|
||||
// b OpBranchConditional
|
||||
// | | |
|
||||
// | b |
|
||||
// | | |
|
||||
// selection merge
|
||||
//
|
||||
// None of the conditional branches can be simplified; the first is not simple
|
||||
// AND part of a selection header; the second is just not simple (where
|
||||
// "simple" means it only has one target).
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpSelectionMerge %11 None
|
||||
OpBranchConditional %8 %12 %13
|
||||
%12 = OpLabel
|
||||
OpBranch %11
|
||||
%13 = OpLabel
|
||||
OpBranchConditional %8 %14 %11
|
||||
%14 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(SimpleConditionalBranchToBranchTest, SimplifyBackEdge) {
|
||||
// A test with the following structure. The loop has a continue construct that
|
||||
// ends with OpBranchConditional. The OpBranchConditional can be simplified.
|
||||
//
|
||||
// loop header
|
||||
// |
|
||||
// loop continue target and back-edge block
|
||||
// OpBranchConditional
|
||||
// ||
|
||||
// loop merge (to loop header^)
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpBranchConditional %8 %10 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto context =
|
||||
BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpBranch %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
|
||||
ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(SimpleConditionalBranchToBranchTest,
|
||||
DontRemoveBackEdgeCombinedHeaderContinue) {
|
||||
// A test with the following structure.
|
||||
//
|
||||
// loop header and continue target and back-edge block
|
||||
// OpBranchConditional
|
||||
// ||
|
||||
// loop merge (to loop header^)
|
||||
//
|
||||
// The conditional branch can be simplified.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %10 None
|
||||
OpBranchConditional %8 %10 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto context =
|
||||
BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %10 None
|
||||
OpBranch %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
|
||||
ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(SimpleConditionalBranchToBranchTest, BackEdgeUnreachable) {
|
||||
// A test with the following structure. I.e. a loop with an unreachable
|
||||
// continue construct that ends with OpBranchConditional.
|
||||
//
|
||||
// loop header
|
||||
// |
|
||||
// | loop continue target (unreachable)
|
||||
// | |
|
||||
// | back-edge block (unreachable)
|
||||
// | OpBranchConditional
|
||||
// | ||
|
||||
// loop merge (to loop header^)
|
||||
//
|
||||
// The conditional branch can be simplified.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %11
|
||||
%12 = OpLabel
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
OpBranchConditional %8 %10 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto context =
|
||||
BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %11
|
||||
%12 = OpLabel
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
OpBranch %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
|
||||
ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
Loading…
Reference in New Issue
Block a user