mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
406 lines
12 KiB
C++
406 lines
12 KiB
C++
/// \file ControlFlowGraph.cpp
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include "llvm/ADT/DepthFirstIterator.h"
|
|
#include "llvm/Support/DOTGraphTraits.h"
|
|
#include "llvm/Support/GraphWriter.h"
|
|
#include "llvm/Support/raw_os_ostream.h"
|
|
|
|
#include "revng/ADT/GenericGraph.h"
|
|
#include "revng/BasicAnalyses/GeneratedCodeBasicInfo.h"
|
|
#include "revng/EarlyFunctionAnalysis/CFGHelpers.h"
|
|
#include "revng/EarlyFunctionAnalysis/ControlFlowGraph.h"
|
|
#include "revng/Model/Binary.h"
|
|
#include "revng/Support/IRHelpers.h"
|
|
|
|
using namespace llvm;
|
|
|
|
namespace efa {
|
|
|
|
struct FunctionCFGNodeData {
|
|
FunctionCFGNodeData(BasicBlockID ID, const MetaAddress &) : ID(ID) {}
|
|
BasicBlockID ID;
|
|
};
|
|
|
|
using FunctionCFGNode = ForwardNode<FunctionCFGNodeData>;
|
|
using FunctionCFG = GenericGraph<FunctionCFGNode, 16, true>;
|
|
|
|
/// A helper data structure to simplify working with the graph
|
|
/// when verifying the CFG.
|
|
struct FunctionCFGVerificationHelper {
|
|
public:
|
|
FunctionCFG Graph;
|
|
std::map<BasicBlockID, FunctionCFGNode *> Map;
|
|
|
|
public:
|
|
FunctionCFGVerificationHelper(const efa::ControlFlowGraph &Metadata,
|
|
const model::Binary &Binary) {
|
|
using G = FunctionCFG;
|
|
std::tie(Graph, Map) = buildControlFlowGraph<G>(Metadata.Blocks(),
|
|
Metadata.Entry(),
|
|
Binary);
|
|
}
|
|
|
|
public:
|
|
SmallVector<const FunctionCFGNode *, 4> unreachableNodes() const {
|
|
revng_assert(Graph.size() == Map.size());
|
|
if (Map.size() == 0)
|
|
return {};
|
|
|
|
// Ensure all the nodes are reachable from the entry node
|
|
df_iterator_default_set<FunctionCFGNode *> Visited;
|
|
revng_assert(Graph.getEntryNode() != nullptr);
|
|
for (auto &Ignore : depth_first_ext(Graph.getEntryNode(), Visited))
|
|
;
|
|
|
|
if (Visited.size() == Graph.size())
|
|
return {};
|
|
|
|
SmallVector<const FunctionCFGNode *, 4> Result;
|
|
|
|
for (const FunctionCFGNode *Node : Graph.nodes())
|
|
if (!Visited.contains(Node))
|
|
Result.push_back(Node);
|
|
|
|
return Result;
|
|
}
|
|
|
|
bool hasAtMostOneInvalidExit() const {
|
|
FunctionCFGNode *Exit = nullptr;
|
|
for (const auto &[Address, Node] : Map) {
|
|
if (not Address.isValid()) {
|
|
if (Node->hasSuccessors() || Exit != nullptr)
|
|
return false;
|
|
Exit = Node;
|
|
} else {
|
|
if (!Node->hasSuccessors())
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
const efa::BasicBlock *ControlFlowGraph::findBlock(GeneratedCodeBasicInfo &GCBI,
|
|
llvm::BasicBlock *BB) const {
|
|
const llvm::BasicBlock *JumpTargetBB = getJumpTargetBlock(BB);
|
|
if (JumpTargetBB == nullptr)
|
|
return nullptr;
|
|
|
|
BasicBlockID CallerBlockID = getBasicBlockID(JumpTargetBB);
|
|
revng_assert(CallerBlockID.isValid());
|
|
auto It = Blocks().find(CallerBlockID);
|
|
|
|
while (It == Blocks().end()) {
|
|
|
|
const llvm::BasicBlock *PredecessorJumpTargetBB = nullptr;
|
|
for (const llvm::BasicBlock *Predecessor : predecessors(JumpTargetBB)) {
|
|
auto IBDHB = BlockType::IndirectBranchDispatcherHelperBlock;
|
|
if (GCBI.isTranslated(Predecessor) or getType(Predecessor) == IBDHB) {
|
|
const llvm::BasicBlock *NewJT = getJumpTargetBlock(Predecessor);
|
|
if (PredecessorJumpTargetBB != nullptr) {
|
|
revng_assert(PredecessorJumpTargetBB == NewJT,
|
|
"Jump target is not in the CFG but it has multiple "
|
|
"predecessors");
|
|
}
|
|
PredecessorJumpTargetBB = NewJT;
|
|
}
|
|
}
|
|
JumpTargetBB = PredecessorJumpTargetBB;
|
|
|
|
revng_assert(JumpTargetBB != nullptr);
|
|
CallerBlockID = getBasicBlockID(JumpTargetBB);
|
|
revng_assert(CallerBlockID.isValid());
|
|
It = Blocks().find(CallerBlockID);
|
|
}
|
|
|
|
return &*It;
|
|
}
|
|
|
|
void ControlFlowGraph::serialize(GeneratedCodeBasicInfo &GCBI) const {
|
|
using namespace llvm;
|
|
using llvm::BasicBlock;
|
|
|
|
BasicBlock *BB = GCBI.getBlockAt(Entry());
|
|
LLVMContext &Context = getContext(BB);
|
|
std::string Buffer;
|
|
{
|
|
raw_string_ostream Stream(Buffer);
|
|
::serialize(Stream, *this);
|
|
}
|
|
|
|
Instruction *Term = BB->getTerminator();
|
|
MDNode *Node = MDNode::get(Context, MDString::get(Context, Buffer));
|
|
Term->setMetadata(ControlFlowGraphMDName, Node);
|
|
}
|
|
|
|
void ControlFlowGraph::simplify(const model::Binary &Binary) {
|
|
// If A does not end with a call and A.end == B.start and A is the only
|
|
// predecessor of B and B is the only successor of A, merge
|
|
|
|
// Create quick map of predecessors
|
|
std::map<BasicBlockID, SmallVector<BasicBlockID, 2>> Predecessors;
|
|
for (efa::BasicBlock &Block : Blocks()) {
|
|
for (auto &Successor : Block.Successors()) {
|
|
if (Successor->Type() == efa::FunctionEdgeType::DirectBranch
|
|
and Successor->Destination().isValid()) {
|
|
Predecessors[Successor->Destination()].push_back(Block.ID());
|
|
} else if (auto *Call = dyn_cast<efa::CallEdge>(Successor.get())) {
|
|
if (not Call->IsTailCall()
|
|
and not Call->hasAttribute(Binary,
|
|
model::FunctionAttribute::NoReturn)) {
|
|
Predecessors[Block.nextBlock()].push_back(Block.ID());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Identify blocks that need to be merged in their predecessor
|
|
SmallVector<std::pair<BasicBlockID, BasicBlockID>, 4> ToMerge;
|
|
for (efa::BasicBlock &Block : Blocks()) {
|
|
// Ignore entry block entirely
|
|
if (Block.End() == Entry())
|
|
continue;
|
|
|
|
// Do we have only one successor?
|
|
if (Block.Successors().size() != 1)
|
|
continue;
|
|
|
|
// Is the successor a direct branch to the end of the block?
|
|
auto &OnlySuccessor = *Block.Successors().begin();
|
|
if (not(OnlySuccessor->Type() == efa::FunctionEdgeType::DirectBranch
|
|
and OnlySuccessor->Destination() == Block.nextBlock()))
|
|
continue;
|
|
|
|
// Does the only successor has only one predecessor?
|
|
auto PredecessorsAddress = Predecessors.at(Block.nextBlock());
|
|
if (PredecessorsAddress.size() != 1)
|
|
continue;
|
|
|
|
// Are we the only predecessor?
|
|
if (*PredecessorsAddress.begin() != Block.ID())
|
|
continue;
|
|
|
|
ToMerge.emplace_back(Block.ID(), Block.nextBlock());
|
|
}
|
|
|
|
for (auto [PredecessorAddress, BlockAddress] : llvm::reverse(ToMerge)) {
|
|
efa::BasicBlock &Predecessor = Blocks().at(PredecessorAddress);
|
|
efa::BasicBlock &Block = Blocks().at(BlockAddress);
|
|
|
|
// Safety checks
|
|
revng_assert(Predecessor.Successors().size() == 1);
|
|
revng_assert(Predecessor.End() == Block.ID().start());
|
|
|
|
// Merge Block into Predecessor
|
|
Predecessor.End() = Block.End();
|
|
Predecessor.Successors() = std::move(Block.Successors());
|
|
|
|
// Drop Block
|
|
Blocks().erase(BlockAddress);
|
|
}
|
|
}
|
|
|
|
bool ControlFlowGraph::verify(const model::Binary &Binary) const {
|
|
return verify(Binary, false);
|
|
}
|
|
|
|
bool ControlFlowGraph::verify(const model::Binary &Binary, bool Assert) const {
|
|
model::VerifyHelper VH(Assert);
|
|
return verify(Binary, VH);
|
|
}
|
|
|
|
bool ControlFlowGraph::verify(const model::Binary &Binary,
|
|
model::VerifyHelper &VH) const {
|
|
const auto &Function = Binary.Functions().at(Entry());
|
|
|
|
if (Blocks().size() == 0)
|
|
return VH.fail("The function has no CFG", *this);
|
|
|
|
// Populate graph
|
|
FunctionCFGVerificationHelper Helper(*this, Binary);
|
|
|
|
// Ensure all the nodes are reachable from the entry node
|
|
auto UnreachableNodes = Helper.unreachableNodes();
|
|
if (UnreachableNodes.size() > 0) {
|
|
std::string Message = "The following nodes are unreachable:\n\n";
|
|
for (const FunctionCFGNode *UnreachableNode : UnreachableNodes)
|
|
Message += " " + UnreachableNode->ID.toString() + "\n";
|
|
return VH.fail(Message, *this);
|
|
}
|
|
|
|
// Ensure the only node with no successors is invalid
|
|
if (not Helper.hasAtMostOneInvalidExit())
|
|
return VH.fail("We have more than one invalid exit", *this);
|
|
|
|
// Verify blocks
|
|
if (Blocks().size() > 0) {
|
|
bool HasEntry = false;
|
|
for (const BasicBlock &Block : Blocks()) {
|
|
|
|
if (Block.ID() == BasicBlockID(Entry())) {
|
|
if (HasEntry)
|
|
return VH.fail("Multiple entry point blocks found, reporting the "
|
|
"second one",
|
|
Block);
|
|
HasEntry = true;
|
|
}
|
|
|
|
if (Block.Successors().size() == 0)
|
|
return VH.fail("A block has no successors", Block);
|
|
|
|
for (const auto &Edge : Block.Successors())
|
|
if (not Edge->verify(VH))
|
|
return VH.fail("Invalid successor", Edge);
|
|
}
|
|
|
|
if (not HasEntry) {
|
|
return VH.fail("The function CFG does not contain a block starting at "
|
|
"the entry point",
|
|
*this);
|
|
}
|
|
}
|
|
|
|
// Check function calls
|
|
for (const auto &Block : Blocks()) {
|
|
for (const auto &Edge : Block.Successors()) {
|
|
if (Edge->Type() == efa::FunctionEdgeType::FunctionCall) {
|
|
// We're in a direct call, get the callee
|
|
const auto *Call = dyn_cast<CallEdge>(Edge.get());
|
|
|
|
if (not Call->DynamicFunction().empty()) {
|
|
// It's a dynamic call
|
|
auto &Function = Call->DynamicFunction();
|
|
if (!Binary.ImportedDynamicFunctions().contains(Function))
|
|
return VH.fail("Can't find callee \"" + Call->DynamicFunction()
|
|
+ "\"",
|
|
Edge);
|
|
} else if (Call->isDirect()) {
|
|
// Regular call
|
|
if (!Binary.Functions().contains(Call->Destination().start()))
|
|
return VH.fail("Can't find callee", Edge);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ControlFlowGraph::dump() const {
|
|
::serialize(dbg, *this);
|
|
}
|
|
|
|
void ControlFlowGraph::dumpCFG(const model::Binary &Binary) const {
|
|
auto [Graph,
|
|
_] = buildControlFlowGraph<FunctionCFG>(Blocks(), Entry(), Binary);
|
|
WriteGraph(&Graph, "function-metadata");
|
|
}
|
|
|
|
bool FunctionEdgeBase::verify() const {
|
|
return verify(false);
|
|
}
|
|
|
|
bool FunctionEdgeBase::verify(bool Assert) const {
|
|
model::VerifyHelper VH(Assert);
|
|
return verify(VH);
|
|
}
|
|
|
|
bool FunctionEdgeBase::verify(model::VerifyHelper &VH) const {
|
|
using namespace efa::FunctionEdgeType;
|
|
|
|
switch (Type()) {
|
|
case Invalid:
|
|
case Count:
|
|
return VH.fail();
|
|
|
|
case DirectBranch:
|
|
if (not Destination().isValid())
|
|
return VH.fail();
|
|
break;
|
|
case FunctionCall: {
|
|
const auto &Call = cast<const CallEdge>(*this);
|
|
if (Destination().isValid()) {
|
|
if (not Call.DynamicFunction().empty())
|
|
return VH.fail("Dynamic function has destination address");
|
|
|
|
if (Destination().isInlined())
|
|
return VH.fail("Callee block marked as inlined");
|
|
}
|
|
} break;
|
|
|
|
case Return:
|
|
case BrokenReturn:
|
|
case LongJmp:
|
|
case Killer:
|
|
case Unreachable:
|
|
if (Destination().isValid())
|
|
return VH.fail();
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FunctionEdgeBase::dump() const {
|
|
serialize(dbg, *this);
|
|
}
|
|
|
|
void CallEdge::dump() const {
|
|
serialize(dbg, *this);
|
|
}
|
|
|
|
model::Identifier BasicBlock::name() const {
|
|
using llvm::Twine;
|
|
return model::Identifier(std::string("bb_") + ID().toString());
|
|
}
|
|
|
|
void BasicBlock::dump() const {
|
|
serialize(dbg, *this);
|
|
}
|
|
|
|
bool BasicBlock::verify() const {
|
|
return verify(false);
|
|
}
|
|
|
|
bool BasicBlock::verify(bool Assert) const {
|
|
model::VerifyHelper VH(Assert);
|
|
return verify(VH);
|
|
}
|
|
|
|
bool BasicBlock::verify(model::VerifyHelper &VH) const {
|
|
if (not ID().isValid() or End().isInvalid())
|
|
return VH.fail();
|
|
|
|
for (auto &Edge : Successors())
|
|
if (not Edge->verify(VH))
|
|
return VH.fail();
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace efa
|
|
|
|
template<>
|
|
struct llvm::DOTGraphTraits<efa::FunctionCFG *> : public DefaultDOTGraphTraits {
|
|
DOTGraphTraits(bool Simple = false) : DefaultDOTGraphTraits(Simple) {}
|
|
|
|
static std::string getNodeLabel(const efa::FunctionCFGNode *Node,
|
|
const efa::FunctionCFG *) {
|
|
return Node->ID.toString();
|
|
}
|
|
|
|
static std::string getNodeAttributes(const efa::FunctionCFGNode *Node,
|
|
const efa::FunctionCFG *Graph) {
|
|
if (Node->ID == Graph->getEntryNode()->ID) {
|
|
return "shape=box,peripheries=2";
|
|
}
|
|
|
|
return "";
|
|
}
|
|
};
|