Add IsReachable function to IRContext (#4323)

There was a lot of code in the codebase that would get the dominator
analysis for a function and then use it to check whether a block is
reachable. In the fuzzer, a utility method had been introduced to make
this more concise, but it was not being used consistently.

This change moves the utility method to IRContext, so that it can be
used throughout the codebase, and refactors all existing checks for
block reachability to use the utility method.
This commit is contained in:
Alastair Donaldson 2021-06-28 20:00:14 +01:00 коммит произвёл GitHub
Родитель 237173a070
Коммит 4fcdc58946
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
17 изменённых файлов: 46 добавлений и 60 удалений

Просмотреть файл

@ -44,7 +44,7 @@ AvailableInstructions::AvailableInstructions(
// Consider every reachable block in the function.
auto dominator_analysis = ir_context->GetDominatorAnalysis(&function);
for (auto& block : function) {
if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, &block)) {
if (!ir_context->IsReachable(block)) {
// The block is not reachable.
continue;
}

Просмотреть файл

@ -111,10 +111,8 @@ void FuzzerPass::ForEachInstructionWithInstructionDescriptor(
// module.
std::vector<opt::BasicBlock*> reachable_blocks;
const auto* dominator_analysis =
GetIRContext()->GetDominatorAnalysis(function);
for (auto& block : *function) {
if (dominator_analysis->IsReachable(&block)) {
if (GetIRContext()->IsReachable(block)) {
reachable_blocks.push_back(&block);
}
}

Просмотреть файл

@ -31,8 +31,7 @@ void FuzzerPassPropagateInstructionsDown::Apply() {
for (const auto& function : *GetIRContext()->module()) {
std::vector<const opt::BasicBlock*> reachable_blocks;
for (const auto& block : function) {
if (GetIRContext()->GetDominatorAnalysis(&function)->IsReachable(
&block)) {
if (GetIRContext()->IsReachable(block)) {
reachable_blocks.push_back(&block);
}
}

Просмотреть файл

@ -47,7 +47,7 @@ void FuzzerPassPushIdsThroughVariables::Apply() {
// The block containing the instruction we are going to insert before
// must be reachable.
if (!fuzzerutil::BlockIsReachableInItsFunction(GetIRContext(), block)) {
if (!GetIRContext()->IsReachable(*block)) {
return;
}

Просмотреть файл

@ -252,11 +252,11 @@ bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id,
return false;
}
// |block_id| must be reachable and be dominated by |loop_header|.
// |block| must be reachable and be dominated by |loop_header|.
opt::DominatorAnalysis* dominator_analysis =
context->GetDominatorAnalysis(loop_header->GetParent());
return dominator_analysis->IsReachable(block_id) &&
dominator_analysis->Dominates(loop_header_id, block_id);
return context->IsReachable(*block) &&
dominator_analysis->Dominates(loop_header, block);
}
bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
@ -284,13 +284,6 @@ opt::BasicBlock::iterator GetIteratorForInstruction(
return block->end();
}
bool BlockIsReachableInItsFunction(opt::IRContext* context,
opt::BasicBlock* bb) {
auto enclosing_function = bb->GetParent();
return context->GetDominatorAnalysis(enclosing_function)
->Dominates(enclosing_function->entry().get(), bb);
}
bool CanInsertOpcodeBeforeInstruction(
SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block) {
if (instruction_in_block->PreviousNode() &&
@ -660,13 +653,12 @@ bool IdIsAvailableAtUse(opt::IRContext* context,
// It is not OK for a definition to use itself.
return false;
}
auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function);
if (!dominator_analysis->IsReachable(
context->get_instr_block(use_instruction)) ||
!dominator_analysis->IsReachable(context->get_instr_block(id))) {
if (!context->IsReachable(*context->get_instr_block(use_instruction)) ||
!context->IsReachable(*context->get_instr_block(id))) {
// Skip unreachable blocks.
return false;
}
auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function);
if (use_instruction->opcode() == SpvOpPhi) {
// In the case where the use is an operand to OpPhi, it is actually the
// *parent* block associated with the operand that must be dominated by
@ -704,8 +696,8 @@ bool IdIsAvailableBeforeInstruction(opt::IRContext* context,
}
const auto* dominator_analysis =
context->GetDominatorAnalysis(function_enclosing_instruction);
if (dominator_analysis->IsReachable(context->get_instr_block(instruction)) &&
dominator_analysis->IsReachable(context->get_instr_block(id)) &&
if (context->IsReachable(*context->get_instr_block(instruction)) &&
context->IsReachable(*context->get_instr_block(id)) &&
dominator_analysis->Dominates(id_definition, instruction)) {
// The id's definition dominates the instruction, and both the definition
// and the instruction are in reachable blocks, thus the id is available at
@ -715,8 +707,7 @@ bool IdIsAvailableBeforeInstruction(opt::IRContext* context,
if (id_definition->opcode() == SpvOpVariable &&
function_enclosing_instruction ==
context->get_instr_block(id)->GetParent()) {
assert(!dominator_analysis->IsReachable(
context->get_instr_block(instruction)) &&
assert(!context->IsReachable(*context->get_instr_block(instruction)) &&
"If the instruction were in a reachable block we should already "
"have returned true.");
// The id is a variable and it is in the same function as |instruction|.
@ -1883,7 +1874,7 @@ bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context,
// all its dependencies satisfy domination rules (i.e. all id operands
// dominate that instruction).
for (const auto& block : *mutated_block->GetParent()) {
if (!dominator_analysis.IsReachable(&block)) {
if (!ir_context->IsReachable(block)) {
// If some block is not reachable then we don't need to worry about the
// preservation of domination rules for its instructions.
continue;

Просмотреть файл

@ -108,11 +108,6 @@ bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
opt::BasicBlock::iterator GetIteratorForInstruction(
opt::BasicBlock* block, const opt::Instruction* inst);
// Returns true if and only if there is a path to |bb| from the entry block of
// the function that contains |bb|.
bool BlockIsReachableInItsFunction(opt::IRContext* context,
opt::BasicBlock* bb);
// Determines whether it is OK to insert an instruction with opcode |opcode|
// before |instruction_in_block|.
bool CanInsertOpcodeBeforeInstruction(

Просмотреть файл

@ -79,9 +79,7 @@ bool TransformationAddDeadBlock::IsApplicable(
}
// |existing_block| must be reachable.
opt::DominatorAnalysis* dominator_analysis =
ir_context->GetDominatorAnalysis(existing_block->GetParent());
if (!dominator_analysis->IsReachable(existing_block->id())) {
if (!ir_context->IsReachable(*existing_block)) {
return false;
}
@ -94,6 +92,8 @@ bool TransformationAddDeadBlock::IsApplicable(
// the selection construct, its header |existing_block| will not dominate the
// merge block |successor_block_id|, which is invalid. Thus, |existing_block|
// must dominate |successor_block_id|.
opt::DominatorAnalysis* dominator_analysis =
ir_context->GetDominatorAnalysis(existing_block->GetParent());
if (!dominator_analysis->Dominates(existing_block->id(),
successor_block_id)) {
return false;

Просмотреть файл

@ -134,7 +134,7 @@ bool TransformationAddDeadBreak::IsApplicable(
return false;
}
if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, bb_to)) {
if (!ir_context->IsReachable(*bb_to)) {
// If the target of the break is unreachable, we conservatively do not
// allow adding a dead break, to avoid the compilations that arise due to
// the lack of sensible dominance information for unreachable blocks.

Просмотреть файл

@ -83,8 +83,7 @@ bool TransformationAddDeadContinue::IsApplicable(
auto continue_block =
ir_context->cfg()->block(loop_header)->ContinueBlockId();
if (!fuzzerutil::BlockIsReachableInItsFunction(
ir_context, ir_context->cfg()->block(continue_block))) {
if (!ir_context->IsReachable(*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.

Просмотреть файл

@ -441,17 +441,17 @@ bool TransformationFlattenConditionalBranch::
header->terminator()->opcode() == SpvOpBranchConditional &&
"|header| must be the header of a conditional.");
// |header| must be reachable.
if (!ir_context->IsReachable(*header)) {
return false;
}
auto enclosing_function = header->GetParent();
auto dominator_analysis =
ir_context->GetDominatorAnalysis(enclosing_function);
auto postdominator_analysis =
ir_context->GetPostDominatorAnalysis(enclosing_function);
// |header| must be reachable.
if (!dominator_analysis->IsReachable(header)) {
return false;
}
// Check that the header and the merge block describe a single-entry,
// single-exit region.
if (!dominator_analysis->Dominates(header->id(), merge_block_id) ||

Просмотреть файл

@ -180,8 +180,7 @@ bool TransformationOutlineFunction::IsApplicable(
// predecessors. If it does, then we do not regard the region as single-
// entry-single-exit and hence do not outline it.
for (auto pred : ir_context->cfg()->preds(block.id())) {
if (!fuzzerutil::BlockIsReachableInItsFunction(
ir_context, ir_context->cfg()->block(pred))) {
if (!ir_context->IsReachable(*ir_context->cfg()->block(pred))) {
// The predecessor is unreachable.
return false;
}

Просмотреть файл

@ -386,11 +386,8 @@ bool TransformationPropagateInstructionDown::IsApplicableToBlock(
return false;
}
const auto* dominator_analysis =
ir_context->GetDominatorAnalysis(block->GetParent());
// |block| must be reachable.
if (!dominator_analysis->IsReachable(block)) {
if (!ir_context->IsReachable(*block)) {
return false;
}
@ -430,6 +427,9 @@ bool TransformationPropagateInstructionDown::IsApplicableToBlock(
auto phi_block_id =
GetOpPhiBlockId(ir_context, block_id, *inst_to_propagate, successor_ids);
const auto* dominator_analysis =
ir_context->GetDominatorAnalysis(block->GetParent());
// Make sure we can adjust all users of the propagated instruction.
return ir_context->get_def_use_mgr()->WhileEachUse(
inst_to_propagate,
@ -537,7 +537,7 @@ uint32_t TransformationPropagateInstructionDown::GetOpPhiBlockId(
// Check that |merge_block_id| is reachable in the CFG and |block_id|
// dominates |merge_block_id|.
if (!dominator_analysis->IsReachable(merge_block_id) ||
if (!ir_context->IsReachable(*ir_context->cfg()->block(merge_block_id)) ||
!dominator_analysis->Dominates(block_id, merge_block_id)) {
return 0;
}

Просмотреть файл

@ -61,7 +61,7 @@ bool TransformationPushIdThroughVariable::IsApplicable(
// The instruction to insert before must belong to a reachable block.
auto basic_block = ir_context->get_instr_block(instruction_to_insert_before);
if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, basic_block)) {
if (!ir_context->IsReachable(*basic_block)) {
return false;
}

Просмотреть файл

@ -104,9 +104,7 @@ bool CanMergeWithSuccessor(IRContext* context, BasicBlock* block) {
}
// Don't bother trying to merge unreachable blocks.
if (auto dominators = context->GetDominatorAnalysis(block->GetParent())) {
if (!dominators->IsReachable(block)) return false;
}
if (!context->IsReachable(*block)) return false;
Instruction* merge_inst = block->GetMergeInst();
const bool pred_is_header = IsHeader(block);

Просмотреть файл

@ -1034,5 +1034,11 @@ bool IRContext::CheckCFG() {
return true;
}
bool IRContext::IsReachable(const opt::BasicBlock& bb) {
auto enclosing_function = bb.GetParent();
return GetDominatorAnalysis(enclosing_function)
->Dominates(enclosing_function->entry().get(), &bb);
}
} // namespace opt
} // namespace spvtools

Просмотреть файл

@ -598,10 +598,14 @@ class IRContext {
bool ProcessCallTreeFromRoots(ProcessFunction& pfn,
std::queue<uint32_t>* roots);
// Emmits a error message to the message consumer indicating the error
// Emits a error message to the message consumer indicating the error
// described by |message| occurred in |inst|.
void EmitErrorMessage(std::string message, Instruction* inst);
// Returns true if and only if there is a path to |bb| from the entry block of
// the function that contains |bb|.
bool IsReachable(const opt::BasicBlock& bb);
private:
// Builds the def-use manager from scratch, even if it was already valid.
void BuildDefUseManager() {

Просмотреть файл

@ -27,10 +27,8 @@ const uint32_t kMergeNodeIndex = 0;
bool StructuredLoopToSelectionReductionOpportunity::PreconditionHolds() {
// Is the loop header reachable?
return loop_construct_header_->GetLabel()
->context()
->GetDominatorAnalysis(loop_construct_header_->GetParent())
->IsReachable(loop_construct_header_);
return loop_construct_header_->GetLabel()->context()->IsReachable(
*loop_construct_header_);
}
void StructuredLoopToSelectionReductionOpportunity::Apply() {
@ -78,8 +76,7 @@ void StructuredLoopToSelectionReductionOpportunity::RedirectToClosestMergeBlock(
}
already_seen.insert(pred);
if (!context_->GetDominatorAnalysis(loop_construct_header_->GetParent())
->IsReachable(pred)) {
if (!context_->IsReachable(*context_->cfg()->block(pred))) {
// We do not care about unreachable predecessors (and dominance
// information, and thus the notion of structured control flow, makes
// little sense for unreachable blocks).