Files
revng-revng/lib/Decompiler/CDecompilerBeautify.cpp
T
Andrea Gussoni 8eb5abaaeb Fix emission computation for continue nodes
Added a new attribute for the `ContinueNode`, which tells if a
`ContinueNode` should be considered implicit. An implicit continue node
means that it can be dropped without it altering the semantics of the
code (e.g., when the `continue` is the last statement inside the body of
a cycle).

This attribute enables us to avoid directly dropping the continue node,
which would also cause the computation attached to this node to be
dropped, also in the output of the decompiler pass.
2019-07-30 18:20:06 +02:00

938 lines
32 KiB
C++

/// \file CDecompilerBeautify.cpp
/// \brief Bautify passes on the final AST
///
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
// LLVM includes
#include "llvm/Support/Casting.h"
#include <llvm/IR/Instructions.h>
// revng includes
#include "revng/Support/Debug.h"
#include <revng/Support/Assert.h>
//#include "revng/Support/MonotoneFramework.h"
// local libraries includes
#include "revng-c/RestructureCFGPass/ASTTree.h"
#include "revng-c/RestructureCFGPass/ExprNode.h"
#include "revng-c/RestructureCFGPass/RegionCFGTree.h"
// local includes
#include "CDecompilerBeautify.h"
#include "MarkForSerialization.h"
Logger<> BeautifyLogger("beautify");
using namespace llvm;
using std::make_unique;
using std::unique_ptr;
static void flipEmptyThen(ASTNode *RootNode, ASTTree &AST) {
if (auto *Sequence = llvm::dyn_cast<SequenceNode>(RootNode)) {
for (ASTNode *Node : Sequence->nodes()) {
flipEmptyThen(Node, AST);
}
} else if (auto *If = llvm::dyn_cast<IfNode>(RootNode)) {
if (!If->hasThen()) {
if (BeautifyLogger.isEnabled()) {
BeautifyLogger << "Flipping then and else branches for : ";
BeautifyLogger << If->getName() << "\n";
}
If->setThen(If->getElse());
If->setElse(nullptr);
// Invert the conditional expression of the current `IfNode`.
auto Not = std::make_unique<NotNode>(If->getCondExpr());
ExprNode *NotNode = AST.addCondExpr(std::move(Not));
If->replaceCondExpr(NotNode);
flipEmptyThen(If->getThen(), AST);
} else {
// We are sure to have the `then` branch since the previous check did
// not verify
flipEmptyThen(If->getThen(), AST);
// We have not the same assurance for the `else` branch
if (If->hasElse()) {
flipEmptyThen(If->getElse(), AST);
}
}
} else if (auto *Scs = llvm::dyn_cast<ScsNode>(RootNode)) {
flipEmptyThen(Scs->getBody(), AST);
}
}
using Marker = MarkForSerialization::Analysis;
static bool requiresNoStatement(IfNode *If, Marker &Mark) {
// Compute how many statement we need to serialize for the basicblock
// associated with the internal `IfNode`.
ExprNode *ExprBB = If->getCondExpr();
if (auto *Atomic = llvm::dyn_cast<AtomicNode>(ExprBB)) {
llvm::BasicBlock *BB = Atomic->getConditionalBasicBlock();
if (Mark.getToSerialize(BB).size() == 0) {
return true;
}
}
return false;
}
// Helper function to simplify short-circuit IFs
static void
simplifyShortCircuit(ASTNode *RootNode, ASTTree &AST, Marker &Mark) {
if (auto *Sequence = llvm::dyn_cast<SequenceNode>(RootNode)) {
for (ASTNode *Node : Sequence->nodes()) {
simplifyShortCircuit(Node, AST, Mark);
}
} else if (auto *Scs = llvm::dyn_cast<ScsNode>(RootNode)) {
simplifyShortCircuit(Scs->getBody(), AST, Mark);
} else if (auto *Switch = llvm::dyn_cast<SwitchNode>(RootNode)) {
for (auto &Case : Switch->unordered_cases())
simplifyShortCircuit(Case, AST, Mark);
if (ASTNode *Default = Switch->getDefault())
simplifyShortCircuit(Default, AST, Mark);
} else if (auto *If = llvm::dyn_cast<IfNode>(RootNode)) {
if (If->hasBothBranches()) {
if (auto NestedIf = llvm::dyn_cast<IfNode>(If->getThen())) {
// TODO: Refactor this with some kind of iterator
if (NestedIf->getThen() != nullptr) {
if (If->getElse()->isEqual(NestedIf->getThen())
and requiresNoStatement(NestedIf, Mark)) {
if (BeautifyLogger.isEnabled()) {
BeautifyLogger << "Candidate for short-circuit reduction found:";
BeautifyLogger << "\n";
BeautifyLogger << "IF " << If->getName() << " and ";
BeautifyLogger << "IF " << NestedIf->getName() << "\n";
BeautifyLogger << "Nodes being simplified:\n";
BeautifyLogger << If->getElse()->getName() << " and ";
BeautifyLogger << NestedIf->getThen()->getName() << "\n";
}
If->setThen(NestedIf->getElse());
If->setElse(NestedIf->getThen());
// `if A and not B` situation.
auto NotB = std::make_unique<NotNode>(NestedIf->getCondExpr());
ExprNode *NotBNode = AST.addCondExpr(std::move(NotB));
auto AAndNotB = std::make_unique<AndNode>(If->getCondExpr(),
NotBNode);
ExprNode *AAndNotBNode = AST.addCondExpr(std::move(AAndNotB));
If->replaceCondExpr(AAndNotBNode);
// Recursive call.
simplifyShortCircuit(If, AST, Mark);
}
}
if (NestedIf->getElse() != nullptr) {
if (If->getElse()->isEqual(NestedIf->getElse())
and requiresNoStatement(NestedIf, Mark)) {
if (BeautifyLogger.isEnabled()) {
BeautifyLogger << "Candidate for short-circuit reduction found:";
BeautifyLogger << "\n";
BeautifyLogger << "IF " << If->getName() << " and ";
BeautifyLogger << "IF " << NestedIf->getName() << "\n";
BeautifyLogger << "Nodes being simplified:\n";
BeautifyLogger << If->getElse()->getName() << " and ";
BeautifyLogger << NestedIf->getElse()->getName() << "\n";
}
If->setThen(NestedIf->getThen());
If->setElse(NestedIf->getElse());
// `if A and B` situation.
auto AAndB = std::make_unique<AndNode>(If->getCondExpr(),
NestedIf->getCondExpr());
ExprNode *AAndBNode = AST.addCondExpr(std::move(AAndB));
If->replaceCondExpr(AAndBNode);
simplifyShortCircuit(If, AST, Mark);
}
}
}
if (auto NestedIf = llvm::dyn_cast<IfNode>(If->getElse())) {
// TODO: Refactor this with some kind of iterator
if (NestedIf->getThen() != nullptr) {
if (If->getThen()->isEqual(NestedIf->getThen())
and requiresNoStatement(NestedIf, Mark)) {
if (BeautifyLogger.isEnabled()) {
BeautifyLogger << "Candidate for short-circuit reduction found:";
BeautifyLogger << "\n";
BeautifyLogger << "IF " << If->getName() << " and ";
BeautifyLogger << "IF " << NestedIf->getName() << "\n";
BeautifyLogger << "Nodes being simplified:\n";
BeautifyLogger << If->getThen()->getName() << " and ";
BeautifyLogger << NestedIf->getThen()->getName() << "\n";
}
If->setElse(NestedIf->getElse());
If->setThen(NestedIf->getThen());
// `if not A and not B` situation.
auto NotA = std::make_unique<NotNode>(If->getCondExpr());
ExprNode *NotANode = AST.addCondExpr(std::move(NotA));
auto NotB = std::make_unique<NotNode>(NestedIf->getCondExpr());
ExprNode *NotBNode = AST.addCondExpr(std::move(NotB));
auto NotAAndNotB = std::make_unique<AndNode>(NotANode, NotBNode);
ExprNode *NotAAndNotBNode = AST.addCondExpr(std::move(NotAAndNotB));
simplifyShortCircuit(If, AST, Mark);
}
}
if (NestedIf->getElse() != nullptr) {
if (If->getThen()->isEqual(NestedIf->getElse())
and requiresNoStatement(NestedIf, Mark)) {
if (BeautifyLogger.isEnabled()) {
BeautifyLogger << "Candidate for short-circuit reduction found:";
BeautifyLogger << "\n";
BeautifyLogger << "IF " << If->getName() << " and ";
BeautifyLogger << "IF " << NestedIf->getName() << "\n";
BeautifyLogger << "Nodes being simplified:\n";
BeautifyLogger << If->getThen()->getName() << " and ";
BeautifyLogger << NestedIf->getElse()->getName() << "\n";
}
If->setElse(NestedIf->getThen());
If->setThen(NestedIf->getElse());
// `if not A and B` situation.
auto NotA = std::make_unique<NotNode>(If->getCondExpr());
ExprNode *NotANode = AST.addCondExpr(std::move(NotA));
auto NotAAndB = std::make_unique<AndNode>(NotANode,
NestedIf->getCondExpr());
ExprNode *NotAAndBNode = AST.addCondExpr(std::move(NotAAndB));
simplifyShortCircuit(If, AST, Mark);
}
}
}
}
}
}
static void
simplifyTrivialShortCircuit(ASTNode *RootNode, ASTTree &AST, Marker &Mark) {
if (auto *Sequence = llvm::dyn_cast<SequenceNode>(RootNode)) {
for (ASTNode *Node : Sequence->nodes()) {
simplifyTrivialShortCircuit(Node, AST, Mark);
}
} else if (auto *Scs = llvm::dyn_cast<ScsNode>(RootNode)) {
simplifyTrivialShortCircuit(Scs->getBody(), AST, Mark);
} else if (auto *Switch = llvm::dyn_cast<SwitchNode>(RootNode)) {
for (auto &Case : Switch->unordered_cases())
simplifyTrivialShortCircuit(Case, AST, Mark);
if (ASTNode *Default = Switch->getDefault())
simplifyTrivialShortCircuit(Default, AST, Mark);
} else if (auto *If = llvm::dyn_cast<IfNode>(RootNode)) {
if (!If->hasElse()) {
if (auto *InternalIf = llvm::dyn_cast<IfNode>(If->getThen())) {
if (!InternalIf->hasElse() and requiresNoStatement(InternalIf, Mark)) {
if (BeautifyLogger.isEnabled()) {
BeautifyLogger << "Candidate for trivial short-circuit reduction";
BeautifyLogger << "found:\n";
BeautifyLogger << "IF " << If->getName() << " and ";
BeautifyLogger << "If " << InternalIf->getName() << "\n";
BeautifyLogger << "Nodes being simplified:\n";
BeautifyLogger << If->getThen()->getName() << " and ";
BeautifyLogger << InternalIf->getThen()->getName() << "\n";
}
If->setThen(InternalIf->getThen());
// `if A and B` situation.
auto AAndB = std::make_unique<AndNode>(If->getCondExpr(),
InternalIf->getCondExpr());
ExprNode *AAndBNode = AST.addCondExpr(std::move(AAndB));
If->replaceCondExpr(AAndBNode);
simplifyTrivialShortCircuit(RootNode, AST, Mark);
}
}
}
if (If->hasThen())
simplifyTrivialShortCircuit(If->getThen(), AST, Mark);
if (If->hasElse())
simplifyTrivialShortCircuit(If->getElse(), AST, Mark);
}
}
static ConstantInt *getCaseConstant(ASTNode *Node) {
llvm::BasicBlock *BB = Node->getOriginalBB();
revng_assert(BB->size() == 2);
llvm::Instruction &CompareInst = BB->front();
llvm::Instruction &BranchInst = BB->back();
revng_assert(llvm::isa<llvm::ICmpInst>(CompareInst));
revng_assert(llvm::isa<llvm::BranchInst>(BranchInst));
revng_assert(llvm::cast<llvm::BranchInst>(BranchInst).isConditional());
auto *Compare = llvm::cast<llvm::ICmpInst>(&CompareInst);
revng_assert(Compare->getNumOperands() == 2);
llvm::ConstantInt *CI = llvm::cast<llvm::ConstantInt>(Compare->getOperand(1));
return CI;
}
static llvm::Value *getCaseValue(ASTNode *Node) {
llvm::BasicBlock *BB = Node->getOriginalBB();
revng_assert(BB->size() == 2);
llvm::Instruction &CompareInst = BB->front();
llvm::Instruction &BranchInst = BB->back();
revng_assert(llvm::isa<llvm::ICmpInst>(CompareInst));
revng_assert(llvm::isa<llvm::BranchInst>(BranchInst));
revng_assert(llvm::cast<llvm::BranchInst>(BranchInst).isConditional());
auto *Compare = llvm::cast<llvm::ICmpInst>(&CompareInst);
revng_assert(Compare->getNumOperands() == 2);
return Compare->getOperand(0);
}
static bool wasOriginalSwitch(ASTNode *Candidate) {
llvm::BasicBlock *BB = Candidate->getOriginalBB();
// Check that the if corresponds to an original basic block.
if (BB != nullptr) {
// Check that the body contains an `icmp` instruction over the condition
// of the switch and a constant, and then a conditional branch.
if (BB->size() == 2 and BB->getName().startswith("switch check")) {
auto &InstList = BB->getInstList();
llvm::Instruction &First = BB->front();
llvm::Instruction &Second = BB->back();
if (llvm::isa<llvm::ICmpInst>(First)) {
if (auto *Branch = llvm::dyn_cast<llvm::BranchInst>(&Second)) {
if (Branch->isConditional()) {
return true;
}
}
}
}
}
return false;
}
static ASTNode *matchSwitch(ASTTree &AST, ASTNode *RootNode, Marker &Mark) {
// Inspect all the nodes composing a sequence node.
if (auto *Sequence = llvm::dyn_cast<SequenceNode>(RootNode)) {
for (ASTNode *&Node : Sequence->nodes()) {
Node = matchSwitch(AST, Node, Mark);
}
} else if (auto *Scs = llvm::dyn_cast<ScsNode>(RootNode)) {
// Inspect the body of a SCS region.
Scs->setBody(matchSwitch(AST, Scs->getBody(), Mark));
} else if (auto *If = llvm::dyn_cast<IfNode>(RootNode)) {
// Switch matching routine.
if (wasOriginalSwitch(If)) {
// Get the Value of the condition.
llvm::Value *SwitchValue = getCaseValue(If);
// An IfEqualNode represents the start of a switch statement.
llvm::SmallVector<IfNode *, 8> Candidates;
Candidates.push_back(If);
// Continue to accumulate the IfEqual nodes until it is possible.
while (If->getElse() and wasOriginalSwitch(If->getElse())
and (SwitchValue == getCaseValue(If->getElse()))) {
If = llvm::cast<IfNode>(If->getElse());
Candidates.push_back(If);
}
RegularSwitchNode::case_container Cases;
RegularSwitchNode::case_value_container CaseValues;
for (IfNode *Candidate : Candidates) {
Cases.push_back(Candidate->getThen());
CaseValues.push_back(getCaseConstant(Candidate));
}
// Collect the last else (which will become the default case).
llvm::Type *SwitchType = SwitchValue->getType();
auto *Zero = cast<ConstantInt>(llvm::Constant::getNullValue(SwitchType));
ASTNode *DefaultCase = Candidates.back()->getElse();
if (DefaultCase == nullptr)
DefaultCase = AST.addSequenceNode();
// Create the switch node.
auto Switch = std::make_unique<RegularSwitchNode>(SwitchValue,
Cases,
CaseValues,
DefaultCase);
// Invoke the switch matching on the switch just reconstructed.
matchSwitch(AST, Switch.get(), Mark);
// Return the new object.
return AST.addSwitch(std::move(Switch));
} else {
// Analyze a standard IfNode.
if (If->hasThen()) {
If->setThen(matchSwitch(AST, If->getThen(), Mark));
}
if (If->hasElse()) {
If->setElse(matchSwitch(AST, If->getElse(), Mark));
}
}
} else if (auto *Switch = llvm::dyn_cast<SwitchNode>(RootNode)) {
for (auto &Case : Switch->unordered_cases())
Case = matchSwitch(AST, Case, Mark);
if (ASTNode *Default = Switch->getDefault())
Default = matchSwitch(AST, Default, Mark);
}
return RootNode;
}
static ASTNode *matchDispatcher(ASTTree &AST, ASTNode *RootNode, Marker &Mark) {
// Inspect all the nodes composing a sequence node.
if (auto *Sequence = llvm::dyn_cast<SequenceNode>(RootNode)) {
for (ASTNode *&Node : Sequence->nodes()) {
Node = matchDispatcher(AST, Node, Mark);
}
} else if (auto *Scs = llvm::dyn_cast<ScsNode>(RootNode)) {
// Inspect the body of a SCS region.
Scs->setBody(matchDispatcher(AST, Scs->getBody(), Mark));
} else if (auto *IfCheck = llvm::dyn_cast<IfCheckNode>(RootNode)) {
// An `IfCheckNode` represents the start of a dispatcher, which we want to
// represent using a `SwitchCheckNode`.
std::vector<IfCheckNode *> Candidates;
Candidates.push_back(IfCheck);
// Continue to accumulate the `IfCheck` nodes until it is possible.
while (auto *SuccChk = dyn_cast_or_null<IfCheckNode>(IfCheck->getElse())) {
Candidates.push_back(SuccChk);
IfCheck = SuccChk;
}
SwitchCheckNode::case_container Cases;
SwitchCheckNode::case_value_container CaseValues;
for (IfCheckNode *Candidate : Candidates) {
Cases.push_back(Candidate->getThen());
CaseValues.push_back(Candidate->getCaseValue());
}
// Collect the last else which always corresponds to case 0
unsigned Zero = 0;
if (ASTNode *DefaultCase = Candidates.back()->getElse()) {
Cases.push_back(DefaultCase);
CaseValues.push_back(Zero);
}
// Create the `SwitchCheckNode`.
unique_ptr<SwitchCheckNode> Switch(new SwitchCheckNode(Cases, CaseValues));
// Invoke the dispatcher matching on the switch just reconstructed.
matchDispatcher(AST, Switch.get(), Mark);
// Return the new object.
return AST.addSwitchCheck(std::move(Switch));
} else if (auto *If = llvm::dyn_cast<IfNode>(RootNode)) {
// Analyze a standard `IfNode`.
if (If->hasThen()) {
If->setThen(matchDispatcher(AST, If->getThen(), Mark));
}
if (If->hasElse()) {
If->setElse(matchDispatcher(AST, If->getElse(), Mark));
}
} else if (auto *Switch = llvm::dyn_cast<SwitchNode>(RootNode)) {
for (auto &Case : Switch->unordered_cases())
Case = matchDispatcher(AST, Case, Mark);
if (ASTNode *Default = Switch->getDefault())
Default = matchDispatcher(AST, Default, Mark);
}
return RootNode;
}
static ASTNode *getLastOfSequenceOrSelf(ASTNode *RootNode) {
ASTNode *LastNode = nullptr;
if (auto *Sequence = llvm::dyn_cast<SequenceNode>(RootNode)) {
int SequenceSize = Sequence->listSize();
LastNode = Sequence->getNodeN(SequenceSize - 1);
} else {
LastNode = RootNode;
}
revng_assert(LastNode != nullptr);
return LastNode;
}
// HACK: districate this mutual calling tree.
static void simplifyLastContinue(ASTNode *RootNode, ASTTree &AST);
static void removeLastContinue(ASTNode *RootNode, ASTTree &AST) {
if (auto *LastIf = llvm::dyn_cast<IfNode>(RootNode)) {
// TODO: unify this
// Handle then
if (LastIf->hasThen()) {
ASTNode *Then = LastIf->getThen();
if (ContinueNode *ThenContinue = llvm::dyn_cast<ContinueNode>(Then)) {
if (ThenContinue->hasComputation()) {
ThenContinue->setImplicit();
} else {
LastIf->setThen(LastIf->getElse());
LastIf->setElse(nullptr);
// Manual flip when removing then.
// TODO: handle this in the flipIfEmpty phase.
// Invert the conditional expression of the current `IfNode`.
auto Not = std::make_unique<NotNode>(LastIf->getCondExpr());
ExprNode *NotNode = AST.addCondExpr(std::move(Not));
LastIf->replaceCondExpr(NotNode);
}
} else {
removeLastContinue(Then, AST);
}
}
// Handle else
if (LastIf->hasElse()) {
ASTNode *Else = LastIf->getElse();
if (ContinueNode *ElseContinue = llvm::dyn_cast<ContinueNode>(Else)) {
if (ElseContinue->hasComputation()) {
ElseContinue->setImplicit();
} else {
LastIf->setElse(nullptr);
}
} else {
removeLastContinue(Else, AST);
}
}
} else if (auto *Sequence = llvm::dyn_cast<SequenceNode>(RootNode)) {
ASTNode *LastNode = getLastOfSequenceOrSelf(Sequence);
if (ContinueNode *LastContinue = llvm::dyn_cast<ContinueNode>(LastNode)) {
LastContinue->setImplicit();
} else {
removeLastContinue(LastNode, AST);
}
} else if (auto *Scs = llvm::dyn_cast<ScsNode>(RootNode)) {
// Body could be nullptr (previous while/dowhile semplification)
if (Scs->getBody() == nullptr) {
return;
}
simplifyLastContinue(Scs->getBody(), AST);
}
}
static void simplifyLastContinue(ASTNode *RootNode, ASTTree &AST) {
if (auto *Sequence = llvm::dyn_cast<SequenceNode>(RootNode)) {
for (ASTNode *Node : Sequence->nodes()) {
simplifyLastContinue(Node, AST);
}
removeLastContinue(Sequence, AST);
} else if (auto *If = llvm::dyn_cast<IfNode>(RootNode)) {
if (If->hasThen()) {
simplifyLastContinue(If->getThen(), AST);
}
if (If->hasElse()) {
simplifyLastContinue(If->getElse(), AST);
}
} else if (auto *Scs = llvm::dyn_cast<ScsNode>(RootNode)) {
// Body could be nullptr (previous while/dowhile semplification)
if (Scs->getBody() == nullptr) {
return;
}
// Recursive invocation on the body
simplifyLastContinue(Scs->getBody(), AST);
ASTNode *LastNode = getLastOfSequenceOrSelf(Scs->getBody());
removeLastContinue(LastNode, AST);
}
}
static void matchDoWhile(ASTNode *RootNode, ASTTree &AST) {
BeautifyLogger << "Matching do whiles"
<< "\n";
if (auto *Sequence = llvm::dyn_cast<SequenceNode>(RootNode)) {
for (ASTNode *Node : Sequence->nodes()) {
matchDoWhile(Node, AST);
}
} else if (auto *If = llvm::dyn_cast<IfNode>(RootNode)) {
if (If->hasThen()) {
matchDoWhile(If->getThen(), AST);
}
if (If->hasElse()) {
matchDoWhile(If->getElse(), AST);
}
} else if (auto *Switch = llvm::dyn_cast<SwitchNode>(RootNode)) {
for (auto &Case : Switch->unordered_cases())
matchDoWhile(Case, AST);
if (ASTNode *Default = Switch->getDefault())
matchDoWhile(Default, AST);
} else if (auto *Scs = llvm::dyn_cast<ScsNode>(RootNode)) {
ASTNode *Body = Scs->getBody();
// Recursive scs nesting handling
matchDoWhile(Body, AST);
// ASTNode *LastNode = getLastOfSequenceOrSelf(Scs->getBody());
ASTNode *LastNode = nullptr;
bool InsideSequence = false;
if (auto *Sequence = llvm::dyn_cast<SequenceNode>(Body)) {
int SequenceSize = Sequence->listSize();
LastNode = Sequence->getNodeN(SequenceSize - 1);
InsideSequence = true;
} else {
LastNode = Body;
}
revng_assert(LastNode != nullptr);
if (auto *If = llvm::dyn_cast<IfNode>(LastNode)) {
// Only if nodes with both branches are candidates.
if (If->hasBothBranches()) {
ASTNode *Then = If->getThen();
ASTNode *Else = If->getElse();
if (llvm::isa<BreakNode>(Then) and llvm::isa<ContinueNode>(Else)) {
Scs->setDoWhile(If);
// Invert the conditional expression of the current `IfNode`.
auto Not = std::make_unique<NotNode>(If->getCondExpr());
ExprNode *NotNode = AST.addCondExpr(std::move(Not));
If->replaceCondExpr(NotNode);
// Remove the if node
if (InsideSequence) {
cast<SequenceNode>(Body)->removeNode(If);
} else {
Scs->setBody(nullptr);
}
} else if (llvm::isa<BreakNode>(Else)
and llvm::isa<ContinueNode>(Then)) {
Scs->setDoWhile(If);
// Remove the if node
if (InsideSequence) {
cast<SequenceNode>(Body)->removeNode(If);
} else {
Scs->setBody(nullptr);
}
}
}
}
} else {
BeautifyLogger << "No matching done\n";
}
}
static void addComputationToContinue(ASTNode *RootNode, IfNode *ConditionIf) {
BeautifyLogger << "Adding computation code to continue node"
<< "\n";
if (auto *Sequence = llvm::dyn_cast<SequenceNode>(RootNode)) {
for (ASTNode *Node : Sequence->nodes()) {
addComputationToContinue(Node, ConditionIf);
}
} else if (auto *If = llvm::dyn_cast<IfNode>(RootNode)) {
if (If->hasThen()) {
addComputationToContinue(If->getThen(), ConditionIf);
}
if (If->hasElse()) {
addComputationToContinue(If->getElse(), ConditionIf);
}
} else if (auto *Switch = llvm::dyn_cast<SwitchNode>(RootNode)) {
for (auto &Case : Switch->unordered_cases())
addComputationToContinue(Case, ConditionIf);
if (ASTNode *Default = Switch->getDefault())
addComputationToContinue(Default, ConditionIf);
} else if (auto *Continue = llvm::dyn_cast<ContinueNode>(RootNode)) {
Continue->addComputationIfNode(ConditionIf);
}
}
static void matchWhile(ASTNode *RootNode, ASTTree &AST) {
if (auto *Sequence = llvm::dyn_cast<SequenceNode>(RootNode)) {
for (ASTNode *Node : Sequence->nodes()) {
matchWhile(Node, AST);
}
} else if (auto *If = llvm::dyn_cast<IfNode>(RootNode)) {
if (If->hasThen()) {
matchWhile(If->getThen(), AST);
}
if (If->hasElse()) {
matchWhile(If->getElse(), AST);
}
} else if (auto *Switch = llvm::dyn_cast<SwitchNode>(RootNode)) {
for (auto &Case : Switch->unordered_cases())
matchWhile(Case, AST);
if (ASTNode *Default = Switch->getDefault())
matchWhile(Default, AST);
} else if (auto *Scs = llvm::dyn_cast<ScsNode>(RootNode)) {
ASTNode *Body = Scs->getBody();
// Body could be nullptr (previous while/dowhile semplification)
if (Body == nullptr) {
return;
}
// Recursive scs nesting handling
matchWhile(Body, AST);
// ASTNode *FirstNode = getLastOfSequenceOrSelf(Scs->getBody());
ASTNode *FirstNode = nullptr;
bool InsideSequence = false;
if (auto *Sequence = llvm::dyn_cast<SequenceNode>(Body)) {
FirstNode = Sequence->getNodeN(0);
InsideSequence = true;
} else {
FirstNode = Body;
}
revng_assert(FirstNode != nullptr);
if (auto *If = llvm::dyn_cast<IfNode>(FirstNode)) {
// Only if nodes with both branches are candidates.
if (If->hasBothBranches()) {
ASTNode *Then = If->getThen();
ASTNode *Else = If->getElse();
if (llvm::isa<BreakNode>(Then)) {
Scs->setWhile(If);
// Invert the conditional expression of the current `IfNode`.
auto Not = std::make_unique<NotNode>(If->getCondExpr());
ExprNode *NotNode = AST.addCondExpr(std::move(Not));
If->replaceCondExpr(NotNode);
// Remove the if node
if (InsideSequence) {
cast<SequenceNode>(Body)->removeNode(If);
} else {
Scs->setBody(If->getElse());
// Add computation before the continue nodes
addComputationToContinue(Scs->getBody(), If);
}
} else if (llvm::isa<BreakNode>(Else)) {
Scs->setWhile(If);
// Remove the if node
if (InsideSequence) {
cast<SequenceNode>(Body)->removeNode(If);
} else {
Scs->setBody(If->getThen());
// Add computation before the continue nodes
addComputationToContinue(Scs->getBody(), If);
}
}
}
}
} else {
BeautifyLogger << "No matching done\n";
}
}
class SwitchBreaksFixer {
protected:
using SwitchStackT = llvm::SmallVector<SwitchNode *, 2>;
using LoopStackEntryT = std::pair<ScsNode *, SwitchStackT>;
using LoopStackT = llvm::SmallVector<LoopStackEntryT, 8>;
public:
SwitchBreaksFixer() = default;
~SwitchBreaksFixer() = default;
void run(ASTNode *RootNode, ASTTree &AST) {
LoopStack.clear();
exec(RootNode, AST);
}
protected:
void exec(ASTNode *Node, ASTTree &AST) {
if (Node == nullptr)
return;
switch (Node->getKind()) {
case ASTNode::NK_If: {
IfNode *If = llvm::cast<IfNode>(Node);
exec(If->getThen(), AST);
exec(If->getElse(), AST);
} break;
case ASTNode::NK_Scs: {
ScsNode *Loop = llvm::cast<ScsNode>(Node);
LoopStack.push_back({ Loop, {} });
exec(Loop->getBody(), AST);
revng_assert(LoopStack.back().second.empty());
LoopStack.pop_back();
} break;
case ASTNode::NK_List: {
SequenceNode *Seq = llvm::cast<SequenceNode>(Node);
for (ASTNode *N : Seq->nodes())
exec(N, AST);
} break;
case ASTNode::NK_SwitchCheck:
case ASTNode::NK_SwitchRegular: {
SwitchNode *Switch = llvm::cast<SwitchNode>(Node);
if (not LoopStack.empty())
LoopStack.back().second.push_back(Switch);
for (ASTNode *Case : Switch->unordered_cases())
exec(Case, AST);
if (ASTNode *Default = Switch->getDefault())
exec(Default, AST);
if (not LoopStack.empty())
LoopStack.back().second.pop_back();
} break;
case ASTNode::NK_Break: {
revng_assert(not LoopStack.empty()); // assert that we're in a loop
BreakNode *B = llvm::cast<BreakNode>(Node);
SwitchStackT &ActiveSwitches = LoopStack.back().second;
if (not ActiveSwitches.empty()) {
// The outer switch needs a declaration for the state variable necessary
// to break directly out of the loop from within the switches
ActiveSwitches.front()->setNeedsStateVariable(true);
B->setBreakFromWithinSwitch(true);
for (SwitchNode *S : LoopStack.back().second) {
// this loop break is inside one (or possibly more nested) switch(es),
// contained in the loop, hence all the active switches need a
// dispatcher to be inserted right after the switch, to use the state
// variable to dispatch the break out of the loop.
S->setNeedsLoopBreakDispatcher(true);
}
}
} break;
case ASTNode::NK_SwitchBreak:
// assert that we're either not in a loop, or, if we're in a loop we're
// also inside a switch which is nested in the loop
revng_assert(LoopStack.empty() or not LoopStack.back().second.empty());
// fallthrough
case ASTNode::NK_Set:
case ASTNode::NK_Code:
case ASTNode::NK_Continue:
break; // do nothing
case ASTNode::NK_IfCheck:
BeautifyLogger << "Unexpected: IfCheck\n";
default:
revng_unreachable("unexpected node kind");
}
}
protected:
LoopStackT LoopStack{};
};
void beautifyAST(Function &F, ASTTree &CombedAST, Marker &Mark) {
ASTNode *RootNode = CombedAST.getRoot();
// Flip IFs with empty then branches.
revng_log(BeautifyLogger,
"Performing IFs with empty then branches flipping\n");
flipEmptyThen(RootNode, CombedAST);
if (BeautifyLogger.isEnabled()) {
CombedAST.dumpOnFile("ast", F.getName(), "After-if-flip");
}
// Match switch node.
revng_log(BeautifyLogger, "Performing dispatcher nodes matching\n");
RootNode = matchDispatcher(CombedAST, RootNode, Mark);
if (BeautifyLogger.isEnabled()) {
CombedAST.dumpOnFile("ast", F.getName(), "After-dispatcher-match");
}
// Simplify short-circuit nodes.
revng_log(BeautifyLogger, "Performing short-circuit simplification\n");
simplifyShortCircuit(RootNode, CombedAST, Mark);
if (BeautifyLogger.isEnabled()) {
CombedAST.dumpOnFile("ast", F.getName(), "After-short-circuit");
}
// Simplify trivial short-circuit nodes.
revng_log(BeautifyLogger,
"Performing trivial short-circuit simplification\n");
simplifyTrivialShortCircuit(RootNode, CombedAST, Mark);
if (BeautifyLogger.isEnabled()) {
CombedAST.dumpOnFile("ast", F.getName(), "After-trivial-short-circuit");
}
// Match switch node.
revng_log(BeautifyLogger, "Performing switch nodes matching\n");
RootNode = matchSwitch(CombedAST, RootNode, Mark);
if (BeautifyLogger.isEnabled()) {
CombedAST.dumpOnFile("ast", F.getName(), "After-switch-match");
}
// Match dowhile.
revng_log(BeautifyLogger, "Matching do-while\n");
matchDoWhile(RootNode, CombedAST);
if (BeautifyLogger.isEnabled()) {
CombedAST.dumpOnFile("ast", F.getName(), "After-match-do-while");
}
// Match while.
revng_log(BeautifyLogger, "Matching while\n");
matchWhile(RootNode, CombedAST);
if (BeautifyLogger.isEnabled()) {
CombedAST.dumpOnFile("ast", F.getName(), "After-match-while");
}
// Remove useless continues.
revng_log(BeautifyLogger, "Removing useless continue nodes\n");
simplifyLastContinue(RootNode, CombedAST);
if (BeautifyLogger.isEnabled()) {
CombedAST.dumpOnFile("ast", F.getName(), "After-continue-removal");
}
// Fix loop breaks from within switches
revng_log(BeautifyLogger, "Fixing loop breaks inside switches\n");
SwitchBreaksFixer().run(RootNode, CombedAST);
if (BeautifyLogger.isEnabled())
CombedAST.dumpOnFile("ast", F.getName(), "After-fix-switch-breaks");
// Remove empty sequences.
revng_log(BeautifyLogger, "Removing emtpy sequence nodes\n");
simplifyAtomicSequence(RootNode);
if (BeautifyLogger.isEnabled()) {
CombedAST.dumpOnFile("ast", F.getName(), "After-removal-empty-sequences");
}
}