Files
revng-revng/lib/Decompiler/CDecompilerAction.cpp
T
Pietro Fezzardi 2ff7044bb9 Decompiler: forward-declare types coming from DLA
This commits enable the emission of rich types associated with function
signatures. This types are forward-declared in the decompiled C code
before the definition of each decompiled function that uses them.

The types we emit for now are the types that the DLA is able to compute
(if any) for the return values and the arguments of the function.

Such types are not yet used in the body of the function, nor in the
function declaration. These are the next steps to come.
2021-02-02 11:23:53 +01:00

861 lines
35 KiB
C++

//
// Copyright rev.ng Srls. See LICENSE.md for details.
//
#include "llvm/IR/Constants.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Stmt.h"
#include "clang/Basic/SourceLocation.h"
#include "revng/Support/Assert.h"
#include "revng-c/RestructureCFGPass/ASTTree.h"
#include "revng-c/RestructureCFGPass/ExprNode.h"
#include "revng-c/RestructureCFGPass/RegionCFGTree.h"
#include "CDecompilerAction.h"
#include "ASTBuildAnalysis.h"
#include "DecompilationHelpers.h"
#include "IRASTTypeTranslation.h"
#include "MarkForSerialization.h"
namespace clang {
namespace tooling {
using PHIIncomingMap = SmallMap<llvm::PHINode *, unsigned, 4>;
static void buildAndAppendSmts(clang::FunctionDecl &FDecl,
SmallVectorImpl<clang::Stmt *> &Stmts,
ASTNode *N,
clang::ASTContext &ASTCtx,
IR2AST::StmtBuilder &ASTBuilder,
MarkForSerialization::Analysis &Mark);
static clang::CompoundStmt *
buildCompoundScope(clang::FunctionDecl &FDecl,
ASTNode *N,
clang::ASTContext &ASTCtx,
IR2AST::StmtBuilder &ASTBuilder,
MarkForSerialization::Analysis &Mark,
SmallVector<clang::Stmt *, 32> AdditionalStmts = {}) {
SmallVector<clang::Stmt *, 32> Stmts;
buildAndAppendSmts(FDecl, Stmts, N, ASTCtx, ASTBuilder, Mark);
// Add additional statement to handle while e dowhile condition computation.
Stmts.append(AdditionalStmts.begin(), AdditionalStmts.end());
return CompoundStmt::Create(ASTCtx, Stmts, {}, {});
}
static clang::Expr *negateExpr(clang::ASTContext &ASTCtx, clang::Expr *E) {
if (auto *BinOp = dyn_cast<clang::BinaryOperator>(E->IgnoreParenImpCasts())) {
auto OpCode = BinOp->getOpcode();
if (clang::BinaryOperator::isComparisonOp(OpCode)) {
// For binary comparison operators, we can just invert the comparison and
// return.
auto NegatedOpCode = clang::BinaryOperator::negateComparisonOp(OpCode);
return new (ASTCtx) clang::BinaryOperator(BinOp->getLHS(),
BinOp->getRHS(),
NegatedOpCode,
BinOp->getLHS()->getType(),
VK_RValue,
OK_Ordinary,
{},
FPOptions());
}
}
if (isa<clang::BinaryOperator>(E) or isa<clang::ConditionalOperator>(E))
E = new (ASTCtx) ParenExpr({}, {}, E);
QualType ExprTy = E->getType();
bool IsBool = ExprTy.getTypePtr()->isBooleanType();
auto OpCode = IsBool ? UnaryOperatorKind::UO_LNot : UnaryOperatorKind::UO_Not;
using Unary = clang::UnaryOperator;
E = new (ASTCtx) Unary(E, OpCode, ExprTy, VK_RValue, OK_Ordinary, {}, false);
return E;
}
static void buildStmtsForBasicBlock(llvm::BasicBlock *BB,
clang::ASTContext &ASTCtx,
SmallVectorImpl<clang::Stmt *> &Stmts,
IR2AST::StmtBuilder &ASTBuilder,
MarkForSerialization::Analysis &Mark) {
revng_assert(BB != nullptr);
auto StmtEnd = ASTBuilder.InstrStmts.end();
auto VDeclEnd = ASTBuilder.VarDecls.end();
auto AdditionalStmtsEnd = ASTBuilder.AdditionalStmts.end();
const std::set<llvm::Instruction *> &Serialized = Mark.getToSerialize(BB);
for (llvm::Instruction &Instr : *BB) {
if (Serialized.count(&Instr) == 0)
continue;
auto StmtIt = ASTBuilder.InstrStmts.find(&Instr);
if (StmtIt != StmtEnd and StmtIt->second != nullptr) {
clang::Stmt *EmittedStmt = nullptr;
auto VarDeclIt = ASTBuilder.VarDecls.find(&Instr);
if (VarDeclIt != VDeclEnd) {
clang::VarDecl *VDecl = VarDeclIt->second;
QualType VarType = VDecl->getType();
clang::Expr *LHS = new (ASTCtx)
DeclRefExpr(ASTCtx, VDecl, false, VarType, VK_LValue, {});
clang::Expr *RHS = cast<clang::Expr>(StmtIt->second);
if (RHS->getType() != VarType) {
if (isa<clang::BinaryOperator>(RHS))
RHS = new (ASTCtx) ParenExpr({}, {}, RHS);
RHS = createCast(VarType, RHS, ASTCtx);
}
EmittedStmt = new (ASTCtx) clang::BinaryOperator(LHS,
RHS,
BO_Assign,
VarType,
VK_RValue,
OK_Ordinary,
{},
FPOptions());
} else {
EmittedStmt = StmtIt->second;
}
Stmts.push_back(EmittedStmt);
}
auto AdditionalStmtsIt = ASTBuilder.AdditionalStmts.find(&Instr);
if (AdditionalStmtsIt != AdditionalStmtsEnd)
for (clang::Stmt *S : AdditionalStmtsIt->second)
Stmts.push_back(S);
}
// Print assignments of PHI variables where needed
auto PHIMapIt = ASTBuilder.BlockToPHIIncoming.find(BB);
if (PHIMapIt != ASTBuilder.BlockToPHIIncoming.end()) {
using Pair = PHIIncomingMap::value_type;
for (Pair &P : PHIMapIt->second) {
llvm::PHINode *ThePHI = P.first;
unsigned IncomingIdx = P.second;
revng_assert(ThePHI != nullptr);
clang::VarDecl *PHIVarDecl = ASTBuilder.VarDecls.at(ThePHI);
QualType VarType = PHIVarDecl->getType();
clang::Expr *LHS = new (ASTCtx)
DeclRefExpr(ASTCtx, PHIVarDecl, false, VarType, VK_LValue, {});
llvm::Value *IncomingV = ThePHI->getIncomingValue(IncomingIdx);
clang::Expr *RHS = ASTBuilder.getExprForValue(IncomingV);
if (RHS->getType() != VarType) {
if (isa<clang::BinaryOperator>(RHS))
RHS = new (ASTCtx) ParenExpr({}, {}, RHS);
RHS = createCast(VarType, RHS, ASTCtx);
}
clang::Stmt *EmittedStmt = nullptr;
EmittedStmt = new (ASTCtx) clang::BinaryOperator(LHS,
RHS,
BO_Assign,
VarType,
VK_RValue,
OK_Ordinary,
{},
FPOptions());
Stmts.push_back(EmittedStmt);
}
}
}
static clang::Expr *createCondExpr(ExprNode *E,
clang::ASTContext &ASTCtx,
SmallVectorImpl<clang::Stmt *> &Stmts,
IR2AST::StmtBuilder &ASTBuilder,
MarkForSerialization::Analysis &Mark) {
struct StackElement {
ExprNode *Node;
llvm::SmallVector<clang::Expr *, 2> ResolvedOperands;
};
llvm::SmallVector<StackElement, 4> VisitStack;
VisitStack.push_back({ nullptr, {} });
VisitStack.push_back({ E, {} });
revng_assert(VisitStack.size() == 2);
while (VisitStack.size() > 1) {
StackElement &Current = VisitStack.back();
switch (Current.Node->getKind()) {
case ExprNode::NodeKind::NK_Atomic: {
AtomicNode *Atomic = cast<AtomicNode>(Current.Node);
llvm::BasicBlock *BB = Atomic->getConditionalBasicBlock();
revng_assert(BB != nullptr);
buildStmtsForBasicBlock(BB, ASTCtx, Stmts, ASTBuilder, Mark);
llvm::Instruction *CondTerminator = BB->getTerminator();
llvm::BranchInst *Br = cast<llvm::BranchInst>(CondTerminator);
revng_assert(Br->isConditional());
llvm::Value *CondValue = Br->getCondition();
clang::Expr *CondExpr = ASTBuilder.getExprForValue(CondValue);
revng_assert(CondExpr);
VisitStack.pop_back();
VisitStack.back().ResolvedOperands.push_back(CondExpr);
} break;
case ExprNode::NodeKind::NK_Not: {
NotNode *N = cast<NotNode>(Current.Node);
revng_assert(Current.ResolvedOperands.size() <= 1);
if (Current.ResolvedOperands.size() != 1) {
ExprNode *Negated = N->getNegatedNode();
VisitStack.push_back({ Negated, {} });
} else {
clang::Expr *NotExpr = negateExpr(ASTCtx, Current.ResolvedOperands[0]);
VisitStack.pop_back();
VisitStack.back().ResolvedOperands.push_back(NotExpr);
}
} break;
case ExprNode::NodeKind::NK_And:
case ExprNode::NodeKind::NK_Or: {
size_t NumOperands = Current.ResolvedOperands.size();
revng_assert(NumOperands <= 2);
using ExprPair = std::pair<ExprNode *, ExprNode *>;
BinaryNode *Binary = cast<BinaryNode>(Current.Node);
if (NumOperands != 2) {
ExprPair Childs = Binary->getInternalNodes();
ExprNode *Op = (NumOperands == 0) ? Childs.first : Childs.second;
VisitStack.push_back({ Op, {} });
} else {
clang::Expr *LHS = Current.ResolvedOperands[0];
clang::Expr *RHS = Current.ResolvedOperands[1];
bool BothBool = LHS->getType().getTypePtr()->isBooleanType()
and RHS->getType().getTypePtr()->isBooleanType();
auto BinOpKind = isa<AndNode>(Binary) ?
(BothBool ? clang::BinaryOperatorKind::BO_LAnd :
clang::BinaryOperatorKind::BO_And) :
(BothBool ? clang::BinaryOperatorKind::BO_LOr :
clang::BinaryOperatorKind::BO_Or);
clang::Expr *BinExpr = new (ASTCtx)
clang::BinaryOperator(LHS,
RHS,
BinOpKind,
LHS->getType(),
VK_RValue,
OK_Ordinary,
{},
FPOptions());
VisitStack.pop_back();
VisitStack.back().ResolvedOperands.push_back(BinExpr);
}
} break;
}
}
revng_assert(VisitStack.size() == 1);
revng_assert(VisitStack.back().ResolvedOperands.size() == 1);
return VisitStack.back().ResolvedOperands[0];
}
static void buildAndAppendSmts(clang::FunctionDecl &FDecl,
SmallVectorImpl<clang::Stmt *> &Stmts,
ASTNode *N,
clang::ASTContext &ASTCtx,
IR2AST::StmtBuilder &ASTBuilder,
MarkForSerialization::Analysis &Mark) {
if (N == nullptr)
return;
auto Kind = N->getKind();
switch (Kind) {
case ASTNode::NodeKind::NK_Break: {
BreakNode *Break = llvm::cast<BreakNode>(N);
if (Break->breaksFromWithinSwitch()) {
auto *StateVarD = ASTBuilder.getOrCreateSwitchStateVarDecl(FDecl);
QualType T = StateVarD->getType();
clang::Expr *State = new (ASTCtx)
DeclRefExpr(ASTCtx, StateVarD, false, T, VK_LValue, {});
clang::Expr *TrueVal = ASTBuilder.getBoolLiteral(true);
QualType BoolTy = TrueVal->getType();
clang::Stmt *AssignStmt = new (ASTCtx) clang::BinaryOperator(State,
TrueVal,
BO_Assign,
BoolTy,
VK_RValue,
OK_Ordinary,
{},
FPOptions());
Stmts.push_back(AssignStmt);
}
};
[[fallthrough]];
case ASTNode::NodeKind::NK_SwitchBreak:
Stmts.push_back(new (ASTCtx) clang::BreakStmt(SourceLocation{}));
break;
case ASTNode::NodeKind::NK_Continue: {
ContinueNode *Continue = cast<ContinueNode>(N);
// Print the condition computation code of the if statement.
if (Continue->hasComputation()) {
IfNode *ComputationIfNode = Continue->getComputationIfNode();
createCondExpr(ComputationIfNode->getCondExpr(),
ASTCtx,
Stmts,
ASTBuilder,
Mark);
}
// Actually print the continue statement only if the continue is not
// implicit (i.e. it is not the last statement of the loop).
if (not Continue->isImplicit()) {
Stmts.push_back(new (ASTCtx) clang::ContinueStmt(SourceLocation{}));
}
} break;
case ASTNode::NodeKind::NK_Code: {
CodeNode *Code = cast<CodeNode>(N);
llvm::BasicBlock *BB = Code->getOriginalBB();
revng_assert(BB != nullptr);
buildStmtsForBasicBlock(BB, ASTCtx, Stmts, ASTBuilder, Mark);
} break;
case ASTNode::NodeKind::NK_If: {
IfNode *If = cast<IfNode>(N);
clang::Expr *CondExpr = createCondExpr(If->getCondExpr(),
ASTCtx,
Stmts,
ASTBuilder,
Mark);
revng_assert(CondExpr != nullptr);
revng_assert(nullptr != If->getThen());
clang::Stmt *ThenScope = buildCompoundScope(FDecl,
If->getThen(),
ASTCtx,
ASTBuilder,
Mark);
revng_assert(nullptr != ThenScope);
// Handle the situation in which we do have a nullptr in the place of the
// else node of the if statement, which may result in a non empty
// `ElseScope` and therefore an empty compound statement.
if (If->getElse() == nullptr) {
Stmts.push_back(IfStmt::Create(ASTCtx,
{},
false,
nullptr,
nullptr,
CondExpr,
ThenScope,
{},
nullptr));
} else {
clang::Stmt *ElseScope = buildCompoundScope(FDecl,
If->getElse(),
ASTCtx,
ASTBuilder,
Mark);
revng_assert(nullptr != ElseScope);
Stmts.push_back(IfStmt::Create(ASTCtx,
{},
false,
nullptr,
nullptr,
CondExpr,
ThenScope,
{},
ElseScope));
}
break;
}
case ASTNode::NodeKind::NK_Scs: {
ScsNode *LoopBody = cast<ScsNode>(N);
if (LoopBody->isDoWhile()) {
SmallVector<clang::Stmt *, 32> AdditionalStmts;
// This shold retrieve the if which generates the condition of the loop
// by accesing a dedicated field in the ScsNode.
IfNode *LoopCondition = LoopBody->getRelatedCondition();
clang::Expr *CondExpr = createCondExpr(LoopCondition->getCondExpr(),
ASTCtx,
AdditionalStmts,
ASTBuilder,
Mark);
clang::Stmt *Body = buildCompoundScope(FDecl,
LoopBody->getBody(),
ASTCtx,
ASTBuilder,
Mark,
AdditionalStmts);
for (clang::Stmt *S : AdditionalStmts)
Stmts.push_back(S);
Stmts.push_back(new (ASTCtx) DoStmt(Body, CondExpr, {}, {}, {}));
} else if (LoopBody->isWhile()) {
// This shold retrieve the if which generates the condition of the loop
// by accesing a dedicated field in the ScsNode.
IfNode *LoopCondition = LoopBody->getRelatedCondition();
clang::Expr *CondExpr = createCondExpr(LoopCondition->getCondExpr(),
ASTCtx,
Stmts,
ASTBuilder,
Mark);
clang::Stmt *Body = buildCompoundScope(FDecl,
LoopBody->getBody(),
ASTCtx,
ASTBuilder,
Mark);
Stmts.push_back(WhileStmt::Create(ASTCtx, nullptr, CondExpr, Body, {}));
} else {
// Standard case.
clang::Stmt *Body = buildCompoundScope(FDecl,
LoopBody->getBody(),
ASTCtx,
ASTBuilder,
Mark);
QualType UInt = ASTCtx.UnsignedIntTy;
unsigned UIntSize = static_cast<unsigned>(ASTCtx.getTypeSize(UInt));
clang::Expr *TrueCond = IntegerLiteral::Create(ASTCtx,
llvm::APInt(UIntSize, 1),
UInt,
{});
Stmts.push_back(WhileStmt::Create(ASTCtx, nullptr, TrueCond, Body, {}));
}
} break;
case ASTNode::NodeKind::NK_List: {
SequenceNode *Seq = cast<SequenceNode>(N);
for (ASTNode *Child : Seq->nodes())
buildAndAppendSmts(FDecl, Stmts, Child, ASTCtx, ASTBuilder, Mark);
} break;
case ASTNode::NodeKind::NK_Switch: {
SwitchNode *Switch = cast<SwitchNode>(N);
// Generate the condition of the switch.
clang::Expr *CondExpr = nullptr;
llvm::Value *SwitchVar = Switch->getCondition();
if (SwitchVar) {
// If the switch is not weaved we need to print the instructions in the
// basic block before it.
if (not Switch->isWeaved()) {
llvm::BasicBlock *BB = Switch->getOriginalBB();
revng_assert(BB != nullptr); // This is not a switch dispatcher.
buildStmtsForBasicBlock(BB, ASTCtx, Stmts, ASTBuilder, Mark);
}
CondExpr = ASTBuilder.getExprForValue(SwitchVar);
} else {
revng_assert(Switch->getOriginalBB() == nullptr);
// This is a dispatcher switch, check the loop state variable
clang::VarDecl *StateVarD = ASTBuilder.getOrCreateLoopStateVarDecl(FDecl);
QualType T = StateVarD->getType();
CondExpr = new (ASTCtx)
DeclRefExpr(ASTCtx, StateVarD, false, T, VK_LValue, {});
}
revng_assert(CondExpr != nullptr);
// Generate the switch statement
clang::SwitchStmt *SwitchStatement = SwitchStmt::Create(ASTCtx,
nullptr,
nullptr,
CondExpr);
// Generate the body of the switch
SmallVector<clang::Stmt *, 8> BodyStmts;
for (const auto &[Labels, CaseNode] : Switch->cases()) {
revng_assert(not Labels.empty()); // The Default is handled separately
// Build the body of the case. We build it before iterating on the case
// labels, because we may have more than one case label with the same
// body, such as in:
// switch (x) {
// case 0:
// case 1:
// case 2:
// return 5;
// }
// So, first we build here the compound statement representing the scope
// with return 5;
clang::Stmt *CaseBody = buildCompoundScope(FDecl,
CaseNode,
ASTCtx,
ASTBuilder,
Mark);
// Now we iterate on the case labels and we build them as clang produces
// them, i. e. in the following shape
//
// |-SwitchStmt
// | `-DeclRefExpr 'int' 'x'
// `-CompoundStmt
// |-CaseStmt
// | |-ConstantExpr 'int'
// | | `-IntegerLiteral 'int' 0
// | `-CaseStmt
// | |-ConstantExpr 'int'
// | | `-IntegerLiteral 'int' 1
// | `-CaseStmt
// | |-ConstantExpr 'int'
// | | `-IntegerLiteral 'int' 2
// | `-ReturnStmt
// | `-IntegerLiteral 'int' 5
llvm::SmallVector<clang::CaseStmt *, 8> Cases;
for (uint64_t CaseVal : Labels) {
clang::Expr *CaseExpr = nullptr;
if (SwitchVar) {
llvm::Type *SwitchVarT = SwitchVar->getType();
auto *IntType = cast<llvm::IntegerType>(SwitchVarT);
auto *CaseConst = llvm::ConstantInt::get(IntType, CaseVal);
CaseExpr = ASTBuilder.getExprForValue(CaseConst);
} else {
CaseExpr = ASTBuilder.getUIntLiteral(CaseVal);
}
revng_assert(CaseExpr != nullptr);
// Build the case
clang::CaseStmt *Case = CaseStmt::Create(ASTCtx,
CaseExpr,
nullptr,
{},
{},
{});
Case->setSubStmt(CaseBody);
Cases.push_back(Case);
// Set CaseBody to point to the last added Case, because this Case will
// be the body of the next CaseStmt.
CaseBody = Case;
}
revng_assert(llvm::isa<clang::CaseStmt>(CaseBody));
BodyStmts.push_back(CaseBody);
BodyStmts.push_back(new (ASTCtx) clang::BreakStmt(SourceLocation{}));
// Do it in reverse order, so that cases are inserted in the same order
// that you can see them in the emitted code. Not sure if this is
// necessary, but just want to avoid problems.
for (clang::CaseStmt *Case : llvm::reverse(Cases))
SwitchStatement->addSwitchCase(Case);
}
if (auto *Default = Switch->getDefault()) {
// Build the case
auto *Def = new (ASTCtx) clang::DefaultStmt({}, {}, nullptr);
// Build the body of the case
clang::Stmt *DefBody = buildCompoundScope(FDecl,
Default,
ASTCtx,
ASTBuilder,
Mark);
Def->setSubStmt(DefBody);
BodyStmts.push_back(Def);
BodyStmts.push_back(new (ASTCtx) clang::BreakStmt(SourceLocation{}));
SwitchStatement->addSwitchCase(Def);
}
clang::Stmt *SwitchBody = CompoundStmt::Create(ASTCtx, BodyStmts, {}, {});
SwitchStatement->setBody(SwitchBody);
// If the switch needs a loop break dispatcher, reset the associated state
// variable before emitting the switch statement.
if (Switch->needsLoopBreakDispatcher()) {
auto *StateVarD = ASTBuilder.getOrCreateSwitchStateVarDecl(FDecl);
QualType T = StateVarD->getType();
clang::Expr *State = new (ASTCtx)
DeclRefExpr(ASTCtx, StateVarD, false, T, VK_LValue, {});
clang::Expr *FalseInit = ASTBuilder.getBoolLiteral(false);
QualType BoolTy = FalseInit->getType();
clang::Stmt *AssignStmt = new (ASTCtx) clang::BinaryOperator(State,
FalseInit,
BO_Assign,
BoolTy,
VK_RValue,
OK_Ordinary,
{},
FPOptions());
Stmts.push_back(AssignStmt);
}
Stmts.push_back(SwitchStatement);
// If the switch needs it, generate a dispatcher to handle break
// instructions inside the switch that are trying to break direcly out of a
// loop that contains the switch
if (Switch->needsLoopBreakDispatcher()) {
// Build the AST for
// if (CondExpr)
// break;
auto *StateVarD = ASTBuilder.getOrCreateSwitchStateVarDecl(FDecl);
QualType T = StateVarD->getType();
CondExpr = new (ASTCtx)
DeclRefExpr(ASTCtx, StateVarD, false, T, VK_LValue, {});
clang::BreakStmt *Break = new (ASTCtx) clang::BreakStmt(SourceLocation{});
Stmts.push_back(IfStmt::Create(ASTCtx,
{},
false,
nullptr,
nullptr,
CondExpr,
Break,
{},
nullptr));
}
} break;
case ASTNode::NodeKind::NK_Set: {
SetNode *Set = cast<SetNode>(N);
auto *StateVarDecl = ASTBuilder.getOrCreateLoopStateVarDecl(FDecl);
QualType Type = StateVarDecl->getType();
clang::DeclRefExpr *StateVar = new (ASTCtx)
DeclRefExpr(ASTCtx, StateVarDecl, false, Type, VK_LValue, {});
unsigned StateValue = Set->getStateVariableValue();
clang::Expr *StateValueUInt = ASTBuilder.getUIntLiteral(StateValue);
QualType UIntType = StateValueUInt->getType();
clang::Stmt *AssignStmt = new (ASTCtx) clang::BinaryOperator(StateVar,
StateValueUInt,
BO_Assign,
UIntType,
VK_RValue,
OK_Ordinary,
{},
FPOptions());
Stmts.push_back(AssignStmt);
} break;
}
}
static void buildFunctionBody(llvm::Function *F,
clang::FunctionDecl *FDecl,
ASTTree &CombedAST,
IR2AST::StmtBuilder &ASTBuilder,
MarkForSerialization::Analysis &Mark) {
ASTContext &ASTCtx = FDecl->getASTContext();
// Check that the function we are attempting to decompile is not a variadic
// function
revng_assert(not FDecl->isVariadic());
SmallVector<clang::Stmt *, 32> BodyStmts;
buildAndAppendSmts(*FDecl,
BodyStmts,
CombedAST.getRoot(),
ASTCtx,
ASTBuilder,
Mark);
SmallVector<clang::Decl *, 16> LocalVarDecls;
for (auto &DeclPair : ASTBuilder.AllocaDecls)
LocalVarDecls.push_back(DeclPair.second);
for (auto &DeclPair : ASTBuilder.VarDecls)
LocalVarDecls.push_back(DeclPair.second);
if (clang::VarDecl *V = ASTBuilder.getLoopStateVarDecl())
LocalVarDecls.push_back(V);
if (clang::VarDecl *V = ASTBuilder.getSwitchStateVarDecl())
LocalVarDecls.push_back(V);
auto NumLocalVars = LocalVarDecls.size();
auto NumStmtsInBody = BodyStmts.size() + NumLocalVars;
revng_check(static_cast<unsigned>(NumLocalVars) == NumLocalVars,
"error: the function you are trying to decompile is too big!");
unsigned BodySize = static_cast<unsigned>(NumStmtsInBody);
unsigned VarSize = static_cast<unsigned>(NumLocalVars);
revng_check(static_cast<unsigned>(NumStmtsInBody) == NumStmtsInBody,
"error: the function you are trying to decompile is too big!");
CompoundStmt *Body = CompoundStmt::CreateEmpty(ASTCtx, BodySize);
FDecl->setBody(Body);
for (unsigned I = 0; I < VarSize; ++I) {
Decl *VDecl = LocalVarDecls[I];
auto *LocalVarDeclStmt = new (ASTCtx) DeclStmt(DeclGroupRef(VDecl), {}, {});
Body->body_begin()[I] = LocalVarDeclStmt;
}
for (unsigned I = VarSize; I < BodySize; ++I)
Body->body_begin()[I] = BodyStmts[I - VarSize];
/*
* This is a leftover from when we used to print all the labels of the
* basicblocks and put gotos all over the places.
* I left it here as a reference, for when we plan to revive part on the work
* on the gotos.
*/
/*
int I = VarSize;
auto End = ASTInfo.InstrStmts.end();
for (llvm::BasicBlock &BB : &F) {
SmallVector<clang::Stmt *, 16> BBStmts;
for (llvm::Instruction &Instr : BB) {
auto It = ASTInfo.InstrStmts.find(&Instr);
if (It != End)
BBStmts.push_back(It->second);
}
auto *BBCompoundStmt = CompoundStmt::Create(ASTCtx, BBStmts, {}, {});
Body->body_begin()[I] = new (ASTCtx)
LabelStmt({}, ASTInfo.LabelDecls.at(&BB), BBCompoundStmt);
++I;
}
*/
}
class Decompiler : public ASTConsumer {
private:
using BBPHIMap = SmallMap<llvm::BasicBlock *, PHIIncomingMap, 4>;
using DuplicationMap = std::map<llvm::BasicBlock *, size_t>;
public:
explicit Decompiler(llvm::Function &F,
ASTTree &CombedAST,
BBPHIMap &BlockToPHIIncoming,
const dla::ValueLayoutMap *VL,
std::unique_ptr<llvm::raw_ostream> Out,
DuplicationMap &NDuplicates) :
TheF(F),
CombedAST(CombedAST),
Out(std::move(Out)),
BlockToPHIIncoming(BlockToPHIIncoming),
ValueLayouts(VL),
NDuplicates(NDuplicates) {}
virtual void HandleTranslationUnit(ASTContext &Context) override;
private:
llvm::Function &TheF;
ASTTree &CombedAST;
std::unique_ptr<llvm::raw_ostream> Out;
BBPHIMap &BlockToPHIIncoming;
const dla::ValueLayoutMap *ValueLayouts;
DuplicationMap &NDuplicates;
};
void Decompiler::HandleTranslationUnit(ASTContext &Context) {
revng_assert(not TheF.isDeclaration());
revng_assert(TheF.getMetadata("revng.func.entry"));
revng_assert(not TheF.isDeclaration());
revng_assert(TheF.getMetadata("revng.func.entry"));
MarkForSerialization::Analysis Mark(TheF, NDuplicates);
Mark.initialize();
Mark.run();
beautifyAST(TheF, CombedAST, Mark);
DeclCreator Declarator(ValueLayouts);
IR2AST::StmtBuilder ASTBuilder(Mark.getToSerialize(),
Context,
BlockToPHIIncoming,
Declarator);
Declarator.createTypeDeclsForFunctionPrototype(Context, &TheF);
Declarator.createGlobalVarDeclUsedByFunction(Context, &TheF, ASTBuilder);
Declarator.createFunctionAndCalleesDecl(Context, &TheF);
clang::FunctionDecl *FunctionDecl = Declarator.FunctionDecls.at(&TheF);
ASTBuilder.createAST(TheF, *FunctionDecl);
clang::TranslationUnitDecl *TUDecl = Context.getTranslationUnitDecl();
// TODO: sooner or later, whenever we start emitting complex type
// declarations, we will need to enforce proper ordering between dependent
// types, and inject forward type declarations when needed.
//
// Create and add forward-declare all the structs, so that we other structs
// that have pointer-to-struct fields are always well-formed.
// This does not solve all the problems. For instance, if a struct A has a
// field of type struct B (not struct B *) B must be fully declared before the
// declaration of A. Just a forward declaration will not cut it.
for (const auto &TypeDecl : Declarator.typeDecls()) {
if (auto *StructDecl = llvm::dyn_cast<clang::RecordDecl>(TypeDecl)) {
auto *FwdDecl = clang::RecordDecl::Create(Context,
StructDecl->getTagKind(),
TUDecl,
clang::SourceLocation{},
clang::SourceLocation{},
StructDecl->getIdentifier(),
nullptr);
TUDecl->addDecl(FwdDecl);
}
}
// The ordering of definitions for full struct type is now handled by ensuring
// that type declarations are pushed into the typeDecls vector in correct
// order.
// TypeDecls are printed in C in the same order they are inserted into TUDecl.
// Here, we iterate in reverse order when inserting them into the TUDecl, so
// that the types that were created first are printed first. This, for now,
// ensures that, when we emit the full definition of a struct A with a field
// with type struct B, we have already emitted the full definition of struct B
for (const auto &TypeDecl : llvm::reverse(Declarator.typeDecls())) {
// Double check that the typedef decl for bool is not inserted twice
clang::DeclarationName TypeName = TypeDecl->getDeclName();
if (TypeName.getAsString() == "bool") {
bool Found = false;
revng_assert(isa<clang::TypedefDecl>(TypeDecl));
for (clang::Decl *D : TUDecl->lookup(TypeName)) {
if (D == TypeDecl) {
// the TypedefDecl `typedef _Bool bool` has already been inserted
// in the translation unit `DeclContext`
Found = true;
break;
}
}
// if the TypedefDecl `typedef _Bool bool` has already been inserted
// we don't insert it twice and we jump to the next TypeDecl
if (Found)
continue;
}
TUDecl->addDecl(TypeDecl);
}
for (auto &GlobalDecl : Declarator.GlobalDecls)
TUDecl->addDecl(GlobalDecl.second);
for (auto &FDecl : Declarator.FunctionDecls) {
if (FunctionDecl == FDecl.second)
continue;
TUDecl->addDecl(FDecl.second);
}
TUDecl->addDecl(FunctionDecl);
buildFunctionBody(&TheF, FunctionDecl, CombedAST, ASTBuilder, Mark);
using ConsumerPtr = std::unique_ptr<ASTConsumer>;
ConsumerPtr Printer = CreateASTPrinter(std::move(Out), "");
Printer->HandleTranslationUnit(Context);
}
std::unique_ptr<ASTConsumer> CDecompilerAction::newASTConsumer() {
return std::make_unique<Decompiler>(F,
CombedAST,
BlockToPHIIncoming,
LayoutMap,
std::move(O),
NDuplicates);
}
std::unique_ptr<ASTConsumer>
CDecompilerAction::CreateASTConsumer(CompilerInstance &, llvm::StringRef) {
return newASTConsumer();
}
} // end namespace tooling
} // end namespace clang