Files
revng-revng/include/revng/BasicAnalyses/AdvancedValueInfo.h
Alessandro Di Federico b0e82f709e [REWORD] Fix AVI
2021-05-11 21:52:22 +02:00

1080 lines
31 KiB
C++

#pragma once
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <set>
#include "llvm/Analysis/ConstantFolding.h"
#include "llvm/Analysis/LazyValueInfo.h"
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
#include "llvm/IR/ConstantRange.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/Support/raw_os_ostream.h"
#include "revng/ADT/ConstantRangeSet.h"
#include "revng/BasicAnalyses/MaterializedValue.h"
#include "revng/Support/Debug.h"
#include "revng/Support/GraphAlgorithms.h"
#include "revng/Support/IRHelpers.h"
#include "revng/Support/MonotoneFramework.h"
inline Logger<> AVILogger("avi");
using range_size_t = uint64_t;
const range_size_t MaxMaterializedValues = (1 << 16);
inline unsigned getTypeSize(const llvm::DataLayout &DL, llvm::Type *T) {
using namespace llvm;
if (auto *IntegerTy = dyn_cast<IntegerType>(T))
return IntegerTy->getBitWidth();
else if (auto *PtrTy = dyn_cast<PointerType>(T))
return DL.getPointerSize() * 8;
else
revng_abort();
}
/// Return the only Unknown value in the SCEV (if no AddRec/CouldNotCompute)
inline llvm::Value *
getUniqueUnknown(llvm::ScalarEvolution &SE, const llvm::SCEV *SC) {
using namespace llvm;
class FindSingleUnknown {
private:
bool Stop = false;
public:
Value *UniqueUnknown = nullptr;
public:
bool follow(const SCEV *S) {
if (Stop)
return false;
switch (S->getSCEVType()) {
case scConstant:
case scTruncate:
case scZeroExtend:
case scSignExtend:
case scMulExpr:
case scSMaxExpr:
case scUMaxExpr:
case scSMinExpr:
case scUMinExpr:
case scUDivExpr:
case scAddExpr:
break;
case scUnknown:
if (UniqueUnknown == nullptr) {
UniqueUnknown = cast<SCEVUnknown>(S)->getValue();
} else {
UniqueUnknown = nullptr;
Stop = true;
}
break;
case scAddRecExpr:
case scCouldNotCompute:
UniqueUnknown = nullptr;
Stop = true;
break;
}
return not Stop;
}
bool isDone() const { return Stop; }
};
FindSingleUnknown FSU;
visitAll(SC, FSU);
if (FSU.UniqueUnknown != nullptr)
revng_assert(not SE.containsAddRecurrence(SC));
return FSU.UniqueUnknown;
}
inline llvm::ConstantInt *replaceAllUnknownsWith(llvm::ScalarEvolution &SE,
const llvm::SCEV *SC,
llvm::ConstantInt *C) {
using namespace llvm;
revng_assert(not SE.containsAddRecurrence(SC));
class Rewriter : public SCEVRewriteVisitor<Rewriter> {
private:
ConstantInt *NewConstant;
public:
Rewriter(ScalarEvolution &SE, ConstantInt *NewConstant) :
SCEVRewriteVisitor(SE), NewConstant(NewConstant) {}
const SCEV *visitUnknown(const SCEVUnknown *) {
return SE.getConstant(NewConstant);
}
};
Rewriter RW(SE, C);
return cast<SCEVConstant>(RW.visit(SC))->getValue();
}
struct Edge {
llvm::BasicBlock *Start;
llvm::BasicBlock *End;
bool operator<(const Edge &Other) const {
return std::tie(Start, End) < std::tie(Other.Start, Other.End);
}
void dump() const debug_function { dump(dbg); }
template<typename T>
void dump(T &Output) const {
Output << getName(Start) << "->" << getName(End);
}
};
/// \brief Monotone framework to collect ConstantRangeSets from LazyValueInfo
namespace DisjointRanges {
class Element {
private:
using Container = std::map<llvm::Instruction *, ConstantRangeSet>;
Container Ranges;
public:
Element() {}
static Element bottom() { return Element(); }
Element copy() const { return *this; }
public:
void combine(const Element &Other) {
// We can't use zipmap_ranges since Other is const
for (auto &P : Other.Ranges) {
auto It = Ranges.find(P.first);
if (It == Ranges.end())
Ranges[P.first] = P.second;
else
It->second = It->second.unionWith(P.second);
}
}
bool lowerThanOrEqual(const Element &Other) const {
for (auto &P : Ranges) {
auto It = Other.Ranges.find(P.first);
if (It == Other.Ranges.end() or not It->second.contains(P.second))
return false;
}
return true;
}
ConstantRangeSet &operator[](llvm::Instruction *I) { return Ranges[I]; }
const ConstantRangeSet &operator[](llvm::Instruction *I) const {
return Ranges.at(I);
}
bool hasKey(llvm::Instruction *I) const { return Ranges.count(I) != 0; }
};
class Analysis
: public MonotoneFramework<Analysis,
llvm::BasicBlock *,
Element,
ReversePostOrder,
llvm::SmallVector<llvm::BasicBlock *, 2>> {
private:
using Base = MonotoneFramework<Analysis,
llvm::BasicBlock *,
Element,
ReversePostOrder,
llvm::SmallVector<llvm::BasicBlock *, 2>>;
private:
llvm::BasicBlock *Entry;
llvm::LazyValueInfo &LVI;
const llvm::DominatorTree &DT;
std::map<llvm::Instruction *, ConstantRangeSet> InstructionRanges;
std::set<Edge> TargetEdges;
std::set<llvm::BasicBlock *> WhiteList;
public:
Analysis(const llvm::SmallVectorImpl<llvm::BasicBlock *> &RPOT,
llvm::LazyValueInfo &LVI,
const llvm::DominatorTree &DT,
const std::vector<llvm::Instruction *> &TargetInstructions,
const std::vector<Edge> &TargetEdges) :
Base(RPOT), Entry(RPOT[0]), LVI(LVI), DT(DT) {
using namespace llvm;
registerExtremal(Entry);
for (Instruction *I : TargetInstructions) {
if (auto *Ty = dyn_cast<IntegerType>(I->getType())) {
InstructionRanges[I] = { ConstantRange(Ty->getIntegerBitWidth(),
true) };
}
}
for (const Edge &E : TargetEdges) {
this->TargetEdges.insert(E);
}
for (BasicBlock *BB : RPOT) {
WhiteList.insert(BB);
}
}
Element extremalValue(llvm::BasicBlock *) const { return Element(); }
void assertLowerThanOrEqual(const Element &A, const Element &B) const {
revng_assert(A.lowerThanOrEqual(B));
}
DefaultInterrupt<Element> transfer(llvm::BasicBlock *BB) {
if (TargetEdges.count({ BB, nullptr }) != 0) {
Element Result = *compute(State[BB], BB, nullptr, true);
return DefaultInterrupt<Element>::createInterrupt(Result);
}
return DefaultInterrupt<Element>::createInterrupt(State[BB]);
}
llvm::Optional<Element> handleEdge(const Element &Original,
llvm::BasicBlock *Source,
llvm::BasicBlock *Destination) {
bool IsTargetEdge = TargetEdges.count({ Source, Destination }) != 0;
return compute(Original, Source, Destination, IsTargetEdge);
}
llvm::SmallVector<llvm::BasicBlock *, 2>
successors(llvm::BasicBlock *L, DefaultInterrupt<Element> &I) const {
using namespace llvm;
SmallVector<BasicBlock *, 2> Result;
for (llvm::BasicBlock *Successor : make_range(succ_begin(L), succ_end(L)))
if (WhiteList.count(Successor) != 0)
Result.push_back(Successor);
return Result;
}
size_t
successor_size(llvm::BasicBlock *L, DefaultInterrupt<Element> &I) const {
return successors(L, I).size();
}
const ConstantRangeSet &get(llvm::Instruction *I) const {
return InstructionRanges.at(I);
}
void dump() const debug_function { dump(dbg); }
template<typename T>
void dump(T &Output) const {
for (auto &P : InstructionRanges) {
Output << getName(P.first) << ": ";
P.second.dump(Output);
Output << "\n";
}
}
void dumpFinalState() const { revng_abort(); }
private:
llvm::Optional<Element> compute(const Element &Original,
llvm::BasicBlock *Source,
llvm::BasicBlock *Destination,
bool IsTargetEdge) {
Element Result = Original;
for (auto &P : InstructionRanges) {
llvm::Instruction *I = P.first;
ConstantRangeSet &InstructionRangeSet = P.second;
if (not DT.dominates(I->getParent(), Source))
continue;
unsigned BitWidth = I->getType()->getIntegerBitWidth();
InstructionRangeSet.setWidth(BitWidth);
auto NewRange = llvm::ConstantRange::getFull(BitWidth);
if (Destination == nullptr)
NewRange = LVI.getConstantRange(I, Source);
else
NewRange = LVI.getConstantRangeOnEdge(I, Source, Destination);
bool IsNew = not Result.hasKey(I);
ConstantRangeSet &RangeSet = Result[I];
if (IsNew) {
RangeSet = NewRange;
} else {
RangeSet = RangeSet.intersectWith({ NewRange });
}
// If it's on target edge and the range set is smaller, register it
if (IsTargetEdge)
InstructionRangeSet = RangeSet;
}
return { std::move(Result) };
}
};
} // namespace DisjointRanges
inline bool isPhiLike(llvm::Value *V) {
return (llvm::isa<llvm::PHINode>(V) or llvm::isa<llvm::SelectInst>(V));
}
inline bool isMemory(llvm::Value *V) {
using namespace llvm;
V = skipCasts(V);
return not(isa<GlobalVariable>(V) or isa<AllocaInst>(V));
}
/// \brief An operation producing a result and having a single free operand
struct Operation {
static const unsigned UseSCEV = std::numeric_limits<unsigned>::max();
llvm::User *V;
unsigned FreeOperandIndex;
ConstantRangeSet Range;
range_size_t RangeSize;
Operation() : V(nullptr), FreeOperandIndex(0), Range(), RangeSize(0) {}
Operation(llvm::User *V,
unsigned FreeOperandIndex,
llvm::ConstantRange Range,
range_size_t RangeSize) :
V(V),
FreeOperandIndex(FreeOperandIndex),
Range(Range),
RangeSize(RangeSize) {}
unsigned getBitSize() const {
using namespace llvm;
return cast<IntegerType>(V->getType())->getBitWidth();
}
bool usesSCEV() const { return FreeOperandIndex == UseSCEV; }
void dump(unsigned Indent = 0,
llvm::ScalarEvolution *SCEV = nullptr) const debug_function {
dump(dbg, Indent, SCEV);
}
template<typename T>
void dump(T &Output,
unsigned Indent = 0,
llvm::ScalarEvolution *SCEV = nullptr) const {
std::string Prefix(Indent, ' ');
Output << Prefix << V << "\n";
Output << Prefix;
if (usesSCEV()) {
if (SCEV != nullptr) {
Output << dumpToString(SCEV->getSCEV(V)) << "\n";
} else {
Output << "UsesSCEV\n";
}
} else {
Output << "FreeOperandIndex: " << FreeOperandIndex << "\n";
}
Output << Prefix;
Range.dump(Output);
Output << "\n";
Output << Prefix << "RangeSize: " << RangeSize << "\n";
}
};
/// \brief Class representing an expression on the IR
class Expression {
private:
const llvm::DataLayout &DL;
llvm::ScalarEvolution &SE;
std::vector<Operation> OperationsStack;
unsigned SmallestRangeIndex;
bool PhiIsSmallest;
MaterializedValues Values;
bool Materialized;
public:
using PhiEdges = std::vector<Edge>;
public:
Expression(const llvm::DataLayout &DL, llvm::ScalarEvolution &SE) :
DL(DL), SE(SE) {
reset();
}
void reset() {
SmallestRangeIndex = 0;
PhiIsSmallest = false;
Materialized = false;
OperationsStack.clear();
Values.clear();
}
void dump(unsigned Indent = 0) const debug_function { dump(dbg, Indent); }
template<typename T>
void dump(T &Output, unsigned Indent = 0) const {
std::string Prefix(Indent, ' ');
Output << Prefix << "OperationStack: \n";
unsigned I = 0;
for (const Operation &Op : OperationsStack) {
Output << Prefix << " " << I;
if (I == SmallestRangeIndex)
Output << " [smallest]";
Output << ":\n";
Op.dump(Output, Indent + 4);
++I;
}
Output << "\n";
Output << Prefix << "PhiIsSmallest: " << PhiIsSmallest << "\n";
Output << Prefix << "Values: {";
for (const MaterializedValue &Value : Values) {
Output << " ";
Value.dump(Output);
}
Output << " }\n";
Output << Prefix << "Materialized: " << Materialized << "\n";
}
bool lastIsPhi() const { return isPhiLike(OperationsStack.back().V); }
range_size_t smallestRangeSize() const {
return OperationsStack.at(SmallestRangeIndex).RangeSize;
}
llvm::Value *smallestRangeValue() const {
return OperationsStack.at(SmallestRangeIndex).V;
}
/// Use LVI to build an expression about \p V
///
/// 1. Build a chain of single non-const-operand instructions until you find a
/// phi or a load from a global variable.
/// 2. For each instruction in the chain record the number of possible values
/// according to LVI.
/// 3. Iterate over the chain looking for the instruction associated with the
/// smallest range.
llvm::Instruction *buildExpression(llvm::LazyValueInfo &LVI,
const llvm::DominatorTree &DT,
PhiEdges &Edges,
llvm::Value *V,
llvm::BasicBlock *StopAt) {
using namespace llvm;
revng_log(AVILogger, "Building expression for " << V);
Instruction *Result = nullptr;
reset();
User *U = cast<User>(V);
do {
revng_log(AVILogger, " Considering " << U);
//
// Identify the free operand
//
unsigned Index = 0;
unsigned NextIndex = 0;
Value *Next = nullptr;
unsigned BitWidth = getTypeSize(DL, U->getType());
auto Range = ConstantRange::getFull(BitWidth);
range_size_t RangeSize = MaxMaterializedValues;
auto *I = dyn_cast<Instruction>(U);
if (auto *Call = dyn_cast<CallInst>(U)) {
if (Function *Callee = Call->getCalledFunction()) {
if (Callee->getIntrinsicID() == Intrinsic::bswap) {
// Handle bswap intrinsic
Use &FirstArg = Call->getArgOperandUse(0);
Next = FirstArg.get();
NextIndex = FirstArg.getOperandNo();
}
}
} else if (I != nullptr) {
// We found an instruction
for (Value *Operand : U->operands()) {
if (not isa<Constant>(Operand)) {
if (Next != nullptr) {
Next = nullptr;
break;
}
NextIndex = Index;
Next = Operand;
}
Index++;
}
if (Next == nullptr and I->getNumOperands() > 1) {
// The instruction has more than one free operand, let's give SCEV a
// shot
Next = getUniqueUnknown(SE, SE.getSCEV(I));
if (Next == I)
Next = nullptr;
NextIndex = Operation::UseSCEV;
}
} else if (auto *C = dyn_cast<ConstantInt>(U)) {
RangeSize = 1;
Range = ConstantRange(C->getValue());
} else if (isa<ConstantPointerNull>(U) or isa<UndefValue>(U)) {
RangeSize = 0;
Range = ConstantRange(BitWidth, false);
} else {
revng_assert(isa<Constant>(U));
Next = U->getOperand(0);
}
// Push on the stack
OperationsStack.push_back({ U, NextIndex, Range, RangeSize });
if (isPhiLike(U)) {
Result = I;
Next = nullptr;
} else if (auto *Load = dyn_cast<LoadInst>(U)) {
Next = Load->getPointerOperand();
if (not isMemory(skipCasts(Load->getPointerOperand())))
Next = nullptr;
}
U = cast_or_null<User>(Next);
} while (U != nullptr);
auto IsInteresting = [](const Operation &O) -> llvm::Instruction * {
if (O.Range.isFullSet())
if (auto *I = dyn_cast<Instruction>(O.V))
if (isa<IntegerType>(I->getType()))
return I;
return nullptr;
};
std::vector<Instruction *> Targets;
for (const Operation &O : OperationsStack) {
if (auto *I = IsInteresting(O)) {
Targets.push_back(I);
}
}
if (Targets.size() != 0) {
BasicBlock *StartBB = Targets.back()->getParent();
const Edge &FirstEdge = Edges.front();
revng_assert(FirstEdge.End == nullptr);
BasicBlock *EndBB = FirstEdge.Start;
BasicBlock *LimitedStartBB = StartBB;
if (not DT.dominates(StopAt, LimitedStartBB))
LimitedStartBB = StopAt;
SmallPtrSet<BasicBlock *, 4> IgnoreList = { StopAt };
auto Reachable = nodesBetweenReverse(EndBB, LimitedStartBB, &IgnoreList);
for (Instruction *I : Targets)
Reachable.insert(I->getParent());
Reachable.insert(EndBB);
// Note: ordering in reverse post order is more costly than beneficial
Reachable.erase(StartBB);
SmallVector<BasicBlock *, 8> ReachableVector{ StartBB };
for (BasicBlock *BB : Reachable)
ReachableVector.push_back(BB);
DisjointRanges::Analysis DR(ReachableVector, LVI, DT, Targets, Edges);
DR.initialize();
DR.run();
for (Operation &O : OperationsStack) {
if (auto *I = IsInteresting(O)) {
O.Range = DR.get(I);
AVILogger << I << ": ";
O.Range.dump(AVILogger);
AVILogger << DoLog;
O.RangeSize = O.Range.size().getLimitedValue();
}
}
}
for (const Operation &O : OperationsStack) {
// Get the LVI and record if it's the smallest
if (OperationsStack[SmallestRangeIndex].RangeSize > O.RangeSize) {
SmallestRangeIndex = &O - &*OperationsStack.begin();
}
}
return Result;
}
/// \brief Materialize all the values in this expression
template<typename MemoryOracle>
MaterializedValues materialize(MemoryOracle &MO) {
using namespace llvm;
revng_assert(not Materialized);
Materialized = true;
IntegerType *SmallestType = nullptr;
for (const Operation &Operation : OperationsStack) {
if (auto *ITy = dyn_cast<IntegerType>(Operation.V->getType())) {
if (SmallestType == nullptr
or SmallestType->getBitWidth() > ITy->getBitWidth()) {
SmallestType = ITy;
}
}
}
range_size_t WorstCase = MaxMaterializedValues;
if (SmallestType != nullptr)
WorstCase = std::min(SmallestType->getBitMask(), WorstCase);
Type *SmallestOperationType = nullptr;
{
const auto &SmallestOperation = OperationsStack.at(SmallestRangeIndex);
SmallestOperationType = SmallestOperation.V->getType();
if (not PhiIsSmallest) {
// Materialize all the values, so we can process them one by one
revng_assert(Values.size() == 0);
if (SmallestOperation.RangeSize >= WorstCase)
return {};
Values.resize(SmallestOperation.RangeSize);
auto It = SmallestOperation.Range.begin();
const auto End = SmallestOperation.Range.end();
for (MaterializedValue &Entry : Values) {
revng_assert(It != End);
Entry = { *It };
++It;
}
} else {
// The Values vector has already been initialized
revng_assert(lastIsPhi());
}
OperationsStack.resize(SmallestRangeIndex);
}
// Process one value at a time
for (MaterializedValue &Entry : Values) {
using CI = ConstantInt;
using CE = ConstantExpr;
if (AVILogger.isEnabled()) {
AVILogger << "Now materializing ";
Entry.dump(AVILogger);
AVILogger << DoLog;
}
llvm::Optional<llvm::StringRef> SymbolName;
Constant *Current = nullptr;
{
auto Value = Entry.value();
if (SmallestOperationType->isIntegerTy()) {
Current = CI::get(SmallestOperationType, Value);
} else if (SmallestOperationType->isPointerTy()) {
auto &C = SmallestOperationType->getContext();
Current = CI::get(DL.getIntPtrType(C), Value);
Current = CE::getIntToPtr(Current, SmallestOperationType);
} else {
revng_abort();
}
}
// Materialize the value I through the operations stack
auto It = OperationsStack.rbegin();
auto End = OperationsStack.rend();
if (It != End) {
auto Range = make_range(It, End);
for (const Operation &Op : Range) {
if (AVILogger.isEnabled()) {
AVILogger << " Processing:";
Op.dump(AVILogger, 4);
AVILogger << DoLog;
}
// After we get a symbol name we only track casts, additions and
// subtractions
auto *I = dyn_cast<Instruction>(Op.V);
Module *M = nullptr;
LLVMContext *Context = nullptr;
const DataLayout *DL = nullptr;
if (I != nullptr) {
M = I->getParent()->getParent()->getParent();
Context = &M->getContext();
DL = &M->getDataLayout();
}
if (SymbolName
and not(I != nullptr
and (I->isCast() or I->getOpcode() == Instruction::Add
or I->getOpcode() == Instruction::Sub))) {
return {};
}
if (auto *C = dyn_cast<Constant>(Op.V)) {
Current = C;
} else if (auto *C = dyn_cast<Constant>(Op.V)) {
revng_assert(Op.V->getNumOperands() == 1);
Current = cast<Constant>(C->getOperand(0));
} else if (auto *Load = dyn_cast<LoadInst>(Op.V)) {
revng_assert(isMemory(skipCasts(Load->getPointerOperand())));
MaterializedValue Loaded = MO.load(Current);
if (AVILogger.isEnabled()) {
AVILogger << " MemoryOracle says its ";
Loaded.dump(AVILogger);
AVILogger << DoLog;
}
if (not Loaded.isValid()) {
// Couldn't read memory, bail out
return {};
}
if (Loaded.hasSymbol())
SymbolName = Loaded.symbolName();
Type *LoadedType = Load->getType();
if (LoadedType->isPointerTy()) {
Current = CI::get(DL->getIntPtrType(*Context), Loaded.value());
Current = CE::getIntToPtr(Current, LoadedType);
} else {
Current = CI::get(cast<IntegerType>(LoadedType), Loaded.value());
}
} else if (auto *Call = dyn_cast<CallInst>(Op.V)) {
Function *Callee = Call->getCalledFunction();
revng_assert(Callee != nullptr
&& Callee->getIntrinsicID() == Intrinsic::bswap);
using CI = ConstantInt;
Current = CI::get(*Context,
cast<CI>(Current)->getValue().byteSwap());
} else if (I != nullptr) {
if (Op.usesSCEV()) {
Current = replaceAllUnknownsWith(SE,
SE.getSCEV(I),
cast<ConstantInt>(Current));
} else {
// Build operands list patching the free operand
SmallVector<Constant *, 4> Operands;
unsigned Index = 0;
for (Value *Operand : Op.V->operands()) {
if (auto *ConstantOperand = dyn_cast<Constant>(Operand)) {
Operands.push_back(ConstantOperand);
} else {
revng_assert(Index == Op.FreeOperandIndex);
Operands.push_back(Current);
}
Index++;
}
Current = ConstantFoldInstOperands(I,
Operands,
MO.getDataLayout());
}
revng_assert(Current != nullptr);
} else {
revng_abort();
}
}
}
// Ignore undef
if (isa<UndefValue>(Current))
return {};
APInt Value(getTypeSize(DL, Current->getType()), 0);
if (not Current->isNullValue()) {
if (auto *CI = dyn_cast<ConstantInt>(skipCasts(Current))) {
Value = CI->getValue();
} else {
skipCasts(Current)->dump();
revng_abort("Unexpected Constant");
}
}
if (SymbolName)
Entry = { *SymbolName, Value };
else
Entry = { Value };
}
return std::move(Values);
}
void setPhiValues(MaterializedValues PhiValues) {
const Operation &SmallestOperation = OperationsStack.at(SmallestRangeIndex);
revng_assert(lastIsPhi());
revng_assert(PhiValues.size() < SmallestOperation.RangeSize);
Values = std::move(PhiValues);
PhiIsSmallest = true;
SmallestRangeIndex = OperationsStack.size() - 1;
}
};
/// \brief Context for processing a phi node
class PhiProcess {
public:
/// The considered Phi
llvm::Instruction *Phi;
/// Index of the next incoming value of Phi to handle
unsigned NextIncomingIndex;
/// Set of possible values for this phi
MaterializedValues Values;
/// Expression representing the value of the current incoming value of the phi
Expression Expr;
/// Processing of the current index (NextIncomingIndex - 1) is in progress
bool Unfinished;
/// Size of the smallest range at the previous level
range_size_t UpperBound;
/// Did we exceed MaxMaterializedValues?
bool TooLarge;
public:
PhiProcess(const llvm::DataLayout &DL,
llvm::ScalarEvolution &SE,
llvm::Instruction *Phi,
range_size_t UpperBound) :
Phi(Phi),
NextIncomingIndex(0),
Expr(DL, SE),
Unfinished(false),
UpperBound(UpperBound),
TooLarge(false) {
revng_assert(isPhiLike(Phi));
}
void dump() const debug_function { dump(dbg); }
template<typename T>
void dump(T &Output) const {
Output << "Phi: " << Phi << "\n";
Output << "NextIncomingIndex: " << NextIncomingIndex << "\n";
Output << "Values: {";
for (const MaterializedValue &Value : Values) {
Output << " ";
Value.dump(Output);
}
Output << " }\n";
Output << "Expr:\n";
Expr.dump(Output, 2);
Output << "Unfinished: " << Unfinished << "\n";
Output << "UpperBound: " << UpperBound << "\n";
Output << "TooLarge: " << TooLarge << "\n";
}
};
/// \brief Analyis to associate to each value a ConstantRangeSet using
/// LazyValueInfo
///
/// \tparam MemoryOracle the type of the class used to produce obtain the result
/// of memory accesses from constant addresses.
template<typename MemoryOracle>
class AdvancedValueInfo {
private:
llvm::LazyValueInfo &LVI;
llvm::ScalarEvolution &SE;
const llvm::DominatorTree &DT;
MemoryOracle &MO;
llvm::BasicBlock *StopAt;
public:
AdvancedValueInfo(llvm::LazyValueInfo &LVI,
llvm::ScalarEvolution &SE,
const llvm::DominatorTree &DT,
MemoryOracle &MO,
llvm::BasicBlock *StopAt) :
LVI(LVI), SE(SE), DT(DT), MO(MO), StopAt(StopAt) {}
MaterializedValues explore(llvm::BasicBlock *BB, llvm::Value *V);
};
template<class MemoryOracle>
MaterializedValues
AdvancedValueInfo<MemoryOracle>::explore(llvm::BasicBlock *BB, llvm::Value *V) {
using namespace llvm;
const llvm::DataLayout &DL = getModule(BB)->getDataLayout();
revng_log(AVILogger, "Exploring " << V << " in " << BB);
// Create a fake Phi for the initial entry
PHINode *FakePhi = PHINode::Create(V->getType(), 1);
FakePhi->addIncoming(V, BB);
struct DeleteValueOnReturn {
Instruction *I;
~DeleteValueOnReturn() {
if (I->getParent() != nullptr)
I->eraseFromParent();
else
I->deleteValue();
}
};
DeleteValueOnReturn X{ FakePhi };
std::set<Instruction *> VisitedPhis;
std::vector<PhiProcess> PendingPhis{
{ DL, SE, FakePhi, MaxMaterializedValues }
};
Expression::PhiEdges Edges;
while (true) {
PhiProcess &Current = PendingPhis.back();
if (AVILogger.isEnabled()) {
AVILogger << "Processing ";
Current.dump(AVILogger);
AVILogger << DoLog;
}
Instruction *NextPhi = nullptr;
if (not Current.Unfinished) {
// No processing in progress, proceed
unsigned NextIndex = Current.NextIncomingIndex;
Value *NextValue = nullptr;
Edge NewEdge;
if (auto *Phi = dyn_cast<PHINode>(Current.Phi)) {
NextValue = Phi->getIncomingValue(NextIndex);
NewEdge = { Phi->getIncomingBlock(NextIndex), Phi->getParent() };
} else if (auto *Select = dyn_cast<SelectInst>(Current.Phi)) {
NextValue = Select->getOperand(1 + NextIndex);
NewEdge = { Select->getParent(), nullptr };
} else {
revng_abort();
}
Edges.push_back(NewEdge);
NextPhi = Current.Expr.buildExpression(LVI, DT, Edges, NextValue, StopAt);
Current.NextIncomingIndex++;
}
// Don't enter in loops
if (VisitedPhis.count(NextPhi) != 0)
NextPhi = nullptr;
if (NextPhi != nullptr) {
VisitedPhis.insert(NextPhi);
Current.Unfinished = true;
// The last node of the Expression we just build is a phi node,
// we have to suspend processing and proceed towards it
PendingPhis.emplace_back(DL,
SE,
NextPhi,
Current.Expr.smallestRangeSize());
} else {
// The last node is not a phi, we're done on this incoming value of
// the phi
// Drop this edge from the list of edges
Edges.pop_back();
MaterializedValues Result;
size_t UpperBound = Current.Expr.smallestRangeSize();
bool IsSmallerThanUpperBound = UpperBound < Current.UpperBound;
bool PhiDone = not IsSmallerThanUpperBound;
if (IsSmallerThanUpperBound) {
// Materialize the current expression
Result = std::move(Current.Expr.materialize<MemoryOracle>(MO));
// Reset the unfinished flag
Current.Unfinished = false;
range_size_t NewSize = Current.Values.size() + Result.size();
if (Current.TooLarge or NewSize > MaxMaterializedValues) {
Current.TooLarge = true;
Current.Values.clear();
IsSmallerThanUpperBound = false;
} else {
// Merge results in Current.Values
Current.Values.insert(Current.Values.end(),
Result.begin(),
Result.end());
}
unsigned IncomingCount = 0;
if (auto *Phi = dyn_cast<PHINode>(Current.Phi)) {
IncomingCount = Phi->getNumIncomingValues();
} else if (isa<SelectInst>(Current.Phi)) {
IncomingCount = 2;
} else {
revng_abort();
}
if (Current.NextIncomingIndex == IncomingCount) {
// We're done with this phi
PhiDone = true;
// Save and deduplicate the result
Result = std::move(Current.Values);
std::sort(Result.begin(), Result.end());
auto LastIt = std::unique(Result.begin(), Result.end());
Result.erase(LastIt, Result.end());
IsSmallerThanUpperBound = Result.size() < Current.UpperBound;
}
}
if (PhiDone) {
if (PendingPhis.size() == 1)
return Result;
// Pop
PendingPhis.pop_back();
// Inform the new top of the stack about the results
revng_assert(PendingPhis.back().Unfinished);
if (IsSmallerThanUpperBound)
PendingPhis.back().Expr.setPhiValues(std::move(Result));
}
}
}
revng_abort();
}