mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-28 18:51:05 +00:00
502e982956
This fixes a problem where TransformationInlineFunction could lead to distinct instructions having identical unique ids. It adds a validity check to detect this problem in general. Fixes #3911.
169 lines
6.5 KiB
C++
169 lines
6.5 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* ir_context,
|
|
const TransformationContext& transformation_context) const {
|
|
// First, we check that a constant with the same value as
|
|
// |message_.continue_condition_value| is present.
|
|
if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
|
|
message_.continue_condition_value(),
|
|
false)) {
|
|
// 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(ir_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()
|
|
: ir_context->GetStructuredCFGAnalysis()->ContainingLoop(
|
|
message_.from_block());
|
|
if (!loop_header) {
|
|
return false;
|
|
}
|
|
|
|
auto continue_block =
|
|
ir_context->cfg()->block(loop_header)->ContinueBlockId();
|
|
|
|
if (!fuzzerutil::BlockIsReachableInItsFunction(
|
|
ir_context, ir_context->cfg()->block(continue_block))) {
|
|
// If the loop's continue block is unreachable, we conservatively do not
|
|
// allow adding a dead continue, to avoid the compilations that arise due to
|
|
// the lack of sensible dominance information for unreachable blocks.
|
|
return false;
|
|
}
|
|
|
|
if (fuzzerutil::BlockIsInLoopContinueConstruct(
|
|
ir_context, message_.from_block(), loop_header)) {
|
|
// We cannot jump to the continue target from the continue construct.
|
|
return false;
|
|
}
|
|
|
|
if (ir_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;
|
|
}
|
|
|
|
// Check whether the data passed to extend OpPhi instructions is appropriate.
|
|
if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, bb_from,
|
|
ir_context->cfg()->block(continue_block),
|
|
message_.phi_id())) {
|
|
return false;
|
|
}
|
|
|
|
// Adding the dead break is only valid if SPIR-V rules related to dominance
|
|
// hold. Rather than checking these rules explicitly, we defer to the
|
|
// validator. We make a clone of the module, apply the transformation to the
|
|
// clone, and check whether the transformed clone is valid.
|
|
//
|
|
// In principle some of the above checks could be removed, with more reliance
|
|
// being placed on the validator. This should be revisited if we are sure
|
|
// the validator is complete with respect to checking structured control flow
|
|
// rules.
|
|
auto cloned_context = fuzzerutil::CloneIRContext(ir_context);
|
|
ApplyImpl(cloned_context.get(), transformation_context);
|
|
return fuzzerutil::IsValid(cloned_context.get(),
|
|
transformation_context.GetValidatorOptions(),
|
|
fuzzerutil::kSilentMessageConsumer);
|
|
}
|
|
|
|
void TransformationAddDeadContinue::Apply(
|
|
opt::IRContext* ir_context,
|
|
TransformationContext* transformation_context) const {
|
|
ApplyImpl(ir_context, *transformation_context);
|
|
// Invalidate all analyses
|
|
ir_context->InvalidateAnalysesExceptFor(
|
|
opt::IRContext::Analysis::kAnalysisNone);
|
|
}
|
|
|
|
protobufs::Transformation TransformationAddDeadContinue::ToMessage() const {
|
|
protobufs::Transformation result;
|
|
*result.mutable_add_dead_continue() = message_;
|
|
return result;
|
|
}
|
|
|
|
void TransformationAddDeadContinue::ApplyImpl(
|
|
spvtools::opt::IRContext* ir_context,
|
|
const TransformationContext& transformation_context) const {
|
|
auto bb_from = ir_context->cfg()->block(message_.from_block());
|
|
auto continue_block =
|
|
bb_from->IsLoopHeader()
|
|
? bb_from->ContinueBlockId()
|
|
: ir_context->GetStructuredCFGAnalysis()->LoopContinueBlock(
|
|
message_.from_block());
|
|
assert(continue_block && "message_.from_block must be in a loop.");
|
|
fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
|
|
ir_context, bb_from, ir_context->cfg()->block(continue_block),
|
|
fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
|
|
message_.continue_condition_value(),
|
|
false),
|
|
message_.phi_id());
|
|
}
|
|
|
|
std::unordered_set<uint32_t> TransformationAddDeadContinue::GetFreshIds()
|
|
const {
|
|
return std::unordered_set<uint32_t>();
|
|
}
|
|
|
|
} // namespace fuzz
|
|
} // namespace spvtools
|