Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reduce: remove unreferenced blocks pass #2398

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions source/reduce/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
reduction_opportunity_finder.h
reduction_pass.h
reduction_util.h
remove_block_reduction_opportunity.h
remove_block_reduction_opportunity_finder.h
remove_instruction_reduction_opportunity.h
remove_function_reduction_opportunity.h
remove_function_reduction_opportunity_finder.h
Expand All @@ -43,6 +45,8 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
reduction_opportunity.cpp
reduction_pass.cpp
reduction_util.cpp
remove_block_reduction_opportunity.cpp
remove_block_reduction_opportunity_finder.cpp
remove_function_reduction_opportunity.cpp
remove_function_reduction_opportunity_finder.cpp
remove_instruction_reduction_opportunity.cpp
Expand Down
56 changes: 56 additions & 0 deletions source/reduce/remove_block_reduction_opportunity.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// 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 "remove_block_reduction_opportunity.h"

#include "source/opt/ir_context.h"

namespace spvtools {
namespace reduce {

using namespace opt;

RemoveBlockReductionOpportunity::RemoveBlockReductionOpportunity(
Function* function, BasicBlock* block)
: function_(function), block_(block) {
// precondition:
assert(block_->begin() != block_->end() &&
block_->begin()->context()->get_def_use_mgr()->NumUsers(
block_->id()) == 0 &&
"RemoveBlockReductionOpportunity block must have 0 references");
}

bool RemoveBlockReductionOpportunity::PreconditionHolds() {
// Removing other blocks cannot disable this opportunity.
return true;
}

void RemoveBlockReductionOpportunity::Apply() {
// We need an iterator pointing to the block, hence the loop.
for (auto bi = function_->begin(); bi != function_->end(); ++bi) {
afd marked this conversation as resolved.
Show resolved Hide resolved
if (bi->id() == block_->id()) {
bi->KillAllInsts(true);
bi.Erase();
Copy link
Contributor Author

@paulthomson paulthomson Feb 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This "RemoveBlock" code is seen elsewhere. Should it be refactored into some common function? If yes, then where? See MemPass::RemoveBlock; it seems to kill the label instruction last, but I don't know why, especially given that KillAllInsts exists, which kills the label first.

// Block removal changes the function, but we don't use analyses, so no
// need to invalidate them.
return;
}
}

assert(false &&
"Unreachable: we should have found a block with the desired id.");
}

} // namespace reduce
} // namespace spvtools
46 changes: 46 additions & 0 deletions source/reduce/remove_block_reduction_opportunity.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_H_
#define SOURCE_REDUCE_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_H_

#include "reduction_opportunity.h"
#include "source/opt/basic_block.h"
#include "source/opt/function.h"

namespace spvtools {
namespace reduce {

// An opportunity to remove an unreferenced block.
// See RemoveBlockReductionOpportunityFinder.
class RemoveBlockReductionOpportunity : public ReductionOpportunity {
public:
// Creates the opportunity to remove |block| in |function| in |context|.
RemoveBlockReductionOpportunity(opt::Function* function,
opt::BasicBlock* block);

bool PreconditionHolds() override;

protected:
void Apply() override;

private:
opt::Function* function_;
opt::BasicBlock* block_;
};

} // namespace reduce
} // namespace spvtools

#endif // SOURCE_REDUCE_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_H_
96 changes: 96 additions & 0 deletions source/reduce/remove_block_reduction_opportunity_finder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// 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/remove_block_reduction_opportunity_finder.h"
#include "source/reduce/remove_block_reduction_opportunity.h"

namespace spvtools {
namespace reduce {

using namespace opt;

std::string RemoveBlockReductionOpportunityFinder::GetName() const {
return "RemoveBlockReductionOpportunityFinder";
}

std::vector<std::unique_ptr<ReductionOpportunity>>
RemoveBlockReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;

// Consider every block in every function.
for (auto& function : *context->module()) {
for (auto bi = function.begin(); bi != function.end(); ++bi) {
if (IsBlockValidOpportunity(context, function, bi)) {
result.push_back(spvtools::MakeUnique<RemoveBlockReductionOpportunity>(
&function, &*bi));
}
}
}
return result;
}

bool RemoveBlockReductionOpportunityFinder::IsBlockValidOpportunity(
opt::IRContext* context, opt::Function& function,
opt::Function::iterator& bi) {
assert(bi != function.end() && "Block iterator was out of bounds");

// Don't remove first block; we don't want to end up with no blocks.
if (bi == function.begin()) {
return false;
}

// Don't remove blocks with references.
if (context->get_def_use_mgr()->NumUsers(bi->id()) > 0) {
return false;
}

// Don't remove blocks whose instructions have outside references.
if (!BlockInstructionsHaveNoOutsideReferences(context, bi)) {
return false;
}

return true;
}

bool RemoveBlockReductionOpportunityFinder::
BlockInstructionsHaveNoOutsideReferences(opt::IRContext* context,
const Function::iterator& bi) {
// Get all instructions in block.
std::unordered_set<uint32_t> instructions_in_block;
for (const Instruction& instruction : *bi) {
instructions_in_block.insert(instruction.unique_id());
}

// For each instruction...
for (const Instruction& instruction : *bi) {
// For each use of the instruction...
bool no_uses_outside_block = context->get_def_use_mgr()->WhileEachUser(
&instruction, [&instructions_in_block](Instruction* user) -> bool {
// If the use is in this block, continue (return true). Otherwise, we
// found an outside use; return false (and stop).
return instructions_in_block.find(user->unique_id()) !=
instructions_in_block.end();
});

if (!no_uses_outside_block) {
return false;
}
}

return true;
}

} // namespace reduce
} // namespace spvtools
55 changes: 55 additions & 0 deletions source/reduce/remove_block_reduction_opportunity_finder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H_
#define SOURCE_REDUCE_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H_

#include "source/opt/function.h"
#include "source/reduce/reduction_opportunity_finder.h"

namespace spvtools {
namespace reduce {

// A finder of opportunities to remove a block. The optimizer can remove dead
// code. However, the reducer needs to be able to remove at a fine-grained
// level.
class RemoveBlockReductionOpportunityFinder
: public ReductionOpportunityFinder {
public:
RemoveBlockReductionOpportunityFinder() = default;

~RemoveBlockReductionOpportunityFinder() override = default;

std::string GetName() const final;

std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;

private:
// Returns true if the block |bi| in function |function| is a valid
// opportunity according to various restrictions.
static bool IsBlockValidOpportunity(opt::IRContext* context,
opt::Function& function,
opt::Function::iterator& bi);

// Returns true if the instructions (definitions) in block |bi| have no
// references, except for references from inside the block itself.
static bool BlockInstructionsHaveNoOutsideReferences(
opt::IRContext* context, const opt::Function::iterator& bi);
};

} // namespace reduce
} // namespace spvtools

#endif // SOURCE_REDUCE_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H_
1 change: 1 addition & 0 deletions test/reduce/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ add_spvtools_unittest(TARGET reduce
reduce_test_util.cpp
reduce_test_util.h
reducer_test.cpp
remove_block_test.cpp
remove_function_test.cpp
remove_opname_instruction_test.cpp
remove_unreferenced_instruction_test.cpp
Expand Down
Loading