Files
revng-revng/lib/StackAnalysis/FunctionABI.cpp
T
2019-03-06 09:04:58 +01:00

1064 lines
31 KiB
C++

/// \file functionabi.cpp
/// \brief Implementation of the ABI analysis
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
// Local libraries includes
#include "revng/Support/MonotoneFramework.h"
// Local includes
#include "ABIIR.h"
#include "FunctionABI.h"
using std::conditional;
using std::tuple;
using std::tuple_element;
using std::tuple_size;
using llvm::Module;
Logger<> SaABI("sa-abi");
namespace StackAnalysis {
using ABIIRBB = ABIIRBasicBlock;
static ASID CPU = ASID::cpuID();
template<typename K1, size_t N1, typename K2, typename V2, size_t N2>
using MapOfMaps = DefaultMap<K1, DefaultMap<K2, V2, N2>, N1>;
/// \brief A set of helper functions related to DefaultMap
namespace MapHelpers {
enum Comparison { Lower = -1, Equal = 0, Greater = 1 };
/// \brief Similar to Rust cmp
template<typename T>
static inline Comparison compare(T A, T B) {
return A == B ? Equal : (A < B ? Lower : Greater);
}
template<typename K, typename V, bool Diff, bool EarlyExit, size_t N>
unsigned
cmp(const DefaultMap<K, V, N> &This, const DefaultMap<K, V, N> &Other) {
LoggerIndent<> Y(SaDiffLog);
unsigned Result = 0;
for (auto &P : This) {
P.second.template cmp<Diff, EarlyExit>(Other.getOrDefault(P.first));
ROA((P.second.template cmp<Diff, EarlyExit>(Other.getOrDefault(P.first))),
{ revng_log(SaDiffLog, P.first); });
}
for (auto &P : Other) {
ROA((This.getOrDefault(P.first).template cmp<Diff, EarlyExit>(P.second)),
{ revng_log(SaDiffLog, P.first); });
}
return Result;
}
template<typename K, typename V, bool Diff, bool EarlyExit, size_t N>
unsigned cmpWithModule(const DefaultMap<K, V, N> &This,
const DefaultMap<K, V, N> &Other,
ASID ID,
const Module *M) {
LoggerIndent<> Y(SaDiffLog);
unsigned Result = 0;
for (auto &P : This) {
ROA((P.second.template cmp<Diff, EarlyExit>(Other.getOrDefault(P.first))), {
ASSlot::create(ID, P.first).dump(M, SaDiffLog);
SaDiffLog << DoLog;
});
}
for (auto &P : Other) {
ROA((This.getOrDefault(P.first).template cmp<Diff, EarlyExit>(P.second)), {
ASSlot::create(ID, P.first).dump(M, SaDiffLog);
SaDiffLog << DoLog;
});
}
return Result;
}
template<typename K,
typename V,
bool Diff,
bool EarlyExit,
size_t N1,
size_t N2>
unsigned nestedCmpWithModule(const MapOfMaps<FunctionCall, N1, K, V, N2> &This,
const MapOfMaps<FunctionCall, N1, K, V, N2> &Other,
ASID ID,
const Module *M) {
LoggerIndent<> Y(SaDiffLog);
unsigned Result = 0;
for (auto &P : This) {
ROA((cmpWithModule<K, V, Diff, EarlyExit>(P.second,
Other.getOrDefault(P.first),
ID,
M)),
{
P.first.dump(SaDiffLog);
SaDiffLog << DoLog;
});
}
for (auto &P : Other) {
ROA((cmpWithModule<K, V, Diff, EarlyExit>(This.getOrDefault(P.first),
P.second,
ID,
M)),
{
P.first.dump(SaDiffLog);
SaDiffLog << DoLog;
});
}
return Result;
}
template<typename V, typename Q>
static void combine(V &This, const Q &Other) {
This.combine(Other);
}
template<typename K, typename V, typename Q, size_t N>
static void
combine(DefaultMap<K, V, N> &This, const DefaultMap<K, Q, N> &Other) {
combine(This.Default, Other.Default);
This.sort();
Other.sort();
llvm::SmallVector<const std::pair<const K, Q> *, N> Missing;
auto ThisIt = This.begin();
auto ThisEnd = This.end();
auto OtherIt = Other.begin();
auto OtherEnd = Other.end();
// Iterate over the two maps pairwise
while (OtherIt != OtherEnd && ThisIt != ThisEnd) {
switch (compare(ThisIt->first, OtherIt->first)) {
case Greater:
// Missing, add later (can't change This while iterating)
Missing.push_back(&*OtherIt);
OtherIt++;
break;
case Equal:
// Merge
combine(ThisIt->second, OtherIt->second);
ThisIt++;
OtherIt++;
break;
case Lower:
// Only ours, merge with default
combine(ThisIt->second, Other.Default);
ThisIt++;
break;
}
}
// Handle the remaining elements of Other
while (OtherIt != OtherEnd) {
combine(This[OtherIt->first], OtherIt->second);
OtherIt++;
}
// Handle the remaining elements of This
while (ThisIt != ThisEnd) {
combine(ThisIt->second, Other.Default);
ThisIt++;
}
// Handle the elements we registered
for (auto *P : Missing)
combine(This[P->first], P->second);
}
template<typename V, typename T, size_t N>
inline void
dump(const Module *M, T &Output, const DefaultMap<int32_t, V, N> &D, ASID ID) {
for (auto &P : D) {
ASSlot::create(ID, P.first).dump(M, Output);
Output << ":\n";
P.second.dump(Output);
Output << "\n";
}
}
template<typename V, typename T, size_t N>
inline void dump(const Module *M,
T &Output,
const DefaultMap<int32_t, V, N> &D,
ASID ID,
const char *Prefix) {
std::string Longer(Prefix);
Longer += " ";
Output << Prefix << "Default:\n";
D.Default.dump(Output, Longer.data());
Output << "\n";
for (auto &P : D) {
Output << Prefix;
ASSlot::create(ID, P.first).dump(M, Output);
Output << ":\n";
P.second.dump(Output, Longer.data());
Output << "\n";
}
}
template<typename V, typename T, size_t N1, size_t N2>
inline void dump(const Module *M,
T &Output,
const MapOfMaps<FunctionCall, N1, int32_t, V, N2> &D,
ASID ID,
const char *Prefix) {
std::string Longer(Prefix);
Longer += " ";
Output << Prefix << "Default:\n";
dump(M, Output, D.Default, ID, Longer.data());
Output << "\n";
for (auto &P : D) {
Output << Prefix;
P.first.dump(Output);
Output << ":\n";
dump(M, Output, P.second, ID, Longer.data());
Output << "\n";
}
}
template<typename V, typename Q>
static void returnFromCall(V &This, const Q &Other) {
This.returnFromCall(Other);
}
template<typename K, typename V, typename Q, size_t N>
static void
returnFromCall(DefaultMap<K, V, N> &This, const DefaultMap<K, Q, N> &Other) {
returnFromCall(This.Default, Other.Default);
This.sort();
Other.sort();
llvm::SmallVector<const std::pair<const K, Q> *, N> Missing;
auto ThisIt = This.begin();
auto ThisEnd = This.end();
auto OtherIt = Other.begin();
auto OtherEnd = Other.end();
// Iterate over the two maps pairwise
while (OtherIt != OtherEnd && ThisIt != ThisEnd) {
switch (compare(ThisIt->first, OtherIt->first)) {
case Greater:
// Missing, add later (can't change This while iterating)
Missing.push_back(&*OtherIt);
OtherIt++;
break;
case Equal:
// Merge
returnFromCall(ThisIt->second, OtherIt->second);
ThisIt++;
OtherIt++;
break;
case Lower:
// Only ours, merge with default
returnFromCall(ThisIt->second, Other.Default);
ThisIt++;
break;
}
}
// Handle the remaining elements of Other
while (OtherIt != OtherEnd) {
returnFromCall(This[OtherIt->first], OtherIt->second);
OtherIt++;
}
// Handle the remaining elements of This
while (ThisIt != ThisEnd) {
returnFromCall(ThisIt->second, Other.Default);
ThisIt++;
}
// Handle the elements we registered
for (auto *P : Missing)
returnFromCall(This[P->first], P->second);
}
template<typename K, typename T1, size_t N>
void unknownFunctionCall(DefaultMap<K, T1, N> &This) {
This.Default.unknownFunctionCall();
for (auto &P : This)
P.second.unknownFunctionCall();
}
template<typename K, typename T1, size_t N>
void disable(DefaultMap<K, T1, N> &This) {
This.Default.disable();
for (auto &P : This)
P.second.disable();
}
template<typename K, typename T1, size_t N>
void enable(DefaultMap<K, T1, N> &This) {
This.Default.enable();
for (auto &P : This)
P.second.enable();
}
} // namespace MapHelpers
/// \brief Wrapper for an analysis that can inhibit it
template<class S>
class Inhibitor : public S {
public:
using Base = S;
public:
bool Enabled;
public:
Inhibitor() : S(), Enabled(false) {}
explicit Inhibitor(typename S::Values V) : S(V), Enabled(false) {}
explicit Inhibitor(typename S::Values V, bool Enabled) :
S(V),
Enabled(Enabled) {}
bool isEnabled() const { return Enabled; }
void enable() { Enabled = true; }
void disable() { Enabled = false; }
void combine(const Inhibitor &Other) {
// TODO: we should assert the non-enabled one is bottom, or just ignore it
S::combine(Other);
Enabled = Enabled || Other.Enabled;
}
bool greaterThan(const Inhibitor &Other) const {
return not lowerThanOrEqual(Other);
}
bool lowerThanOrEqual(const Inhibitor &Other) const {
if (isEnabled() and not Other.isEnabled())
return false;
else
return S::lowerThanOrEqual(Other);
}
void transfer(typename S::TransferFunction T) {
if (isEnabled())
S::transfer(T);
}
void transfer(GeneralTransferFunction T) {
if (isEnabled())
S::transfer(T);
}
void dump() const { dump(dbg); }
template<typename T>
void dump(T &Output) const {
// If analysis is inhibited, simply wrap it in parenthesis
if (not isEnabled())
Output << "(";
S::dump(Output);
if (not isEnabled())
Output << ")";
}
};
/// \brief Return whether a certain analysis should start from return labels
/// only
template<typename T>
static constexpr bool isReturnOnly() {
return false;
}
// Currently only URVOF is supposed to start from return points only
template<>
constexpr bool isReturnOnly<UsedReturnValuesOfFunction>() {
return true;
}
/// \brief Recursive template class to apply certain methods on all the analyses
/// in Tuple
///
/// This class has many template argument which are used only in certain
/// functions. This saves from partial function specialization and from having
/// on class per function.
///
/// \tparam Tuple the tuple of analysis to use
/// \tparam T see dumpAnalysis
/// \tparam Diff see dumpAnalysis
/// \tparam EarlyExit see dumpAnalysis
/// \tparam NextIndex index of the tuple type, used for the recursion
template<typename Tuple,
typename T = int,
bool Diff = false,
bool EarlyExit = false,
size_t NextIndex = tuple_size<Tuple>::value>
struct AnalysesWrapperHelpers {
using Next = AnalysesWrapperHelpers<Tuple, T, Diff, EarlyExit, NextIndex - 1>;
static const size_t Index = NextIndex - 1;
using Type = typename tuple_element<Index, Tuple>::type::Base;
static typename tuple_element<Index, Tuple>::type &get(Tuple &This) {
return std::get<Index>(This);
}
static const typename tuple_element<Index, Tuple>::type &
get(const Tuple &This) {
return std::get<Index>(This);
}
static void initial(Tuple &This, bool IsReturn) {
bool Enable = isReturnOnly<Type>() ? IsReturn : true;
get(This) = Inhibitor<Type>(Type::initial(), Enable);
Next::initial(This, IsReturn);
}
static void combine(Tuple &This, const Tuple &Other) {
get(This).combine(std::get<Index>(Other));
Next::combine(This, Other);
}
// TODO: maybe we should call these "collect"
static void assign(RegisterState &This, const Tuple &Other) {
This.getByType<Type>() = std::get<Index>(Other);
Next::assign(This, Other);
}
static void assign(CallSiteRegisterState &This, const Tuple &Other) {
This.getByType<Type>() = std::get<Index>(Other);
Next::assign(This, Other);
}
static void disable(Tuple &This) {
get(This).disable();
Next::disable(This);
}
static void enable(Tuple &This) {
get(This).enable();
Next::enable(This);
}
static void transfer(Tuple &This, GeneralTransferFunction TF) {
get(This).transfer(TF);
Next::transfer(This, TF);
}
static void dumpAnalysis(const Tuple &This, T &Output, const char *Prefix) {
StackAnalysis::dumpAnalysis(Output, Prefix, get(This));
Next::dumpAnalysis(This, Output, Prefix);
}
static void returnFromCall(Tuple &This, const RegisterState &Other) {
get(This).transfer(Other.getByType<Type>().returnTransferFunction());
Next::returnFromCall(This, Other);
}
static unsigned cmp(const Tuple &This, const Tuple &Other) {
unsigned Result = 0;
Result = !get(This).lowerThanOrEqual(std::get<Index>(Other));
if (Result != 0) {
if (EarlyExit)
return Result;
if (SaDiffLog.isEnabled() and Diff) {
SaDiffLog << Type::name() << ": ";
get(This).dump(SaDiffLog);
SaDiffLog << " and ";
std::get<Index>(Other).dump(SaDiffLog);
SaDiffLog << DoLog;
}
}
return Result + Next::cmp(This, Other);
}
};
/// \brief Specialization for the base case (NextIndex == 0)
template<typename Tuple, typename T, bool Diff, bool EarlyExit>
struct AnalysesWrapperHelpers<Tuple, T, Diff, EarlyExit, 0> {
static void initial(Tuple &, bool) {}
static void assign(Tuple &, const Tuple &) {}
static void combine(Tuple &, const Tuple &) {}
static void assign(RegisterState &, const Tuple &) {}
static void assign(CallSiteRegisterState &, const Tuple &) {}
static void disable(Tuple &) {}
static void enable(Tuple &) {}
static void transfer(Tuple &, GeneralTransferFunction) {}
static void dumpAnalysis(const Tuple &, T &, const char *) {}
static void returnFromCall(Tuple &, const RegisterState &) {}
static unsigned cmp(const Tuple &, const Tuple &) { return 0; }
};
/// \brief Helper class to dispatch methods required by Element onto the
/// low-level analyses
template<typename Tuple>
class AnalysesWrapper {
friend class RegisterState;
friend class CallSiteRegisterState;
public:
Tuple Analyses;
private:
using H = AnalysesWrapperHelpers<Tuple>;
using AnalysesType = Tuple;
public:
static AnalysesWrapper initial(bool IsReturn) {
AnalysesWrapper Result;
H::initial(Result.Analyses, IsReturn);
return Result;
}
AnalysesWrapper &combine(const AnalysesWrapper &Other) {
H::combine(this->Analyses, Other.Analyses);
return *this;
}
void disable() { H::disable(this->Analyses); }
void enable() { H::enable(this->Analyses); }
void write() { H::transfer(this->Analyses, GeneralTransferFunction::Write); }
void read() { H::transfer(this->Analyses, GeneralTransferFunction::Read); }
void unknownFunctionCall() {
H::transfer(this->Analyses, GeneralTransferFunction::UnknownFunctionCall);
}
void returnFromCall(const RegisterState &Other) {
H::returnFromCall(this->Analyses, Other);
}
template<bool Diff, bool EarlyExit>
unsigned cmp(const AnalysesWrapper &Other) const {
using H = AnalysesWrapperHelpers<Tuple, int, Diff, EarlyExit>;
LoggerIndent<> Y(SaDiffLog);
return H::cmp(this->Analyses, Other.Analyses);
}
void dump() const debug_function { dump(dbg); }
template<typename T>
void dump(T &Output, const char *Prefix = " ") const {
using H = AnalysesWrapperHelpers<Tuple, T>;
H::dumpAnalysis(this->Analyses, Output, Prefix);
}
};
/// Namespace for the classes composing the monotone framework of the ABI
/// analysis (and helper classes)
namespace ABIAnalysis {
/// \brief Element of the lattice of the monotone framework, tracks the result
/// of the various analysis for each label
///
/// This class basically acts as a dispatcher of the various actions/transfer
/// functions towards the underlying analysis specified in Analyses
///
/// \tparam Analyses an AnalysesList type listing all the function and funcion
/// call analysis to perform.
template<typename Analyses>
class Element {
friend class ::StackAnalysis::FunctionABI;
private:
using AWF = AnalysesWrapper<typename Analyses::Function>;
using AWFC = AnalysesWrapper<typename Analyses::FunctionCall>;
private:
/// Map tracking the status of registers from the point of view of the current
/// function
DefaultMap<int32_t, AWF, 20> RegisterAnalyses;
/// Map tracking the status of registers from the point of view of the each
/// function call
// TODO: We could have as well have a vector here, considering calls are
// relatively rare
MapOfMaps<FunctionCall, 5, int32_t, AWFC, 20> FunctionCallRegisterAnalyses;
public:
Element() {}
static Element bottom() { return Element(); }
/// \brief Explicit copy constructor
Element copy() const {
Element Result;
Result.RegisterAnalyses = RegisterAnalyses;
Result.FunctionCallRegisterAnalyses = FunctionCallRegisterAnalyses;
return Result;
}
Element(const Element &) = delete;
Element &operator=(const Element &) = delete;
Element(Element &&) = default;
Element &operator=(Element &&) = default;
public:
/// Reset and enable all the function analyses
///
/// This function enables all the function analyses except those that need to
/// start from a return basic block. In such cases, the analysis is enabled
/// only if \p IsReturn is true.
///
/// \param IsReturn whether the current block is a return basic block or not
void resetFunctionAnalyses(bool IsReturn) {
RegisterAnalyses.clear(AWF::initial(IsReturn));
}
/// \brief Enable all the function call analyses associated to \p TheCall
void resetFunctionCallAnalyses(FunctionCall TheCall) {
MapHelpers::unknownFunctionCall(FunctionCallRegisterAnalyses[TheCall]);
MapHelpers::enable(FunctionCallRegisterAnalyses[TheCall]);
FunctionCallRegisterAnalyses[TheCall].clear(AWFC::initial(true));
}
bool lowerThanOrEqual(const Element &Other) const {
return cmp<false, true>(Other) == 0;
}
// TODO: review
template<bool Diff, bool EarlyExit>
unsigned cmp(const Element &Other, const Module *M = nullptr) const {
using namespace MapHelpers;
LoggerIndent<> Y(SaDiffLog);
unsigned Result = 0;
auto registerCmp = cmpWithModule<int32_t, AWF, Diff, EarlyExit, 20>;
ROA((registerCmp(RegisterAnalyses, Other.RegisterAnalyses, CPU, M)),
{ revng_log(SaDiffLog, "RegisterAnalyses"); });
auto X = nestedCmpWithModule<int32_t, AWFC, Diff, EarlyExit, 5, 20>;
ROA((X(FunctionCallRegisterAnalyses,
Other.FunctionCallRegisterAnalyses,
CPU,
M)),
{ revng_log(SaDiffLog, "RegisterAnalyses"); });
return Result;
}
bool greaterThan(const Element &Other) const {
return not lowerThanOrEqual(Other);
}
Element &combine(const Element &Other) {
MapHelpers::combine(RegisterAnalyses, Other.RegisterAnalyses);
MapHelpers::combine(FunctionCallRegisterAnalyses,
Other.FunctionCallRegisterAnalyses);
return *this;
}
/// \brief Record that \p Slot has been written
void write(ASSlot Slot) {
// It should touch the slot at the given offset plus the slot in all the
// function call analyses, including default.
if (Slot.addressSpace() == CPU) {
RegisterAnalyses[Slot.offset()].write();
FunctionCallRegisterAnalyses.Default[Slot.offset()].write();
for (auto &P : FunctionCallRegisterAnalyses)
P.second[Slot.offset()].write();
}
}
/// \brief Record that \p Slot has been read
void read(ASSlot Slot) {
// It should touch the slot at the given offset plus the slot in all the
// function call analyses, including default.
if (Slot.addressSpace() == CPU) {
RegisterAnalyses[Slot.offset()].read();
FunctionCallRegisterAnalyses.Default[Slot.offset()].read();
for (auto &P : FunctionCallRegisterAnalyses)
P.second[Slot.offset()].read();
}
}
/// \brief Handle a call to a function for which the ABI analysis produced
/// \p Other
void directCall(const FunctionABI &CalleeABI) {
// It should touch all the register/stack slots plus all the register of
// every function call (including default).
// All register analyses
MapHelpers::returnFromCall(RegisterAnalyses, CalleeABI.RegisterAnalyses);
// All the register analyses of all the function calls (including default)
MapHelpers::returnFromCall(FunctionCallRegisterAnalyses.Default,
CalleeABI.RegisterAnalyses);
for (auto &P : FunctionCallRegisterAnalyses)
MapHelpers::returnFromCall(P.second, CalleeABI.RegisterAnalyses);
}
void indirectCall() {
// It should touch all the register plus all the register/stack slots of
// every function call (including default).
// All register analyses
MapHelpers::unknownFunctionCall(RegisterAnalyses);
// All the register analyses of all the function calls (including default)
MapHelpers::unknownFunctionCall(FunctionCallRegisterAnalyses.Default);
for (auto &P : FunctionCallRegisterAnalyses)
MapHelpers::unknownFunctionCall(P.second);
}
void dump(const Module *M) const debug_function { dump(M, dbg); }
template<typename T>
void dump(const Module *M, T &Output) const {
std::stringstream Stream;
dumpInternal(M, Stream);
Output << Stream.str();
}
private:
void dumpInternal(const Module *M, std::stringstream &Output) const {
MapHelpers::dump(M, Output, RegisterAnalyses, CPU);
MapHelpers::dump(M, Output, FunctionCallRegisterAnalyses, CPU, " ");
}
};
/// \brief Given a tuple, produce a new tuple where each element is wrapped in
/// another template class
///
/// \tparam Wrapper the template class to use for wrapping the elements of the
/// tuple.
/// \tparam Tuple the tuple to wrap.
template<template<typename X> class Wrapper,
typename Tuple,
int I = tuple_size<Tuple>::value,
typename... Types>
class WrapIn {
public:
/// The resulting tuple
using Wrapped = Wrapper<typename tuple_element<I - 1, Tuple>::type>;
using type = typename WrapIn<Wrapper, Tuple, I - 1, Wrapped, Types...>::type;
};
template<template<typename X> class Wrapper, typename Tuple, typename... Types>
class WrapIn<Wrapper, Tuple, 0, Types...> {
public:
using type = std::tuple<Types...>;
};
/// \brief Compile-time container for a set of function and function call
/// analyses
///
/// \tparam A tuple of function analyses
/// \tparam A tuple of function call analyses
template<typename F, typename FC>
class AnalysesList {
public:
using Function = typename WrapIn<Inhibitor, F>::type;
using FunctionCall = typename WrapIn<Inhibitor, FC>::type;
};
template<typename E>
class Interrupt {
private:
enum Reason { Regular, Return, NoReturn, Summary };
private:
Reason TheReason;
Element<E> Result;
private:
explicit Interrupt(Reason TheReason, Element<E> Result) :
TheReason(TheReason),
Result(std::move(Result)) {}
explicit Interrupt(Reason TheReason) : TheReason(TheReason), Result() {}
public:
static Interrupt createRegular(Element<E> Result) {
return Interrupt(Regular, std::move(Result));
}
static Interrupt createReturn(Element<E> Result) {
return Interrupt(Return, std::move(Result));
}
static Interrupt createNoReturn() { return Interrupt(NoReturn); }
static Interrupt createSummary(Element<E> Result) {
return Interrupt(Summary, std::move(Result));
}
public:
bool requiresInterproceduralHandling() {
switch (TheReason) {
case Regular:
case Return:
return false;
case NoReturn:
case Summary:
return true;
}
revng_abort();
}
bool isPartOfFinalResults() const {
revng_assert(TheReason == Regular or TheReason == Return);
return TheReason == Return;
}
Element<E> &&extractResult() { return std::move(Result); }
};
/// \brief The core of the ABI analysis
///
/// This monotone framework implements the ABI analysis.
///
/// \tparam IsForward whether the analysis should be performed forward or not
/// \tparam E an AnalysesList type listing all the function and funcion call
/// analysis to perform.
///
/// \note Don't reset and re-run this analysis
template<bool IsForward, typename E>
class Analysis
: public MonotoneFramework<ABIIRBasicBlock *,
Element<E>,
Interrupt<E>,
Analysis<IsForward, E>,
ABIIRBasicBlock::links_const_range,
IsForward ? ReversePostOrder : PostOrder> {
private:
using DirectedLabelRange = typename conditional<IsForward,
ABIIRBB::range,
ABIIRBB::reverse_range>::type;
public:
using Base = MonotoneFramework<ABIIRBasicBlock *,
Element<E>,
Interrupt<E>,
Analysis<IsForward, E>,
ABIIRBasicBlock::links_const_range,
IsForward ? ReversePostOrder : PostOrder>;
private:
/// The entry basic block of the function
ABIIRBasicBlock *FunctionEntry;
/// Counter for basic block visits, for statistical purposes
unsigned VisitsCount;
/// Flag to prevent the analysis from being run more than once
bool FirstRun;
public:
Analysis(ABIIRBasicBlock *FunctionEntry) :
Base(FunctionEntry),
FunctionEntry(FunctionEntry),
VisitsCount(0),
FirstRun(true) {}
public:
void assertLowerThanOrEqual(const Element<E> &A, const Element<E> &B) const {
const Module *M = getModule(FunctionEntry->basicBlock());
::StackAnalysis::assertLowerThanOrEqual(A, B, M);
}
/// \brief Prevent the analysis from running twice
void initialize() {
revng_assert(FirstRun, "The ABIAnalysis cannot be run twice");
FirstRun = false;
Base::initialize();
}
void dumpFinalState() const {}
llvm::Optional<Element<E>> handleEdge(const Element<E> &Original,
ABIIRBasicBlock *Source,
ABIIRBasicBlock *Destination) const {
return llvm::Optional<Element<E>>();
}
ABIIRBasicBlock::links_const_range
successors(ABIIRBasicBlock *BB, Interrupt<E> &) const {
return BB->next<IsForward>();
}
size_t successor_size(ABIIRBasicBlock *BB, Interrupt<E> &) const {
return BB->next_size<IsForward>();
}
Interrupt<E> createSummaryInterrupt() {
return Interrupt<E>::createSummary(std::move(this->FinalResult));
}
Interrupt<E> createNoReturnInterrupt() const {
return Interrupt<E>::createNoReturn();
}
Element<E> extremalValue(ABIIRBasicBlock *BB) const {
Element<E> Result;
// Initialize to `::initial()` and enable all the function-related
// analyses. Some of the backward analyses are available only if we're
// starting from a proper return.
Result.resetFunctionAnalyses(BB->isPartOfFinalResults());
return Result;
}
unsigned visitsCount() const { return VisitsCount; }
Interrupt<E> transfer(ABIIRBasicBlock *BB) {
revng_log(SaABI, "Analyzing " << BB->basicBlock());
Element<E> Result = this->State[BB].copy();
VisitsCount++;
for (ABIIRInstruction &I : range(BB)) {
// Result is Element<E>
switch (I.opcode()) {
case ABIIRInstruction::Load:
Result.read(I.target());
break;
case ABIIRInstruction::Store:
Result.write(I.target());
break;
case ABIIRInstruction::DirectCall:
Result.directCall(I.abi());
break;
case ABIIRInstruction::IndirectCall:
Result.indirectCall();
break;
}
// Once we get to a function call, if it's the first time we meet it, its
// analyses are going to be disabled. Here we first activate the unknown
// function call transfer function (while it might still be disabled) and
// then we enable all the analyses.
if (I.opcode() == ABIIRInstruction::DirectCall
or I.opcode() == ABIIRInstruction::IndirectCall) {
Result.resetFunctionCallAnalyses(I.call());
}
}
// We don't check BB->isPartOfFinalResults() since there are basic blocks
// that have no successors but are not returns. And we want to consider
// those too, unlike what happens with the stack analysis, where we are
// interested in understanding what happens from the point of view of the
// caller (e.g., if a callee-saved register is not restored on a noreturn
// path, we don't care).
if ((IsForward and BB->successor_size() == 0)
or (not IsForward and BB->predecessor_size() == 0))
return Interrupt<E>::createReturn(std::move(Result));
else
return Interrupt<E>::createRegular(std::move(Result));
}
private:
DirectedLabelRange range(ABIIRBasicBlock *BB) {
return instructionRange<DirectedLabelRange, IsForward>(BB);
}
};
} // namespace ABIAnalysis
//
// FunctionaABI methods
//
void FunctionABI::analyze(const ABIFunction &TheFunction) {
using namespace ABIAnalysis;
{
revng_log(SaABI, "Running forward function analyses");
// List of the forward ABI analyses to perform
// Note: Among the function analyses we also have an instance of the
// function call analyses so that we can use them interproceduraly to
// simulate the inling of the called function.
using DRAOF = DeadRegisterArgumentsOfFunction;
using UAOF = UsedArgumentsOfFunction;
using URVOFC = UsedReturnValuesOfFunctionCall;
using DRVOFC = DeadReturnValuesOfFunctionCall;
using FunctionWise = tuple<DRAOF, UAOF, URVOFC, DRVOFC>;
using FunctionCallWise = tuple<URVOFC, DRVOFC>;
using ForwardList = AnalysesList<FunctionWise, FunctionCallWise>;
Analysis<true, ForwardList> ForwardFunctionAnalyses(TheFunction.entry());
ForwardFunctionAnalyses.registerExtremal(TheFunction.entry());
ForwardFunctionAnalyses.initialize();
Interrupt<ForwardList> Result = ForwardFunctionAnalyses.run();
int Average = ForwardFunctionAnalyses.visitsCount() / TheFunction.size();
revng_log(SaABI,
"Forward function analyses terminated: "
<< ForwardFunctionAnalyses.visitsCount() << " visits performed"
<< " on " << TheFunction.size() << " blocks ("
<< "average: " << Average << ").");
this->combine(Result.extractResult());
}
{
revng_log(SaABI,
"Running backward function analyses ("
<< TheFunction.finals_size() << " return points)");
/// List of the backward ABI analyses to perform
using URVOF = UsedReturnValuesOfFunction;
using RAOFC = RegisterArgumentsOfFunctionCall;
using FunctionWise = tuple<URVOF, RAOFC>;
using FunctionCallWise = tuple<RAOFC>;
using BackwardList = AnalysesList<FunctionWise, FunctionCallWise>;
Analysis<false, BackwardList> BackwardFunctionAnalyses(TheFunction.entry());
for (ABIIRBasicBlock *FinalBB : TheFunction.finals())
BackwardFunctionAnalyses.registerExtremal(FinalBB);
BackwardFunctionAnalyses.initialize();
Interrupt<BackwardList> Result = BackwardFunctionAnalyses.run();
this->combine(Result.extractResult());
}
}
void FunctionABI::dumpInternal(const Module *M,
std::stringstream &Output) const {
MapHelpers::dump(M, Output, RegisterAnalyses, CPU);
Output << "Calls:\n\n";
for (auto &P : Calls) {
Output << " ";
P.first.dump(Output);
Output << ":\n";
MapHelpers::dump(M, Output, P.second.Registers, CPU, " ");
Output << "\n";
}
}
} // namespace StackAnalysis