Files
2025-10-31 17:25:03 +01:00

1048 lines
38 KiB
C++

#pragma once
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <algorithm>
#include <cstdlib>
#include <fstream>
#include <iterator>
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/GraphTraits.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/SCCIterator.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/GenericDomTreeConstruction.h"
#include "llvm/Support/raw_os_ostream.h"
#include "revng/ADT/ReversePostOrderTraversal.h"
#include "revng/BasicAnalyses/GeneratedCodeBasicInfo.h"
#include "revng/RestructureCFG/ASTNodeUtils.h"
#include "revng/RestructureCFG/ASTTree.h"
#include "revng/RestructureCFG/BasicBlockNodeBB.h"
#include "revng/RestructureCFG/MetaRegionBB.h"
#include "revng/RestructureCFG/RegionCFGTree.h"
#include "revng/RestructureCFG/Utils.h"
#include "revng/Support/Debug.h"
#include "revng/Support/IRHelpers.h"
#include "BasicBlockNode.h"
class GHASTDumper {
size_t GraphLogCounter;
Logger &Logger;
std::string FunctionName;
const ASTTree &AST;
std::string FolderName;
public:
GHASTDumper(::Logger &Logger,
const llvm::Function &F,
const ASTTree &TheAST,
const std::string &FolderName) :
GraphLogCounter(0),
Logger(Logger),
FunctionName(F.getName().str()),
AST(TheAST),
FolderName(FolderName) {}
void log(const std::string &Filename) {
if (Logger.isEnabled()) {
AST.dumpASTOnFile(FunctionName,
"ast-" + FolderName,
std::to_string(GraphLogCounter++) + "-" + Filename);
}
}
};
// Helper function that visit an AST tree and creates the sequence nodes
inline ASTNode *createSequence(ASTTree &Tree, ASTNode *RootNode) {
SequenceNode *RootSequenceNode = Tree.addSequenceNode();
RootSequenceNode->addNode(RootNode);
for (ASTNode *Node : RootSequenceNode->nodes()) {
switch (Node->getKind()) {
case ASTNode::NK_If: {
auto *If = llvm::cast<IfNode>(Node);
if (If->hasThen())
If->setThen(createSequence(Tree, If->getThen()));
if (If->hasElse())
If->setElse(createSequence(Tree, If->getElse()));
} break;
case ASTNode::NK_Switch: {
auto *Switch = llvm::cast<SwitchNode>(Node);
for (auto &LabelCasePair : Switch->cases())
LabelCasePair.second = createSequence(Tree, LabelCasePair.second);
} break;
case ASTNode::NK_Scs: {
auto *Scs = llvm::cast<ScsNode>(Node);
if (Scs->hasBody())
Scs->setBody(createSequence(Tree, Scs->getBody()));
} break;
case ASTNode::NK_Code: {
// TODO: confirm that doesn't make sense to process a code node.
} break;
case ASTNode::NK_Continue:
case ASTNode::NK_Break:
case ASTNode::NK_SwitchBreak:
case ASTNode::NK_Set: {
// Do nothing for these nodes
} break;
case ASTNode::NK_List:
default:
revng_abort("AST node type not expected");
}
}
return RootSequenceNode;
}
// Helper function that simplifies useless dummy nodes
inline void simplifyDummies(ASTTree &AST, ASTNode *RootNode) {
switch (RootNode->getKind()) {
case ASTNode::NK_List: {
auto *Sequence = llvm::cast<SequenceNode>(RootNode);
std::vector<ASTNode *> UselessDummies;
for (ASTNode *Node : Sequence->nodes()) {
if (Node->isDummy()) {
UselessDummies.push_back(Node);
} else {
simplifyDummies(AST, Node);
}
}
for (ASTNode *Node : UselessDummies) {
Sequence->removeNode(Node);
AST.removeASTNode(Node);
}
} break;
case ASTNode::NK_If: {
auto *If = llvm::cast<IfNode>(RootNode);
if (If->hasThen()) {
simplifyDummies(AST, If->getThen());
}
if (If->hasElse()) {
simplifyDummies(AST, If->getElse());
}
} break;
case ASTNode::NK_Switch: {
auto *Switch = llvm::cast<SwitchNode>(RootNode);
for (auto &LabelCaseNodePair : Switch->cases())
simplifyDummies(AST, LabelCaseNodePair.second);
} break;
case ASTNode::NK_Scs: {
auto *Scs = llvm::cast<ScsNode>(RootNode);
if (Scs->hasBody())
simplifyDummies(AST, Scs->getBody());
} break;
case ASTNode::NK_Code:
case ASTNode::NK_Continue:
case ASTNode::NK_Break:
case ASTNode::NK_SwitchBreak:
case ASTNode::NK_Set:
// Do nothing
break;
default:
revng_unreachable();
}
}
template<class NodeT>
inline BasicBlockNode<NodeT> *getDirectSuccessor(BasicBlockNode<NodeT> *Node) {
BasicBlockNode<NodeT> *Successor = nullptr;
if (Node->successor_size() == 1) {
Successor = Node->getSuccessorI(0);
}
return Successor;
}
template<class NodeT>
inline BasicBlockNode<NodeT> *
getUniqueSuccessorOrNull(BasicBlockNode<NodeT> *Node) {
BasicBlockNode<NodeT> *Successor = nullptr;
revng_assert(Node->successor_size() <= 1);
if (Node->successor_size() == 1) {
Successor = Node->getSuccessorI(0);
}
return Successor;
}
template<class NodeT>
inline std::vector<BasicBlockNode<NodeT> *>
getNotInlinedSuccs(BasicBlockNode<NodeT> *Node) {
using BasicBlockNodeT = BasicBlockNode<NodeT>;
using ConstEdge = std::pair<const BasicBlockNodeT *, const BasicBlockNodeT *>;
std::vector<BasicBlockNodeT *> NotInlinedSuccessors;
for (BasicBlockNodeT *Successor : Node->successors()) {
if (not isEdgeInlined(ConstEdge{ Node, Successor })) {
NotInlinedSuccessors.push_back(Successor);
}
}
return NotInlinedSuccessors;
}
template<class NodeT>
inline std::vector<BasicBlockNode<NodeT> *>
getNotInlinedNotTerminalSuccs(BasicBlockNode<NodeT> *Node) {
using BasicBlockNodeT = BasicBlockNode<NodeT>;
using ConstEdge = std::pair<const BasicBlockNodeT *, const BasicBlockNodeT *>;
std::vector<BasicBlockNodeT *> NotInlinedNotTerminalSuccessors;
for (BasicBlockNodeT *Successor : Node->successors()) {
if (not isEdgeInlined(ConstEdge{ Node, Successor })
and getUniqueSuccessorOrNull(Successor)) {
NotInlinedNotTerminalSuccessors.push_back(Successor);
}
}
return NotInlinedNotTerminalSuccessors;
}
template<class NodeT>
inline BasicBlockNode<NodeT> *
findCommonPostDom(BasicBlockNode<NodeT> *Succ1, BasicBlockNode<NodeT> *Succ2) {
// Retrieve the successor of the two successors of the `IfNode`, and check
// that or the retrieved node is equal for both the successors, or it does
// not exists for both of them.
BasicBlockNode<NodeT> *SuccOfSucc1 = nullptr;
BasicBlockNode<NodeT> *SuccOfSucc2 = nullptr;
revng_assert(Succ1->successor_size() < 2);
revng_assert(Succ2->successor_size() < 2);
if (Succ1->successor_size() == 1)
SuccOfSucc1 = getDirectSuccessor(Succ1);
if (Succ2->successor_size() == 1)
SuccOfSucc2 = getDirectSuccessor(Succ2);
revng_assert(SuccOfSucc1 == SuccOfSucc2);
return SuccOfSucc1;
}
template<class NodeT>
inline ASTNode *findASTNode(ASTTree &AST,
typename RegionCFG<NodeT>::BBNodeMap &TileToNodeMap,
BasicBlockNode<NodeT> *Node) {
if (auto It = TileToNodeMap.find(Node); It != TileToNodeMap.end())
Node = It->second;
return AST.findASTNode(Node);
}
template<class NodeT>
inline void
connectTile(llvm::DominatorTreeBase<BasicBlockNode<NodeT>, false> &ASTDT,
BasicBlockNode<NodeT> *Node,
BasicBlockNode<NodeT> *Tile) {
using BBNodeT = BasicBlockNode<NodeT>;
using BasicBlockNodeTVect = std::vector<BBNodeT *>;
using EdgeDescriptor = typename BBNodeT::EdgeDescriptor;
// Move all the edges incoming in the head of the collapsed region to the tile
// node.
BasicBlockNodeTVect Predecessors;
for (BBNodeT *Predecessor : Node->predecessors())
Predecessors.push_back(Predecessor);
for (BBNodeT *Predecessor : Predecessors) {
moveEdgeTarget(EdgeDescriptor{ Predecessor, Node }, Tile);
// Update the dominator tree used for AST building.
using DomUpdate = typename llvm::DominatorTreeBase<BasicBlockNode<NodeT>,
false>::UpdateType;
const auto Insert = llvm::DominatorTree::Insert;
const auto Delete = llvm::DominatorTree::Delete;
std::vector<DomUpdate> Updates;
Updates.push_back({ Delete, Predecessor, Node });
Updates.push_back({ Insert, Predecessor, Tile });
ASTDT.applyUpdates(Updates);
}
}
template<class NodeT>
inline BasicBlockNode<NodeT> *
createTileImpl(RegionCFG<NodeT> &Graph,
llvm::DominatorTreeBase<BasicBlockNode<NodeT>, false> &ASTDT,
typename RegionCFG<NodeT>::BBNodeMap &TileToNodeMap,
BasicBlockNode<NodeT> *Node,
BasicBlockNode<NodeT> *End,
bool EndIsPartOfTile) {
using BBNodeT = BasicBlockNode<NodeT>;
using BasicBlockNodeTVect = std::vector<BBNodeT *>;
using EdgeDescriptor = typename BBNodeT::EdgeDescriptor;
// Create the new tile node
BBNodeT *Tile = Graph.addTile();
// Connect the incoming edge to the newly created tile
connectTile(ASTDT, Node, Tile);
// The `End` node is considered as part of the tile that is being collapsed
if (EndIsPartOfTile) {
// If `End` is marked as part of the tile, it must be present
revng_assert(End);
// We move all the edges exiting from the `End` node of the collapsed region
// to the tile node
BasicBlockNodeTVect Successors;
for (BBNodeT *Successor : End->successors())
Successors.push_back(Successor);
for (BBNodeT *Successor : Successors) {
auto Edge = extractLabeledEdge(EdgeDescriptor{ End, Successor });
ASTDT.deleteEdge(End, Successor);
addEdge(EdgeDescriptor{ Tile, Successor }, Edge.second);
ASTDT.insertEdge(Tile, Successor);
}
}
// Update the map containing the mapping between tiles and the head node which
// gave origin to a certain tile.
TileToNodeMap[Tile] = Node;
return Tile;
}
template<class NodeT>
inline BasicBlockNode<NodeT> *
createTile(RegionCFG<NodeT> &Graph,
llvm::DominatorTreeBase<BasicBlockNode<NodeT>, false> &ASTDT,
typename RegionCFG<NodeT>::BBNodeMap &TileToNodeMap,
BasicBlockNode<NodeT> *Node,
BasicBlockNode<NodeT> *End,
bool EndIsPartOfTile) {
using BBNodeT = BasicBlockNode<NodeT>;
using EdgeDescriptor = typename BBNodeT::EdgeDescriptor;
// Call the `Impl` function that is responsible of creating the tile node
BBNodeT *Tile = createTileImpl(Graph,
ASTDT,
TileToNodeMap,
Node,
End,
EndIsPartOfTile);
// When `End` is present and not part of the collapsed tile, we need to
// connect the `Tile` node to it, in order to preserve the original edges in
// the control-flow
if (not EndIsPartOfTile and End != nullptr) {
moveEdgeSource(EdgeDescriptor(Node, End), Tile);
ASTDT.deleteEdge(Node, End);
ASTDT.insertEdge(Tile, End);
}
return Tile;
}
template<class NodeT>
inline BasicBlockNode<NodeT> *
createSwitchTile(RegionCFG<NodeT> &Graph,
llvm::DominatorTreeBase<BasicBlockNode<NodeT>, false> &ASTDT,
typename RegionCFG<NodeT>::BBNodeMap &TileToNodeMap,
BasicBlockNode<NodeT> *Node,
BasicBlockNode<NodeT> *End,
bool EndIsPartOfTile,
std::vector<BasicBlockNode<NodeT> *>
&NotInlinedNotTerminalSuccessors) {
using BBNodeT = BasicBlockNode<NodeT>;
using BasicBlockNodeT = BasicBlockNode<NodeT>;
using EdgeDescriptor = typename BBNodeT::EdgeDescriptor;
using EdgeInfo = typename BasicBlockNodeT::EdgeInfo;
// Call the `Impl` function that is responsible of creating the tile node
BBNodeT *Tile = createTileImpl(Graph,
ASTDT,
TileToNodeMap,
Node,
End,
EndIsPartOfTile);
// When `End` is present and not part of the collapsed tile, we need to
// connect the `Tile` node to it, in order to preserve the original edges in
// the control-flow
if (not EndIsPartOfTile and End != nullptr) {
// Step 1:
// There is a common successor (which is the `End` node) for all the cases
// of the switch, excluding the inlined cases and the case nodes without a
// successor (see comments on the declaration of the
// `NotInlinedNotTerminalSuccessors` variable for a motivation for this). We
// need to remove all the edges exiting from the cases, and replace them
// with an edge connect the `Tile` node to `End`.
std::optional<EdgeInfo> EdgeInfoN;
for (BasicBlockNodeT *Successor : NotInlinedNotTerminalSuccessors) {
// We do not process `Successor` when it is equal to `End`, it will be
// treated separately
if (Successor == End) {
continue;
}
BasicBlockNodeT *SuccOfSuccN = getUniqueSuccessorOrNull(Successor);
revng_assert(SuccOfSuccN == End);
auto Edge = extractLabeledEdge(EdgeDescriptor{ Successor, End });
// Check that all the `EdgeInfo`s attached to the edges we are
// going to remove, are equal.
if (not EdgeInfoN.has_value()) {
EdgeInfoN.emplace(Edge.second);
} else {
// The following should be an assert, but since the backend is in
// maintenance mode, we have an early return to propagate an early
// failure.
if (EdgeInfoN.value() != Edge.second) {
return nullptr;
}
}
ASTDT.deleteEdge(Successor, End);
}
// Connect the tile to the elected successor, if we found candidate nodes
// eligible above
if (EdgeInfoN.has_value()) {
addEdge(EdgeDescriptor{ Tile, End }, EdgeInfoN.value());
ASTDT.insertEdge(Tile, End);
}
// Step 2:
// It may be that `End` was itself a successor of `Node`, and in Step 1 we
// did not connect `Tile` with `End`. In such situation, we need to do that
// now.
if (Node->hasSuccessor(End) and not(Tile->hasSuccessor(End))) {
moveEdgeSource(EdgeDescriptor(Node, End), Tile);
ASTDT.deleteEdge(Node, End);
ASTDT.insertEdge(Tile, End);
}
}
return Tile;
}
inline bool
generateAst(RegionCFG<llvm::BasicBlock *> &Region,
ASTTree &AST,
std::map<RegionCFG<llvm::BasicBlock *> *, ASTTree> &CollapsedMap) {
// Define some using used in all the function body.
using NodeT = llvm::BasicBlock *;
using BasicBlockNodeT = typename RegionCFG<NodeT>::BasicBlockNodeT;
// Get some fields of `RegionCFG`.
std::string RegionName = Region.getRegionName();
std::string FunctionName = Region.getFunctionName();
Region.markUnreachableAsInlined();
// Invoke the weave function.
Region.weave();
// Invoke the inflate function.
if (not Region.inflate())
// We propagate the failure upwards
return false;
// After we are done with the combing, we need to pre-compute the weight of
// the current RegionCFG, so that during the untangle phase of other
// `RegionCFG` that contain a collapsed node pointing to the current
// `RegionCFG`. the weight of collapsed node is ready to consume. Indeed,
// after the tiling phase, the `RegionCFG` is destroyed, so the last place
// where we can compute it is here.
Region.computeUntangleWeight();
// TODO: factorize out the AST generation phase.
llvm::DominatorTreeBase<BasicBlockNode<NodeT>, false> ASTDT;
ASTDT.recalculate(Region);
CombLogger << DoLog;
std::map<BasicBlockNode<NodeT> *, BasicBlockNode<NodeT> *> TileToNodeMap;
using BasicBlockNodeTVect = typename RegionCFG<NodeT>::BasicBlockNodeTVect;
BasicBlockNodeTVect PONodes;
for (auto *N : post_order(&Region))
PONodes.push_back(N);
CFGDumper Dumper(Region, FunctionName, RegionName, "tile");
for (BasicBlockNode<NodeT> *Node : PONodes) {
Dumper.log("-node-" + Node->getNameStr());
// Collect the children nodes in the dominator tree.
llvm::SmallVector<decltype(Node), 8> Children;
{
const auto &DomTreeChildren = ASTDT[Node]->children();
for (auto *DomTreeNode : DomTreeChildren)
Children.push_back(DomTreeNode->getBlock());
}
// Collect the successor nodes of the current analyzed node.
llvm::SmallVector<decltype(Node), 8> Successors;
for (BasicBlockNode<NodeT> *Successor : Node->successors())
Successors.push_back(Successor);
// Handle collapsded node.
ASTTree::ast_unique_ptr ASTObject;
if (Node->isCollapsed()) {
revng_assert(Children.size() <= 1);
RegionCFG<NodeT> *BodyGraph = Node->getCollapsedCFG();
revng_assert(BodyGraph != nullptr);
revng_log(CombLogger,
"Inspecting collapsed node: " << Node->getNameStr());
// Call recursively the generation of the AST for the collapsed node.
const auto &[It, New] = CollapsedMap.insert({ BodyGraph, ASTTree() });
ASTTree &CollapsedAST = It->second;
if (New)
generateAst(*BodyGraph, CollapsedAST, CollapsedMap);
ASTNode *Body = AST.copyASTNodesFrom(CollapsedAST);
switch (Successors.size()) {
case 0: {
ASTObject.reset(new ScsNode(Node, Body));
} break;
case 1: {
auto *Succ = Successors[0];
ASTNode *ASTChild = nullptr;
if (ASTDT.dominates(Node, Succ)) {
ASTChild = findASTNode(AST, TileToNodeMap, Succ);
createTile(Region, ASTDT, TileToNodeMap, Node, Succ, true);
}
ASTObject.reset(new ScsNode(Node, Body, ASTChild));
} break;
default:
revng_abort();
}
} else if (Node->isDispatcher() or isASwitch(Node)) {
revng_assert(Node->isCode() or Node->isDispatcher());
llvm::Value *SwitchCondition = nullptr;
if (not Node->isDispatcher()) {
NodeT OriginalNode = Node->getOriginalNode();
llvm::Instruction *Terminator = OriginalNode->getTerminator();
llvm::SwitchInst *Switch = llvm::cast<llvm::SwitchInst>(Terminator);
SwitchCondition = Switch->getCondition();
}
revng_assert(SwitchCondition or Node->isDispatcher());
// Results variables
bool EndIsPartOfTile = false;
BasicBlockNodeT *PostDomBB = nullptr;
BasicBlockNodeT *CandidateFallthroughBB = nullptr;
BasicBlockNodeTVect NotInlinedSuccessors = getNotInlinedSuccs(Node);
// In the following, we will also need to exclude successors of the switch
// that do not have successors, but are not inlined either. An example
// motivating this: Imagine node C that has two successors, that are both
// inlined. That node C is the successor of node B. So at some point we
// create tile A, which "inside" has B followed by C followed by its
// inlined successors. This node A is indeed an example of a case node
// without a successor which is not inlined, but that we still do not need
// to consider when performing the search for the common fallthrough node.
BasicBlockNodeTVect
NotInlinedNotTerminalSuccessors = getNotInlinedNotTerminalSuccs(Node);
// Count the non inlined successors
unsigned NotInlined = NotInlinedSuccessors.size();
// Criterion 1:
// We have a single `non inlined` successor, such successor will become
// the `CandidateFallthroughBB`
if (NotInlined == 1) {
// Handle the special case with one a single non inlined successor
const auto NotInlined = [&Node](const auto *Child) {
using ConstEdge = std::pair<const BasicBlockNodeT *,
const BasicBlockNodeT *>;
return not isEdgeInlined(ConstEdge{ Node, Child });
};
auto It = llvm::find_if(Successors, NotInlined);
// Assert that we found one
revng_assert(It != Successors.end());
CandidateFallthroughBB = *It;
// Assert that we don't find more that one
It = std::find_if(std::next(It), Successors.end(), NotInlined);
revng_assert(It == Successors.end());
} else {
// We pre-compute the successor of all the cases that are not inlined
// and not terminal
llvm::SmallVector<BasicBlockNodeT *> SuccOfCases;
for (BasicBlockNodeT *Case : NotInlinedNotTerminalSuccessors) {
BasicBlockNodeT *SuccOfCase = getUniqueSuccessorOrNull(Case);
revng_assert(SuccOfCase);
SuccOfCases.push_back(SuccOfCase);
}
// Criterion 2:
// For each successor, check if a certain one is successor of all the
// other cases
for (BasicBlockNodeT *Case : NotInlinedSuccessors) {
unsigned Count = llvm::count(SuccOfCases, Case);
if (Count > 0) {
if ((getUniqueSuccessorOrNull(Case)
and Count == SuccOfCases.size() - 1)
or (not getUniqueSuccessorOrNull(Case)
and Count == SuccOfCases.size())) {
revng_assert(not CandidateFallthroughBB);
CandidateFallthroughBB = Case;
}
}
}
// Criterion 3:
// Search for node which is the successor for all the cases (excluding
// the inlined ones), even if not a successor of `Node` itself
std::map<BasicBlockNodeT *, unsigned> SuccCounterMap;
for (BasicBlockNodeT *Elem : SuccOfCases) {
SuccCounterMap[Elem]++;
}
for (const auto &[Key, Value] : SuccCounterMap) {
if (Value == SuccOfCases.size()) {
// Criteria 1 and 2 may overlap in some cases, but it is important
// that if they do they elect the same `CandidateFallthroughBB`
revng_assert(not CandidateFallthroughBB
or Key == CandidateFallthroughBB);
CandidateFallthroughBB = Key;
}
}
}
// We now have two possibilities in front of us:
// 1) The `CandidateFallthrough` is dominated. We consider the node to
// be the postdominator of `Node`.
// 2) The `CandidateFallthrough` is not dominated. It will not be part
// of the current tile.
if (CandidateFallthroughBB
and ASTDT.dominates(Node, CandidateFallthroughBB)) {
PostDomBB = CandidateFallthroughBB;
EndIsPartOfTile = true;
} else {
PostDomBB = nullptr;
EndIsPartOfTile = false;
}
// The only legit situation for not having elected a
// `CandidateFallthroughBB` node, is when all the successors are inlined
// or do not have a successor themselves
revng_assert(CandidateFallthroughBB
or NotInlinedNotTerminalSuccessors.size() == 0);
SwitchNode::case_container LabeledCases;
llvm::SmallVector<ASTNode *> SwitchBreakVector;
bool HasDefault = false;
for (const auto &[SwitchSucc, EdgeInfos] : Node->labeled_successors()) {
ASTNode *ASTPointer = nullptr;
if (SwitchSucc == CandidateFallthroughBB) {
ASTPointer = AST.addSwitchBreak(nullptr);
SwitchBreakVector.push_back(ASTPointer);
} else {
ASTPointer = findASTNode(AST, TileToNodeMap, SwitchSucc);
}
revng_assert(nullptr != ASTPointer);
if (EdgeInfos.Labels.empty()) {
revng_assert(HasDefault == false);
HasDefault = true;
}
LabeledCases.push_back({ EdgeInfos.Labels, ASTPointer });
}
revng_assert(HasDefault or Node->isWeaved() or Node->isDispatcher());
// The following call may return `nullptr` as a signal of failure, and in
// that case we propagate the error upwards
if (not(createSwitchTile(Region,
ASTDT,
TileToNodeMap,
Node,
CandidateFallthroughBB,
EndIsPartOfTile,
NotInlinedNotTerminalSuccessors))) {
return false;
}
// If we elected a postdominator for the current tile, we retrieve the
// corresponding AST node in order to set it as the postdominator on the
// AST too
ASTNode *PostDomAST = nullptr;
if (PostDomBB) {
PostDomAST = findASTNode(AST, TileToNodeMap, PostDomBB);
}
ASTObject.reset(new SwitchNode(Node,
SwitchCondition,
std::move(LabeledCases),
PostDomAST));
for (ASTNode *Break : SwitchBreakVector) {
SwitchBreakNode *SwitchBreakCast = llvm::cast<SwitchBreakNode>(Break);
SwitchNode *Switch = llvm::cast<SwitchNode>(ASTObject.get());
SwitchBreakCast->setParentSwitch(Switch);
}
} else {
switch (Successors.size()) {
case 2: {
switch (Children.size()) {
case 0: {
// This means that both our exiting edges have been inlined, and we
// do not have any immediate postdominator. This situation should
// not arise, since not having at least one of the two branches
// dominated is a signal of an error.
revng_assert(not Node->isBreak() and not Node->isContinue()
and not Node->isSet());
revng_log(CombLogger,
"Node " << Node->getNameStr()
<< " does not dominate any "
"node, but has two successors.");
revng_unreachable("A node does not dominate any node, but has two "
"successors.");
} break;
case 1: {
// This means that we have two successors, but we only dominate a
// single node. This situation is possible only if we have an
// inlined edge and we have nopostdominator in the tile.
revng_assert(not Node->isBreak() and not Node->isContinue()
and not Node->isSet());
BasicBlockNode<NodeT> *Successor1 = Successors[0];
BasicBlockNode<NodeT> *Successor2 = Successors[1];
ASTNode *Then = nullptr;
ASTNode *Else = nullptr;
BasicBlockNodeT *NotDominatedSucc = nullptr;
using ConstEdge = std::pair<const BasicBlockNodeT *,
const BasicBlockNodeT *>;
bool Inlined1 = isEdgeInlined(ConstEdge{ Node, Successor1 });
bool Inlined2 = isEdgeInlined(ConstEdge{ Node, Successor2 });
if (Inlined1 and Inlined2) {
revng_assert(ASTDT.dominates(Node, Successor1));
revng_assert(ASTDT.dominates(Node, Successor2));
Then = findASTNode(AST, TileToNodeMap, Successor1);
Else = findASTNode(AST, TileToNodeMap, Successor2);
} else if (Inlined1) {
revng_assert(ASTDT.dominates(Node, Successor1));
Then = findASTNode(AST, TileToNodeMap, Successor1);
NotDominatedSucc = Successor2;
} else if (Inlined2) {
revng_assert(ASTDT.dominates(Node, Successor2));
Else = findASTNode(AST, TileToNodeMap, Successor2);
NotDominatedSucc = Successor1;
} else {
auto *DominatedSucc = Children[0];
revng_assert(DominatedSucc == Successor1
or DominatedSucc == Successor2);
NotDominatedSucc = DominatedSucc == Successor1 ? Successor2 :
Successor1;
if (DominatedSucc == Successor1)
Then = findASTNode(AST, TileToNodeMap, Successor1);
if (DominatedSucc == Successor2)
Else = findASTNode(AST, TileToNodeMap, Successor2);
}
createTile(Region,
ASTDT,
TileToNodeMap,
Node,
NotDominatedSucc,
false);
// Build the `IfNode`.
using UniqueExpr = ASTTree::expr_unique_ptr;
using ExprDestruct = ASTTree::expr_destructor;
auto *OriginalNode = Node->getOriginalNode();
UniqueExpr CondExpr(new AtomicNode(OriginalNode), ExprDestruct());
ExprNode *Condition = AST.addCondExpr(std::move(CondExpr));
// Insert the postdominator if the current tile actually has it.
ASTObject.reset(new IfNode(Node, Condition, Then, Else, nullptr));
} break;
case 2: {
// TODO: Handle this case.
// This means that we have two successors, and we dominate both the
// two successors, or one successor and the postdominator.
revng_assert(not Node->isBreak() and not Node->isContinue()
and not Node->isSet());
BasicBlockNode<NodeT> *Successor1 = Successors[0];
BasicBlockNode<NodeT> *Successor2 = Successors[1];
// First of all, check if one of the successors can also be
// considered as the immediate postdominator of the tile.
auto *SuccOfSucc1 = getDirectSuccessor(Successor1);
auto *SuccOfSucc2 = getDirectSuccessor(Successor2);
using ConstEdge = std::pair<const BasicBlockNodeT *,
const BasicBlockNodeT *>;
bool Inlined1 = isEdgeInlined(ConstEdge{ Node, Successor1 });
bool Inlined2 = isEdgeInlined(ConstEdge{ Node, Successor2 });
ASTNode *Then = nullptr;
ASTNode *Else = nullptr;
BasicBlockNode<NodeT> *PostDomBB = nullptr;
if (SuccOfSucc1 != SuccOfSucc2) {
if (Inlined1 and Inlined2) {
revng_assert(ASTDT.dominates(Node, Successor1));
revng_assert(ASTDT.dominates(Node, Successor2));
Then = findASTNode(AST, TileToNodeMap, Successor1);
Else = findASTNode(AST, TileToNodeMap, Successor2);
} else if (Inlined1) {
revng_assert(ASTDT.dominates(Node, Successor1));
Then = findASTNode(AST, TileToNodeMap, Successor1);
PostDomBB = Successor2;
} else if (Inlined2) {
revng_assert(ASTDT.dominates(Node, Successor2));
Else = findASTNode(AST, TileToNodeMap, Successor2);
PostDomBB = Successor1;
} else if (SuccOfSucc1 == Successor2) {
revng_assert(SuccOfSucc2 != Successor1);
revng_assert(ASTDT.dominates(Node, Successor1));
Then = findASTNode(AST, TileToNodeMap, Successor1);
PostDomBB = Successor2;
} else if (SuccOfSucc2 == Successor1) {
revng_assert(SuccOfSucc1 != Successor2);
revng_assert(ASTDT.dominates(Node, Successor2));
Else = findASTNode(AST, TileToNodeMap, Successor2);
PostDomBB = Successor1;
} else {
revng_assert(ASTDT.dominates(Node, Successor1));
revng_assert(ASTDT.dominates(Node, Successor2));
Then = findASTNode(AST, TileToNodeMap, Successor1);
Else = findASTNode(AST, TileToNodeMap, Successor2);
}
} else {
revng_assert(ASTDT.dominates(Node, Successor1));
revng_assert(ASTDT.dominates(Node, Successor2));
Then = findASTNode(AST, TileToNodeMap, Successor1);
Else = findASTNode(AST, TileToNodeMap, Successor2);
}
// Build the `IfNode`.
using UniqueExpr = ASTTree::expr_unique_ptr;
using ExprDestruct = ASTTree::expr_destructor;
auto *OriginalNode = Node->getOriginalNode();
UniqueExpr CondExpr(new AtomicNode(OriginalNode), ExprDestruct());
ExprNode *Condition = AST.addCondExpr(std::move(CondExpr));
// Insert the postdominator if the current tile actually has it.
ASTNode *PostDom = nullptr;
if (PostDomBB)
PostDom = findASTNode(AST, TileToNodeMap, PostDomBB);
ASTObject.reset(new IfNode(Node, Condition, Then, Else, PostDom));
if (PostDomBB) {
createTile(Region, ASTDT, TileToNodeMap, Node, PostDomBB, true);
} else {
createTile(Region, ASTDT, TileToNodeMap, Node, PostDomBB, false);
}
} break;
case 3: {
// This is the standard situation, we have two successors, we
// dominate both of them and we also dominate the postdominator
// node.
revng_assert(not Node->isBreak() and not Node->isContinue()
and not Node->isSet());
// Check that our successor nodes are also in the dominated node
// vector.
BasicBlockNode<NodeT> *Successor1 = Successors[0];
BasicBlockNode<NodeT> *Successor2 = Successors[1];
revng_assert(containsSmallVector(Children, Successor1));
revng_assert(containsSmallVector(Children, Successor2));
ASTNode *Then = findASTNode(AST, TileToNodeMap, Successor1);
ASTNode *Else = findASTNode(AST, TileToNodeMap, Successor2);
// Retrieve the successors of the `then` and `else` nodes. We expect
// the successor to be identical due to the structure of the tile we
// are covering. And we expect it to be the PostDom node of the
// tile.
BasicBlockNode<NodeT> *PostDomBB = findCommonPostDom(Successor1,
Successor2);
ASTNode *PostDom = nullptr;
if (PostDomBB != nullptr) {
// Check that the postdom is between the nodes dominated by the
// current node.
revng_assert(containsSmallVector(Children, PostDomBB));
PostDom = findASTNode(AST, TileToNodeMap, PostDomBB);
}
// Build the `IfNode`.
using UniqueExpr = ASTTree::expr_unique_ptr;
using ExprDestruct = ASTTree::expr_destructor;
auto *OriginalNode = Node->getOriginalNode();
UniqueExpr CondExpr(new AtomicNode(OriginalNode), ExprDestruct());
ExprNode *Condition = AST.addCondExpr(std::move(CondExpr));
ASTObject.reset(new IfNode(Node, Condition, Then, Else, PostDom));
if (PostDomBB) {
createTile(Region, ASTDT, TileToNodeMap, Node, PostDomBB, true);
} else {
createTile(Region, ASTDT, TileToNodeMap, Node, PostDomBB, false);
}
} break;
default: {
revng_log(CombLogger,
"Node: " << Node->getNameStr() << " dominates "
<< Children.size() << " nodes");
revng_unreachable("Node directly dominates more than 3 other "
"nodes");
} break;
}
} break;
case 1: {
switch (Children.size()) {
case 0: {
// In this situation, we don't need to actually add as a successor
// of the current node the single successor which is not dominated.
// Therefore, the successor will not be a successor on the AST.
revng_assert(not Node->isBreak() and not Node->isContinue());
if (Node->isSet()) {
ASTObject.reset(new SetNode(Node));
} else {
ASTObject.reset(new CodeNode(Node, nullptr));
}
} break;
case 1: {
// In this situation, we dominate the only successor of the current
// node. The successor therefore will be an actual successor on the
// AST.
revng_assert(not Node->isBreak() and not Node->isContinue());
revng_assert(Successors[0] == Children[0]);
auto *Succ = findASTNode(AST, TileToNodeMap, Children[0]);
if (Node->isSet()) {
ASTObject.reset(new SetNode(Node, Succ));
} else {
ASTObject.reset(new CodeNode(Node, Succ));
}
createTile(Region, ASTDT, TileToNodeMap, Node, Children[0], true);
} break;
default: {
revng_log(CombLogger,
"Node: " << Node->getNameStr() << " dominates "
<< Children.size() << "nodes");
revng_unreachable("Node with 1 successor dominates an incorrect "
"number of nodes");
} break;
}
} break;
case 0: {
if (Node->isBreak())
ASTObject.reset(new BreakNode(Node));
else if (Node->isContinue())
ASTObject.reset(new ContinueNode(Node));
else if (Node->isSet())
ASTObject.reset(new SetNode(Node));
else if (Node->isEmpty() or Node->isCode())
ASTObject.reset(new CodeNode(Node, nullptr));
else
revng_abort();
} break;
default: {
revng_log(CombLogger,
"Node: " << Node->getNameStr() << " dominates "
<< Children.size() << " nodes");
revng_unreachable("Node directly dominates more than 3 other nodes");
} break;
}
}
AST.addASTNode(Node, std::move(ASTObject));
}
// Set in the ASTTree object the root node.
BasicBlockNode<NodeT> *Root = ASTDT.getRootNode()->getBlock();
revng_assert(Root);
ASTNode *RootNode = AST.findASTNode(Root);
AST.setRoot(RootNode);
// We return true to notify that no `generateAST` failure arose
return true;
}
inline void normalize(ASTTree &AST, const llvm::Function &F) {
// AST dumper helper
GHASTDumper Dumper(CombLogger, F, AST, "normalize");
// Serialize the graph starting from the root node.
CombLogger << "Serializing first AST draft:\n";
Dumper.log("first-draft");
// Create sequence nodes.
CombLogger << "Performing sequence insertion:\n";
ASTNode *RootNode = AST.getRoot();
RootNode = createSequence(AST, RootNode);
AST.setRoot(RootNode);
Dumper.log("after-sequence");
// Simplify useless sequence nodes.
CombLogger << "Performing useless dummies simplification:\n";
simplifyDummies(AST, RootNode);
Dumper.log("after-dummies-removal");
// Simplify useless sequence nodes.
CombLogger << "Performing useless sequence simplification:\n";
RootNode = simplifyAtomicSequence(AST, RootNode);
AST.setRoot(RootNode);
Dumper.log("after-sequence-simplification");
Dumper.log("final");
}