Files
revng-revng/lib/Decompiler/MarkForSerialization.cpp
T
Pietro Fezzardi 4767fa63c3 Fixed emission for local_sp and scev_barrier
This commit fixes a bug due to interacting behaviors between
MarkForSerialization, AddSCEVBarrierPass, and the emission in C of calls
to revng_init_local_sp.

These interacting behaviors caused the following quirks:
- At the beginning of Functions that contained a call to
  `revng_init_local_sp()`, that call was actually emitted twice.
  The first time was due to the actual call to `revng_init_local_sp()`,
  while the second was due to the first call being wrapped from a call
  to `revng_scev_barrier_*`.
  Now we properly emit only one call.
- The original call to `revng_init_local_sp()` was supposed to generate
  a local variable, to be used in various places across the function.
  However, due to the fact that the call was not properly labeled by
  MarkForSerialization, there was no local variable, causing calls to
  `revng_init_local_sp()` to be scattered around the body of the
  functions, follwed by various arithmetic operations.
  This behavior has been fixed as well, and we now emit the local
  variable correctly.
2021-02-02 11:23:53 +01:00

213 lines
7.1 KiB
C++

//
// Copyright rev.ng Srls. See LICENSE.md for details.
//
/// \brief Dataflow analysis to identify which Instructions must be serialized
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/Casting.h"
#include "revng/Support/IRHelpers.h"
#include "revng-c/Decompiler/MarkForSerialization.h"
#include "revng-c/RestructureCFGPass/BasicBlockNode.h"
#include "revng-c/RestructureCFGPass/RegionCFGTree.h"
#include "revng-c/TargetFunctionOption/TargetFunctionOption.h"
#include "MarkAnalysis.h"
Logger<> MarkLog("mark-serialization");
namespace MarkAnalysis {
static bool isPure(const llvm::Instruction & /*Call*/) {
return false;
}
static bool
haveInterferingSideEffects(const llvm::Instruction & /*InstrWithSideEffects*/,
const llvm::Instruction & /*Other*/) {
return true;
}
Analysis::InterruptType Analysis::transfer(const llvm::BasicBlock *BB) {
using namespace llvm;
revng_log(MarkLog,
"transfer: BB in Function: " << BB->getParent()->getName() << '\n'
<< BB);
LatticeElement Pending = this->State[BB].copy();
size_t NBBDuplicates = NDuplicates.at(BB);
for (const Instruction &I : *BB) {
revng_log(MarkLog, "Analyzing Instr: '" << &I << "': " << dumpToString(&I));
// Operands are removed from pending
revng_log(MarkLog, "Remove operands from pending.");
MarkLog.indent();
revng_log(MarkLog, "Operands:");
for (auto &TheUse : I.operands()) {
Value *V = TheUse.get();
revng_log(MarkLog, "Op: '" << V << "': " << dumpToString(V));
MarkLog.indent();
if (auto *UsedInstr = dyn_cast<Instruction>(V)) {
revng_log(MarkLog, "Op is Instruction: erase it from pending");
Pending.erase(UsedInstr);
} else {
revng_log(MarkLog, "Op is NOT Instruction: leave it in pending");
revng_assert(isa<Argument>(V) or isa<Constant>(V) or isa<BasicBlock>(V)
or isa<MetadataAsValue>(V));
}
MarkLog.unindent();
}
MarkLog.unindent();
// PHINodes are never serialized directly in the BB they are.
if (isa<PHINode>(I))
continue;
// Skip branching instructions.
// Branch instructions are never serialized directly, because it's only
// after building an AST and matching ifs, loops, switches and others that
// we really know what kind of C statement we want to emit for a given
// branch.
if (isa<BranchInst>(I) or isa<SwitchInst>(I))
continue;
if (isa<InsertValueInst>(I)) {
// InsertValueInst are serialized in C as:
// struct x = { .designated = 0xDEAD, .initializers = 0xBEEF };
// x.designated = value_that_overrides_0xDEAD;
// The second statement is always necessary.
ToSerialize[&I].set(NeedsManyStatements);
revng_log(MarkLog, "Instr NeedsManyStatements");
}
if (isa<InsertValueInst>(I) or isa<AllocaInst>(I)) {
// As noted in the comment above, InsertValueInst always need a local
// variable (x in the example above) for the computation of the expression
// that represents the result of Instruction itself.
// This is the local variable in C that will be used by x's users.
// Also AllocaInst always need a local variable, which is the variable
// allocated by the alloca.
ToSerialize[&I].set(NeedsLocalVarToComputeExpr);
revng_log(MarkLog, "Instr NeedsLocalVarToComputeExpr");
}
if (isa<StoreInst>(&I) or (isa<CallInst>(&I) and not isPure(I))) {
// StoreInst and CallInst that are not pure always have side effects.
ToSerialize[&I].set(HasSideEffects);
revng_log(MarkLog, "Instr HasSideEffects");
// Also, force calls to revng_init_local_sp to behave like if they had
// many uses, so that they generate a local variable.
if (auto *Call = dyn_cast<CallInst>(&I)) {
llvm::StringRef CalleeName = Call->getCalledFunction()->getName();
if (CalleeName == "revng_init_local_sp") {
ToSerialize[&I].set(HasManyUses);
revng_log(MarkLog, "Instr HasManyUses");
}
}
}
switch (I.getNumUses()) {
case 1: {
User *U = I.uses().begin()->getUser();
Instruction *UserI = cast<Instruction>(U);
BasicBlock *UserBB = UserI->getParent();
auto UserNDuplicates = NDuplicates.at(UserBB);
if (NBBDuplicates < UserNDuplicates) {
ToSerialize[&I].set(HasDuplicatedUses);
revng_log(MarkLog, "Instr HasDuplicatedUses");
}
} break;
case 0: {
// Do nothing
ToSerialize[&I].set(AlwaysSerialize);
revng_log(MarkLog, "Instr AlwaysSerialize");
} break;
default: {
// Instructions with more than one use are always serialized.
ToSerialize[&I].set(HasManyUses);
revng_log(MarkLog, "Instr HasManyUses");
} break;
}
if (ToSerialize.count(&I)) {
revng_log(MarkLog, "Serialize Pending");
// We also have to serialize all the instructions that are still pending
// and have interfering side effects.
for (auto PendingIt = Pending.begin(); PendingIt != Pending.end();) {
const auto *PendingInstr = PendingIt->first;
revng_log(MarkLog,
"Pending: '" << PendingInstr
<< "': " << dumpToString(PendingInstr));
if (haveInterferingSideEffects(I, *PendingInstr)) {
ToSerialize[PendingInstr].set(HasInterferingSideEffects);
revng_log(MarkLog, "HasInterferingSideEffects");
PendingIt = Pending.erase(PendingIt);
} else {
++PendingIt;
}
}
} else {
Pending.insert(&I);
revng_log(MarkLog,
"Add to pending: '" << &I << "': " << dumpToString(&I));
}
}
return InterruptType::createInterrupt(std::move(Pending));
}
void Analysis::initialize() {
Base::initialize();
LivenessAnalysis::Analysis Liveness(F);
Liveness.initialize();
Liveness.run();
LiveIn = Liveness.extractLiveIn();
}
} // namespace MarkAnalysis
bool MarkForSerializationPass::runOnFunction(llvm::Function &F) {
// Skip non-isolated functions
if (not F.getMetadata("revng.func.entry"))
return false;
// If the `-single-decompilation` option was passed from command line, skip
// decompilation for all the functions that are not the selected one.
if (not TargetFunction.empty())
if (not F.getName().equals(TargetFunction.c_str()))
return false;
// Compute the number of duplicates for each BasicBlock.
const auto &RestructurePass = getAnalysis<RestructureCFG>();
using MarkAnalysis::DuplicationMap;
const DuplicationMap &NDuplicates = RestructurePass.getNDuplicates();
// Mark instructions for serialization, and write the results in ToSerialize
ToSerialize = {};
MarkAnalysis::Analysis Mark(F, NDuplicates, ToSerialize);
Mark.initialize();
Mark.run();
return true;
}
char MarkForSerializationPass::ID = 0;
using Register = llvm::RegisterPass<MarkForSerializationPass>;
static Register X("mark-for-serialization",
"Pass that marks Instructions for serialization in C",
false,
false);