New spirv-reduce reduction pass: operand to dominating id. (#2099)
* Added a reduction pass to replace ids with ids of the same type that dominate them. * Introduce helper method for querying whether an operand type is an input id.
This commit is contained in:
Родитель
e799bfb923
Коммит
3b13040cf9
|
@ -411,6 +411,21 @@ bool spvIsIdType(spv_operand_type_t type) {
|
|||
}
|
||||
}
|
||||
|
||||
bool spvIsInIdType(spv_operand_type_t type) {
|
||||
if (!spvIsIdType(type)) {
|
||||
// If it is not an ID it cannot be an input ID.
|
||||
return false;
|
||||
}
|
||||
switch (type) {
|
||||
// Blacklist non-input IDs.
|
||||
case SPV_OPERAND_TYPE_TYPE_ID:
|
||||
case SPV_OPERAND_TYPE_RESULT_ID:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::function<bool(unsigned)> spvOperandCanBeForwardDeclaredFunction(
|
||||
SpvOp opcode) {
|
||||
std::function<bool(unsigned index)> out;
|
||||
|
|
|
@ -131,6 +131,9 @@ spv_operand_pattern_t spvAlternatePatternFollowingImmediate(
|
|||
// Is the operand an ID?
|
||||
bool spvIsIdType(spv_operand_type_t type);
|
||||
|
||||
// Is the operand an input ID?
|
||||
bool spvIsInIdType(spv_operand_type_t type);
|
||||
|
||||
// Takes the opcode of an instruction and returns
|
||||
// a function object that will return true if the index
|
||||
// of the operand can be forward declared. This function will
|
||||
|
|
|
@ -618,15 +618,8 @@ inline void Instruction::ForEachId(
|
|||
inline bool Instruction::WhileEachInId(
|
||||
const std::function<bool(uint32_t*)>& f) {
|
||||
for (auto& opnd : operands_) {
|
||||
switch (opnd.type) {
|
||||
case SPV_OPERAND_TYPE_RESULT_ID:
|
||||
case SPV_OPERAND_TYPE_TYPE_ID:
|
||||
break;
|
||||
default:
|
||||
if (spvIsIdType(opnd.type)) {
|
||||
if (!f(&opnd.words[0])) return false;
|
||||
}
|
||||
break;
|
||||
if (spvIsInIdType(opnd.type)) {
|
||||
if (!f(&opnd.words[0])) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -635,15 +628,8 @@ inline bool Instruction::WhileEachInId(
|
|||
inline bool Instruction::WhileEachInId(
|
||||
const std::function<bool(const uint32_t*)>& f) const {
|
||||
for (const auto& opnd : operands_) {
|
||||
switch (opnd.type) {
|
||||
case SPV_OPERAND_TYPE_RESULT_ID:
|
||||
case SPV_OPERAND_TYPE_TYPE_ID:
|
||||
break;
|
||||
default:
|
||||
if (spvIsIdType(opnd.type)) {
|
||||
if (!f(&opnd.words[0])) return false;
|
||||
}
|
||||
break;
|
||||
if (spvIsInIdType(opnd.type)) {
|
||||
if (!f(&opnd.words[0])) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
set(SPIRV_TOOLS_REDUCE_SOURCES
|
||||
change_operand_reduction_opportunity.h
|
||||
operand_to_const_reduction_pass.h
|
||||
operand_to_dominating_id_reduction_pass.h
|
||||
reducer.h
|
||||
reduction_opportunity.h
|
||||
reduction_pass.h
|
||||
|
@ -22,6 +23,7 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
|
|||
|
||||
change_operand_reduction_opportunity.cpp
|
||||
operand_to_const_reduction_pass.cpp
|
||||
operand_to_dominating_id_reduction_pass.cpp
|
||||
reducer.cpp
|
||||
reduction_opportunity.cpp
|
||||
reduction_pass.cpp
|
||||
|
|
|
@ -34,7 +34,7 @@ class ChangeOperandReductionOpportunity : public ReductionOpportunity {
|
|||
uint32_t new_id)
|
||||
: inst_(inst),
|
||||
operand_index_(operand_index),
|
||||
original_id_(inst_->GetOperand(operand_index_).words[0]),
|
||||
original_id_(inst->GetOperand(operand_index).words[0]),
|
||||
original_type_(inst->GetOperand(operand_index).type),
|
||||
new_id_(new_id) {}
|
||||
|
||||
|
|
|
@ -44,11 +44,7 @@ OperandToConstReductionPass::GetAvailableOpportunities(
|
|||
// of a ChangeOperandReductionOpportunity
|
||||
for (uint32_t index = 0; index < inst.NumOperands(); index++) {
|
||||
const auto& operand = inst.GetOperand(index);
|
||||
if (spvIsIdType(operand.type)) {
|
||||
if (operand.type == SPV_OPERAND_TYPE_RESULT_ID ||
|
||||
operand.type == SPV_OPERAND_TYPE_TYPE_ID) {
|
||||
continue;
|
||||
}
|
||||
if (spvIsInIdType(operand.type)) {
|
||||
const auto id = operand.words[0];
|
||||
auto def = context->get_def_use_mgr()->GetDef(id);
|
||||
if (spvOpcodeIsConstant(def->opcode())) {
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
// Copyright (c) 2018 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "operand_to_dominating_id_reduction_pass.h"
|
||||
#include "change_operand_reduction_opportunity.h"
|
||||
#include "source/opt/instruction.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
using namespace opt;
|
||||
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>>
|
||||
OperandToDominatingIdReductionPass::GetAvailableOpportunities(
|
||||
opt::IRContext* context) const {
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> result;
|
||||
|
||||
// Go through every instruction in every block, considering it as a potential
|
||||
// dominator of other instructions. We choose this order for two reasons:
|
||||
//
|
||||
// (1) it is profitable for multiple opportunities to replace the same id x by
|
||||
// different dominating ids y and z to be discontiguous, as they are
|
||||
// incompatible.
|
||||
//
|
||||
// (2) We want to prioritise opportunities to replace an id with a more
|
||||
// distant dominator. Intuitively, in a human-readable programming language
|
||||
// if we have a complex expression e with many sub-expressions, we would like
|
||||
// to prioritise replacing e with its smallest sub-expressions; generalising
|
||||
// this idea to dominating ids this roughly corresponds to more distant
|
||||
// dominators.
|
||||
for (auto& function : *context->module()) {
|
||||
for (auto dominating_block = function.begin();
|
||||
dominating_block != function.end(); ++dominating_block) {
|
||||
for (auto& dominating_inst : *dominating_block) {
|
||||
if (dominating_inst.HasResultId() && dominating_inst.type_id()) {
|
||||
// Consider replacing any operand with matching type in a dominated
|
||||
// instruction with the id generated by this instruction.
|
||||
GetOpportunitiesForDominatingInst(
|
||||
&result, &dominating_inst, dominating_block, &function, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void OperandToDominatingIdReductionPass::GetOpportunitiesForDominatingInst(
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>>* opportunities,
|
||||
opt::Instruction* candidate_dominator,
|
||||
opt::Function::iterator candidate_dominator_block, opt::Function* function,
|
||||
opt::IRContext* context) const {
|
||||
assert(candidate_dominator->HasResultId());
|
||||
assert(candidate_dominator->type_id());
|
||||
auto dominator_analysis = context->GetDominatorAnalysis(function);
|
||||
// SPIR-V requires a block to precede all blocks it dominates, so it suffices
|
||||
// to search from the candidate dominator block onwards.
|
||||
for (auto block = candidate_dominator_block; block != function->end();
|
||||
++block) {
|
||||
if (!dominator_analysis->Dominates(&*candidate_dominator_block, &*block)) {
|
||||
// If the candidate dominator block doesn't dominate this block then there
|
||||
// cannot be any of the desired reduction opportunities in this block.
|
||||
continue;
|
||||
}
|
||||
for (auto& inst : *block) {
|
||||
// We iterate through the operands using an explicit index (rather
|
||||
// than using a lambda) so that we use said index in the construction
|
||||
// of a ChangeOperandReductionOpportunity
|
||||
for (uint32_t index = 0; index < inst.NumOperands(); index++) {
|
||||
const auto& operand = inst.GetOperand(index);
|
||||
if (spvIsInIdType(operand.type)) {
|
||||
const auto id = operand.words[0];
|
||||
auto def = context->get_def_use_mgr()->GetDef(id);
|
||||
assert(def);
|
||||
if (!context->get_instr_block(def)) {
|
||||
// The definition does not come from a block; e.g. it might be a
|
||||
// constant. It is thus not relevant to this pass.
|
||||
continue;
|
||||
}
|
||||
// Sanity check that we don't get here if the argument is a constant.
|
||||
assert(!context->get_constant_mgr()->GetConstantFromInst(def));
|
||||
if (def->type_id() != candidate_dominator->type_id()) {
|
||||
// The types need to match.
|
||||
continue;
|
||||
}
|
||||
if (candidate_dominator != def &&
|
||||
dominator_analysis->Dominates(candidate_dominator, def)) {
|
||||
// A hit: the candidate dominator strictly dominates the definition.
|
||||
opportunities->push_back(
|
||||
MakeUnique<ChangeOperandReductionOpportunity>(
|
||||
&inst, index, candidate_dominator->result_id()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string OperandToDominatingIdReductionPass::GetName() const {
|
||||
return "OperandToDominatingIdReductionPass";
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright (c) 2018 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_REDUCE_OPERAND_TO_DOMINATING_ID_REDUCTION_PASS_H_
|
||||
#define SOURCE_REDUCE_OPERAND_TO_DOMINATING_ID_REDUCTION_PASS_H_
|
||||
|
||||
#include "reduction_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// A reduction pass that aims to bring to SPIR-V (and generalize) the idea from
|
||||
// human-readable languages of e.g. replacing an expression with one of its
|
||||
// arguments, (x + y) -> x, or with a reference to an identifier that was
|
||||
// assigned to higher up in the program. The generalization of this is to
|
||||
// replace an id with a different id of the same type defined in some
|
||||
// dominating instruction.
|
||||
//
|
||||
// If id x is defined and then used several times, changing each use of x to
|
||||
// some dominating definition may eventually allow the statement defining x
|
||||
// to be eliminated by another pass.
|
||||
class OperandToDominatingIdReductionPass : public ReductionPass {
|
||||
public:
|
||||
// Creates the reduction pass in the context of the given target environment
|
||||
// |target_env|
|
||||
explicit OperandToDominatingIdReductionPass(const spv_target_env target_env)
|
||||
: ReductionPass(target_env) {}
|
||||
|
||||
~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;
|
||||
|
||||
private:
|
||||
void GetOpportunitiesForDominatingInst(
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>>* opportunities,
|
||||
opt::Instruction* dominating_instruction,
|
||||
opt::Function::iterator candidate_dominator_block,
|
||||
opt::Function* function, opt::IRContext* context) const;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_OPERAND_TO_DOMINATING_ID_REDUCTION_PASS_H_
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
add_spvtools_unittest(TARGET reduce
|
||||
SRCS operand_to_constant_reduction_pass_test.cpp
|
||||
operand_to_dominating_id_reduction_pass_test.cpp
|
||||
reduce_test_util.cpp
|
||||
reduce_test_util.h
|
||||
reducer_test.cpp
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
// Copyright (c) 2018 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/reduce/operand_to_dominating_id_reduction_pass.h"
|
||||
#include "reduce_test_util.h"
|
||||
#include "source/opt/build_module.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
namespace {
|
||||
|
||||
TEST(OperandToDominatingIdReductionPassTest, BasicCheck) {
|
||||
std::string original = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 2
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
%10 = OpVariable %7 Function
|
||||
%14 = OpVariable %7 Function
|
||||
OpStore %8 %9
|
||||
%11 = OpLoad %6 %8
|
||||
%12 = OpLoad %6 %8
|
||||
%13 = OpIAdd %6 %11 %12
|
||||
OpStore %10 %13
|
||||
%15 = OpLoad %6 %10
|
||||
OpStore %14 %15
|
||||
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<OperandToDominatingIdReductionPass>(env);
|
||||
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(10, ops.size());
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
|
||||
std::string after_op_0 = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 2
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
%10 = OpVariable %7 Function
|
||||
%14 = OpVariable %7 Function
|
||||
OpStore %8 %9
|
||||
%11 = OpLoad %6 %8
|
||||
%12 = OpLoad %6 %8
|
||||
%13 = OpIAdd %6 %11 %12
|
||||
OpStore %8 %13 ; %10 -> %8
|
||||
%15 = OpLoad %6 %10
|
||||
OpStore %14 %15
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CheckEqual(env, after_op_0, context.get());
|
||||
|
||||
ASSERT_TRUE(ops[1]->PreconditionHolds());
|
||||
ops[1]->TryToApply();
|
||||
|
||||
std::string after_op_1 = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 2
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
%10 = OpVariable %7 Function
|
||||
%14 = OpVariable %7 Function
|
||||
OpStore %8 %9
|
||||
%11 = OpLoad %6 %8
|
||||
%12 = OpLoad %6 %8
|
||||
%13 = OpIAdd %6 %11 %12
|
||||
OpStore %8 %13 ; %10 -> %8
|
||||
%15 = OpLoad %6 %8 ; %10 -> %8
|
||||
OpStore %14 %15
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CheckEqual(env, after_op_1, context.get());
|
||||
|
||||
ASSERT_TRUE(ops[2]->PreconditionHolds());
|
||||
ops[2]->TryToApply();
|
||||
|
||||
std::string after_op_2 = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 2
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
%10 = OpVariable %7 Function
|
||||
%14 = OpVariable %7 Function
|
||||
OpStore %8 %9
|
||||
%11 = OpLoad %6 %8
|
||||
%12 = OpLoad %6 %8
|
||||
%13 = OpIAdd %6 %11 %12
|
||||
OpStore %8 %13 ; %10 -> %8
|
||||
%15 = OpLoad %6 %8 ; %10 -> %8
|
||||
OpStore %8 %15 ; %14 -> %8
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CheckEqual(env, after_op_2, context.get());
|
||||
|
||||
// The precondition has been disabled by an earlier opportunity's application.
|
||||
ASSERT_FALSE(ops[3]->PreconditionHolds());
|
||||
|
||||
ASSERT_TRUE(ops[4]->PreconditionHolds());
|
||||
ops[4]->TryToApply();
|
||||
|
||||
std::string after_op_4 = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeInt 32 1
|
||||
%7 = OpTypePointer Function %6
|
||||
%9 = OpConstant %6 2
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%8 = OpVariable %7 Function
|
||||
%10 = OpVariable %7 Function
|
||||
%14 = OpVariable %7 Function
|
||||
OpStore %8 %9
|
||||
%11 = OpLoad %6 %8
|
||||
%12 = OpLoad %6 %8
|
||||
%13 = OpIAdd %6 %11 %11 ; %12 -> %11
|
||||
OpStore %8 %13 ; %10 -> %8
|
||||
%15 = OpLoad %6 %8 ; %10 -> %8
|
||||
OpStore %8 %15 ; %14 -> %8
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(env, after_op_4, context.get());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
|
@ -21,6 +21,7 @@
|
|||
#include "source/opt/ir_context.h"
|
||||
#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/reducer.h"
|
||||
#include "source/reduce/remove_unreferenced_instruction_reduction_pass.h"
|
||||
#include "source/spirv_reducer_options.h"
|
||||
|
@ -204,6 +205,8 @@ int main(int argc, const char** argv) {
|
|||
|
||||
reducer.AddReductionPass(
|
||||
spvtools::MakeUnique<OperandToConstReductionPass>(target_env));
|
||||
reducer.AddReductionPass(
|
||||
spvtools::MakeUnique<OperandToDominatingIdReductionPass>(target_env));
|
||||
reducer.AddReductionPass(
|
||||
spvtools::MakeUnique<RemoveUnreferencedInstructionReductionPass>(
|
||||
target_env));
|
||||
|
|
Загрузка…
Ссылка в новой задаче