mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-18 12:06:08 +00:00

We have a class `ImplicitControlFlowTracking` which allows us to keep track of instructions that can abnormally exit and answer queries like "whether or not there is side-exiting instruction above this instruction in its block". We may want to have the similar tracking for other types of "special" instructions, for example instructions that write memory. This patch separates ImplicitControlFlowTracking into two classes, isolating all general logic not related to implicit control flow into its parent class. We can later make another child of this class to keep track of instructions that write memory. The motivation for that is that we want to make these checks efficiently in the patch https://reviews.llvm.org/D50891. NOTE: The naming of the parent class is not super cool, but the other options we have are hardly better. Please feel free to rename it as NFC if you think you've found a more informative name for it. Differential Revision: https://reviews.llvm.org/D50954 Reviewed By: fedor.sergeev llvm-svn: 340728
100 lines
3.9 KiB
C++
100 lines
3.9 KiB
C++
//===-- InstructionPrecedenceTracking.cpp -----------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
// Implements a class that is able to define some instructions as "special"
|
|
// (e.g. as having implicit control flow, or writing memory, or having another
|
|
// interesting property) and then efficiently answers queries of the types:
|
|
// 1. Are there any special instructions in the block of interest?
|
|
// 2. Return first of the special instructions in the given block;
|
|
// 3. Check if the given instruction is preceeded by the first special
|
|
// instruction in the same block.
|
|
// The class provides caching that allows to answer these queries quickly. The
|
|
// user must make sure that the cached data is invalidated properly whenever
|
|
// a content of some tracked block is changed.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Analysis/ValueTracking.h"
|
|
#include "llvm/Transforms/Utils/InstructionPrecedenceTracking.h"
|
|
|
|
using namespace llvm;
|
|
|
|
const Instruction *InstructionPrecedenceTracking::getFirstSpecialInstruction(
|
|
const BasicBlock *BB) {
|
|
if (!KnownBlocks.count(BB))
|
|
fill(BB);
|
|
auto *FirstICF = FirstImplicitControlFlowInsts.lookup(BB);
|
|
assert((!FirstICF || FirstICF->getParent() == BB) && "Inconsistent cache!");
|
|
return FirstICF;
|
|
}
|
|
|
|
bool InstructionPrecedenceTracking::hasSpecialInstructions(
|
|
const BasicBlock *BB) {
|
|
return getFirstSpecialInstruction(BB) != nullptr;
|
|
}
|
|
|
|
bool InstructionPrecedenceTracking::isPreceededBySpecialInstruction(
|
|
const Instruction *Insn) {
|
|
const Instruction *MaybeFirstICF =
|
|
getFirstSpecialInstruction(Insn->getParent());
|
|
return MaybeFirstICF && OI.dominates(MaybeFirstICF, Insn);
|
|
}
|
|
|
|
void InstructionPrecedenceTracking::fill(const BasicBlock *BB) {
|
|
FirstImplicitControlFlowInsts.erase(BB);
|
|
for (auto &I : *BB)
|
|
if (isSpecialInstruction(&I)) {
|
|
FirstImplicitControlFlowInsts[BB] = &I;
|
|
break;
|
|
}
|
|
|
|
// Mark this block as having a known result.
|
|
KnownBlocks.insert(BB);
|
|
}
|
|
|
|
void InstructionPrecedenceTracking::invalidateBlock(const BasicBlock *BB) {
|
|
OI.invalidateBlock(BB);
|
|
FirstImplicitControlFlowInsts.erase(BB);
|
|
KnownBlocks.erase(BB);
|
|
}
|
|
|
|
void InstructionPrecedenceTracking::clear() {
|
|
for (auto It : FirstImplicitControlFlowInsts)
|
|
OI.invalidateBlock(It.first);
|
|
FirstImplicitControlFlowInsts.clear();
|
|
KnownBlocks.clear();
|
|
}
|
|
|
|
bool ImplicitControlFlowTracking::isSpecialInstruction(
|
|
const Instruction *Insn) const {
|
|
// If a block's instruction doesn't always pass the control to its successor
|
|
// instruction, mark the block as having implicit control flow. We use them
|
|
// to avoid wrong assumptions of sort "if A is executed and B post-dominates
|
|
// A, then B is also executed". This is not true is there is an implicit
|
|
// control flow instruction (e.g. a guard) between them.
|
|
//
|
|
// TODO: Currently, isGuaranteedToTransferExecutionToSuccessor returns false
|
|
// for volatile stores and loads because they can trap. The discussion on
|
|
// whether or not it is correct is still ongoing. We might want to get rid
|
|
// of this logic in the future. Anyways, trapping instructions shouldn't
|
|
// introduce implicit control flow, so we explicitly allow them here. This
|
|
// must be removed once isGuaranteedToTransferExecutionToSuccessor is fixed.
|
|
if (isGuaranteedToTransferExecutionToSuccessor(Insn))
|
|
return false;
|
|
if (isa<LoadInst>(Insn)) {
|
|
assert(cast<LoadInst>(Insn)->isVolatile() &&
|
|
"Non-volatile load should transfer execution to successor!");
|
|
return false;
|
|
}
|
|
if (isa<StoreInst>(Insn)) {
|
|
assert(cast<StoreInst>(Insn)->isVolatile() &&
|
|
"Non-volatile store should transfer execution to successor!");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|