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. // Consider every reachable block in the function.
auto dominator_analysis = ir_context->GetDominatorAnalysis(&function); auto dominator_analysis = ir_context->GetDominatorAnalysis(&function);
for (auto& block : function) { for (auto& block : function) {
if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, &block)) { if (!ir_context->IsReachable(block)) {
// The block is not reachable. // The block is not reachable.
continue; continue;
} }

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

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

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

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

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

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

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

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

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

@ -108,11 +108,6 @@ bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
opt::BasicBlock::iterator GetIteratorForInstruction( opt::BasicBlock::iterator GetIteratorForInstruction(
opt::BasicBlock* block, const opt::Instruction* inst); 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| // Determines whether it is OK to insert an instruction with opcode |opcode|
// before |instruction_in_block|. // before |instruction_in_block|.
bool CanInsertOpcodeBeforeInstruction( bool CanInsertOpcodeBeforeInstruction(

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

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

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

@ -134,7 +134,7 @@ bool TransformationAddDeadBreak::IsApplicable(
return false; 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 // 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 // allow adding a dead break, to avoid the compilations that arise due to
// the lack of sensible dominance information for unreachable blocks. // the lack of sensible dominance information for unreachable blocks.

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

@ -83,8 +83,7 @@ bool TransformationAddDeadContinue::IsApplicable(
auto continue_block = auto continue_block =
ir_context->cfg()->block(loop_header)->ContinueBlockId(); ir_context->cfg()->block(loop_header)->ContinueBlockId();
if (!fuzzerutil::BlockIsReachableInItsFunction( if (!ir_context->IsReachable(*ir_context->cfg()->block(continue_block))) {
ir_context, ir_context->cfg()->block(continue_block))) {
// If the loop's continue block is unreachable, we conservatively do not // 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 // allow adding a dead continue, to avoid the compilations that arise due to
// the lack of sensible dominance information for unreachable blocks. // the lack of sensible dominance information for unreachable blocks.

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

@ -441,17 +441,17 @@ bool TransformationFlattenConditionalBranch::
header->terminator()->opcode() == SpvOpBranchConditional && header->terminator()->opcode() == SpvOpBranchConditional &&
"|header| must be the header of a conditional."); "|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 enclosing_function = header->GetParent();
auto dominator_analysis = auto dominator_analysis =
ir_context->GetDominatorAnalysis(enclosing_function); ir_context->GetDominatorAnalysis(enclosing_function);
auto postdominator_analysis = auto postdominator_analysis =
ir_context->GetPostDominatorAnalysis(enclosing_function); 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, // Check that the header and the merge block describe a single-entry,
// single-exit region. // single-exit region.
if (!dominator_analysis->Dominates(header->id(), merge_block_id) || 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- // predecessors. If it does, then we do not regard the region as single-
// entry-single-exit and hence do not outline it. // entry-single-exit and hence do not outline it.
for (auto pred : ir_context->cfg()->preds(block.id())) { for (auto pred : ir_context->cfg()->preds(block.id())) {
if (!fuzzerutil::BlockIsReachableInItsFunction( if (!ir_context->IsReachable(*ir_context->cfg()->block(pred))) {
ir_context, ir_context->cfg()->block(pred))) {
// The predecessor is unreachable. // The predecessor is unreachable.
return false; return false;
} }

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

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

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

@ -61,7 +61,7 @@ bool TransformationPushIdThroughVariable::IsApplicable(
// The instruction to insert before must belong to a reachable block. // The instruction to insert before must belong to a reachable block.
auto basic_block = ir_context->get_instr_block(instruction_to_insert_before); 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; return false;
} }

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

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

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

@ -1034,5 +1034,11 @@ bool IRContext::CheckCFG() {
return true; 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 opt
} // namespace spvtools } // namespace spvtools

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

@ -598,10 +598,14 @@ class IRContext {
bool ProcessCallTreeFromRoots(ProcessFunction& pfn, bool ProcessCallTreeFromRoots(ProcessFunction& pfn,
std::queue<uint32_t>* roots); 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|. // described by |message| occurred in |inst|.
void EmitErrorMessage(std::string message, Instruction* 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: private:
// Builds the def-use manager from scratch, even if it was already valid. // Builds the def-use manager from scratch, even if it was already valid.
void BuildDefUseManager() { void BuildDefUseManager() {

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

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