Files
revng-revng/lib/RestructureCFG/SimplifyCompareNode.cpp
T
Andrea Gussoni 733f5008b3 BeautifyGHAST: implement DualSwitch simplify
We introduce a simplification step, which looks for `switch`es that can
be reduced to simpler `if` statements.

Specifically, the logic is the following:
1) When we identify a `switch` statement composed by a single `case` and
   a possible default, we transform it into an `if` with the `case` now
   corresponding to the `then`, and the `default` corresponding to the
   `else`, if present.
2) When we identify a `switch` statement composed by two `case`s, and no
   `default` is present, we can promote it to an `if` with `then` `else`
   branches.

Other key details:
- The promotion happens only if we can identify at least one of the
  `case`s that have a single element in the `case` label. If this is not
  the case, we do not promote one to RHS of the `if` condition.
- A new `CompareNode` class, inheriting from `ExprNode`, is created, in
  order to represent the equality or inequality condition of an `IfNode`
  instance that is the result of the promotion. This `CompareNode` can
  represent for the LHS both an `llvm::Value` or the `loop_state_var`,
  while it embeds the RHS constant which completes the comparison.
- We remove `SwitchBreak` AST nodes that may now appear as children of
  an `if` node promoted from a `switch`.
- We introduce in the `CompareNode` the `weaved` concept. Indeed, if a
  promotion of a weaved `switch` happens, we should avoid the
  serialization of the instructions leading to the computation of the
  condition of the original `switch`, because they have been already
  emitted by the main related dominating `switch`.

We also introduce an additional simplification step, which takes care
of:
- Promoting `!(==)` to `(!=)` and `!(!=)` to `(==)`, if the inner
  equal/not equal is represented via a `CompareNode`.
- Promoting `x == 0` to `!x` and `x != 0` to `x`.

To be able to correctly emit (or not) the instructions computing a
condition of an `IfNode`, we need to add the `EmittBB` flag, an
additional parameter to the `buildGHASTCondition` function, which
controls the emission of the statements of a basic block computing a
condition.

Consequently, the `IfNode` acquires a `IsWeaved` field, which is used to
mirror the property having the same name on `SwitchNode`. Being now
possible a promotion from a dual `SwitchNode` to an `IfNode`, we need to
represent this property on the `IfNode` too.
2023-10-23 18:16:20 +02:00

125 lines
4.1 KiB
C++

/// \file SimplifyCompareNode.cpp
/// Beautification pass to simplify `CompareNode`
///
//
// Copyright rev.ng Labs Srl. See LICENSE.md for details.
//
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/Casting.h"
#include "llvm/Transforms/Utils/Local.h"
#include "revng/ADT/RecursiveCoroutine.h"
#include "revng/Support/Assert.h"
#include "revng-c/RestructureCFG/ASTNode.h"
#include "revng-c/RestructureCFG/ASTTree.h"
#include "revng-c/RestructureCFG/ExprNode.h"
#include "revng-c/Support/FunctionTags.h"
#include "SimplifyCompareNode.h"
using namespace llvm;
RecursiveCoroutine<ASTNode *> simplifyCompareNode(ASTTree &AST, ASTNode *Node) {
switch (Node->getKind()) {
case ASTNode::NK_List: {
SequenceNode *Seq = llvm::cast<SequenceNode>(Node);
// In place of a sequence node, we need just to inspect all the nodes in the
// sequence
for (ASTNode *&N : Seq->nodes()) {
N = rc_recur simplifyCompareNode(AST, N);
}
} break;
case ASTNode::NK_Scs: {
ScsNode *Scs = llvm::cast<ScsNode>(Node);
// Inspect loop nodes
if (Scs->hasBody()) {
ASTNode *Body = Scs->getBody();
ASTNode *NewBody = rc_recur simplifyCompareNode(AST, Body);
Scs->setBody(NewBody);
}
} break;
case ASTNode::NK_If: {
IfNode *If = llvm::cast<IfNode>(Node);
// Inspect the `then` and `else` branches
if (If->hasThen()) {
ASTNode *Then = If->getThen();
ASTNode *NewThen = rc_recur simplifyCompareNode(AST, Then);
If->setThen(NewThen);
}
if (If->hasElse()) {
ASTNode *Else = If->getElse();
ASTNode *NewElse = rc_recur simplifyCompareNode(AST, Else);
If->setElse(NewElse);
}
// If the associated `CondExpr` contains a `NotNode`, which in turn contains
// a `CompareNode` containing an `Equal` expression, we can remove the
// `NotNode` altogether and transform the `CompareNode` into a `not equal`.
// Same applies for a `NotNode` containing a `NotEqual` `CompareNode`.
ExprNode *IfCondExpr = If->getCondExpr();
if (auto *Not = llvm::dyn_cast<NotNode>(IfCondExpr)) {
ExprNode *NegatedExpr = Not->getNegatedNode();
revng_assert(NegatedExpr);
if (auto *Compare = llvm::dyn_cast<CompareNode>(NegatedExpr)) {
Compare->flipComparison();
If->replaceCondExpr(Compare);
}
}
// Further simplification for special `CompareNode`s comparing with constant
// `0`. Specifically:
// - If the associated CondExpr` contains a `CompareNode`, which is `LHS ==
// 0`, we convert it to a `NotNode` containing a `CompareNode` of the
// `NotPresent` kind
// - Equally, a `CompareNode`, which is `LHS != 0`, we convert it to a
// `CompareNode` of the `NotPresent` kind.
IfCondExpr = If->getCondExpr();
if (auto *Compare = llvm::dyn_cast<CompareNode>(IfCondExpr)) {
if (Compare->getConstant() == 0) {
using ComparisonKind = CompareNode::ComparisonKind;
auto Comparison = Compare->getComparison();
if (Comparison == ComparisonKind::Comparison_Equal) {
Compare->setNotPresentKind();
using UniqueExpr = ASTTree::expr_unique_ptr;
UniqueExpr Not;
Not.reset(new NotNode(Compare));
ExprNode *NotNode = AST.addCondExpr(std::move(Not));
If->replaceCondExpr(NotNode);
} else if (Comparison == ComparisonKind::Comparison_NotEqual) {
Compare->setNotPresentKind();
}
}
}
} break;
case ASTNode::NK_Switch: {
auto *Switch = llvm::cast<SwitchNode>(Node);
// First of all, we recursively process the `case` nodes contained in the
// `switch` in order to process the inner portion of the AST
for (auto &LabelCasePair : Switch->cases()) {
LabelCasePair.second = rc_recur simplifyCompareNode(AST,
LabelCasePair.second);
}
} break;
case ASTNode::NK_Code:
case ASTNode::NK_Set:
case ASTNode::NK_SwitchBreak:
case ASTNode::NK_Continue:
case ASTNode::NK_Break:
// Do nothing
break;
default:
revng_unreachable();
}
rc_return Node;
}