mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-27 05:40:06 +00:00
1a89ac8b28
Similar to the existing 'add dead breaks' pass, this adds a pass to add dead continues to blocks in loops where such a transformation is viable. Various functionality common to this new pass and 'add dead breaks' has been factored into 'fuzzer_util', and some small improvements to 'add dead breaks' that were identified while reviewing that code again have been applied. Fixes #2719.
134 lines
5.1 KiB
C++
134 lines
5.1 KiB
C++
// 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/fuzz/transformation_add_dead_continue.h"
|
|
|
|
#include "source/fuzz/fuzzer_util.h"
|
|
|
|
namespace spvtools {
|
|
namespace fuzz {
|
|
|
|
TransformationAddDeadContinue::TransformationAddDeadContinue(
|
|
const spvtools::fuzz::protobufs::TransformationAddDeadContinue& message)
|
|
: message_(message) {}
|
|
|
|
TransformationAddDeadContinue::TransformationAddDeadContinue(
|
|
uint32_t from_block, bool continue_condition_value,
|
|
std::vector<uint32_t> phi_id) {
|
|
message_.set_from_block(from_block);
|
|
message_.set_continue_condition_value(continue_condition_value);
|
|
for (auto id : phi_id) {
|
|
message_.add_phi_id(id);
|
|
}
|
|
}
|
|
|
|
bool TransformationAddDeadContinue::IsApplicable(
|
|
opt::IRContext* context, const FactManager& /*unused*/) const {
|
|
// First, we check that a constant with the same value as
|
|
// |message_.continue_condition_value| is present.
|
|
opt::analysis::Bool bool_type;
|
|
auto registered_bool_type =
|
|
context->get_type_mgr()->GetRegisteredType(&bool_type);
|
|
if (!registered_bool_type) {
|
|
return false;
|
|
}
|
|
opt::analysis::BoolConstant bool_constant(
|
|
registered_bool_type->AsBool(), message_.continue_condition_value());
|
|
if (!context->get_constant_mgr()->FindConstant(&bool_constant)) {
|
|
// The required constant is not present, so the transformation cannot be
|
|
// applied.
|
|
return false;
|
|
}
|
|
|
|
// Check that |message_.from_block| really is a block id.
|
|
opt::BasicBlock* bb_from =
|
|
fuzzerutil::MaybeFindBlock(context, message_.from_block());
|
|
if (bb_from == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
// Check that |message_.from_block| ends with an unconditional branch.
|
|
if (bb_from->terminator()->opcode() != SpvOpBranch) {
|
|
// The block associated with the id does not end with an unconditional
|
|
// branch.
|
|
return false;
|
|
}
|
|
|
|
assert(bb_from != nullptr &&
|
|
"We should have found a block if this line of code is reached.");
|
|
assert(
|
|
bb_from->id() == message_.from_block() &&
|
|
"The id of the block we found should match the source id for the break.");
|
|
|
|
// Get the header for the innermost loop containing |message_.from_block|.
|
|
// Because the structured CFG analysis does not regard a loop header as part
|
|
// of the loop it heads, we check first whether bb_from is a loop header
|
|
// before using the structured CFG analysis.
|
|
auto loop_header = bb_from->IsLoopHeader()
|
|
? message_.from_block()
|
|
: context->GetStructuredCFGAnalysis()->ContainingLoop(
|
|
message_.from_block());
|
|
if (!loop_header) {
|
|
return false;
|
|
}
|
|
|
|
if (fuzzerutil::BlockIsInLoopContinueConstruct(context, message_.from_block(),
|
|
loop_header)) {
|
|
// We cannot jump to the continue target from the continue construct.
|
|
return false;
|
|
}
|
|
|
|
auto continue_block = context->cfg()->block(loop_header)->ContinueBlockId();
|
|
|
|
if (context->GetStructuredCFGAnalysis()->IsMergeBlock(continue_block)) {
|
|
// A branch straight to the continue target that is also a merge block might
|
|
// break the property that a construct header must dominate its merge block
|
|
// (if the merge block is reachable).
|
|
return false;
|
|
}
|
|
|
|
// The transformation is good if and only if the given phi ids are sufficient
|
|
// to extend relevant OpPhi instructions in the continue block.
|
|
return fuzzerutil::PhiIdsOkForNewEdge(context, bb_from,
|
|
context->cfg()->block(continue_block),
|
|
message_.phi_id());
|
|
}
|
|
|
|
void TransformationAddDeadContinue::Apply(opt::IRContext* context,
|
|
FactManager* /*unused*/) const {
|
|
auto bb_from = context->cfg()->block(message_.from_block());
|
|
auto continue_block =
|
|
bb_from->IsLoopHeader()
|
|
? bb_from->ContinueBlockId()
|
|
: context->GetStructuredCFGAnalysis()->LoopContinueBlock(
|
|
message_.from_block());
|
|
assert(continue_block &&
|
|
"Precondition for this transformation requires that "
|
|
"message_.from_block must be in a loop.");
|
|
fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
|
|
context, bb_from, context->cfg()->block(continue_block),
|
|
message_.continue_condition_value(), message_.phi_id());
|
|
// Invalidate all analyses
|
|
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
|
|
}
|
|
|
|
protobufs::Transformation TransformationAddDeadContinue::ToMessage() const {
|
|
protobufs::Transformation result;
|
|
*result.mutable_add_dead_continue() = message_;
|
|
return result;
|
|
}
|
|
|
|
} // namespace fuzz
|
|
} // namespace spvtools
|