mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
733f5008b3
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.
125 lines
4.1 KiB
C++
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;
|
|
}
|