mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
Extract MarkForSerialization in separate library
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
//
|
||||
// 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/Model/LoadModelPass.h"
|
||||
#include "revng/Support/FunctionTags.h"
|
||||
#include "revng/Support/IRHelpers.h"
|
||||
|
||||
#include "revng-c/MarkForSerialization/MarkForSerializationPass.h"
|
||||
#include "revng-c/RestructureCFGPass/RestructureCFG.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;
|
||||
}
|
||||
|
||||
auto SerIt = ToSerialize.find(&I);
|
||||
if (SerIt != ToSerialize.end()
|
||||
and (SerializationFlags::hasSideEffects(SerIt->second)
|
||||
or SerIt->second.isSet(SerializationReason::AlwaysSerialize))) {
|
||||
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
|
||||
|
||||
void MarkForSerializationPass::getAnalysisUsage(llvm::AnalysisUsage &AU) const {
|
||||
AU.addRequired<RestructureCFG>();
|
||||
AU.setPreservesAll();
|
||||
}
|
||||
|
||||
bool MarkForSerializationPass::runOnFunction(llvm::Function &F) {
|
||||
|
||||
// Skip non-isolated functions
|
||||
auto FTags = FunctionTags::TagsSet::from(&F);
|
||||
if (not FTags.contains(FunctionTags::Lifted))
|
||||
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);
|
||||
Reference in New Issue
Block a user