#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #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(T)) return IntegerTy->getBitWidth(); else if (auto *PtrTy = dyn_cast(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: case scPtrToInt: break; case scUnknown: if (UniqueUnknown == nullptr) { UniqueUnknown = cast(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 { 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(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 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; 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> { private: using Base = MonotoneFramework>; private: llvm::BasicBlock *Entry; llvm::LazyValueInfo &LVI; const llvm::DominatorTree &DT; std::map InstructionRanges; std::set TargetEdges; std::set WhiteList; public: Analysis(const llvm::SmallVectorImpl &RPOT, llvm::LazyValueInfo &LVI, const llvm::DominatorTree &DT, const std::vector &TargetInstructions, const std::vector &TargetEdges) : Base(RPOT), Entry(RPOT[0]), LVI(LVI), DT(DT) { using namespace llvm; registerExtremal(Entry); for (Instruction *I : TargetInstructions) { if (auto *Ty = dyn_cast(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 transfer(llvm::BasicBlock *BB) { if (TargetEdges.count({ BB, nullptr }) != 0) { Element Result = *compute(State[BB], BB, nullptr, true); return DefaultInterrupt::createInterrupt(Result); } return DefaultInterrupt::createInterrupt(State[BB]); } llvm::Optional 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 successors(llvm::BasicBlock *L, DefaultInterrupt &I) const { using namespace llvm; SmallVector 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 &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 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 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->getTerminator()); 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(V) or llvm::isa(V)); } inline bool isMemory(llvm::Value *V) { using namespace llvm; V = skipCasts(V); return not(isa(V) or isa(V)); } /// \brief An operation producing a result and having a single free operand struct Operation { static const unsigned UseSCEV = std::numeric_limits::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(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 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 OperationsStack; unsigned SmallestRangeIndex; bool PhiIsSmallest; MaterializedValues Values; bool Materialized; public: using PhiEdges = std::vector; 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 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(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(U); if (auto *Call = dyn_cast(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(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(U)) { RangeSize = 1; Range = ConstantRange(C->getValue()); } else if (isa(U) or isa(U)) { RangeSize = 0; Range = ConstantRange(BitWidth, false); } else { revng_assert(isa(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(U)) { Next = Load->getPointerOperand(); if (not isMemory(skipCasts(Load->getPointerOperand()))) Next = nullptr; } U = cast_or_null(Next); } while (U != nullptr); auto IsInteresting = [](const Operation &O) -> llvm::Instruction * { if (O.Range.isFullSet()) if (auto *I = dyn_cast(O.V)) if (isa(I->getType())) return I; return nullptr; }; std::vector 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 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 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 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(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 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(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(Op.V)) { Current = C; } else if (auto *C = dyn_cast(Op.V)) { revng_assert(Op.V->getNumOperands() == 1); Current = cast(C->getOperand(0)); } else if (auto *Load = dyn_cast(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(LoadedType), Loaded.value()); } } else if (auto *Call = dyn_cast(Op.V)) { Function *Callee = Call->getCalledFunction(); revng_assert(Callee != nullptr && Callee->getIntrinsicID() == Intrinsic::bswap); using CI = ConstantInt; Current = CI::get(*Context, cast(Current)->getValue().byteSwap()); } else if (I != nullptr) { if (Op.usesSCEV()) { Current = replaceAllUnknownsWith(SE, SE.getSCEV(I), cast(Current)); } else { // Build operands list patching the free operand SmallVector Operands; unsigned Index = 0; for (Value *Operand : Op.V->operands()) { if (auto *ConstantOperand = dyn_cast(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(Current)) return {}; APInt Value(getTypeSize(DL, Current->getType()), 0); if (not Current->isNullValue()) { if (auto *CI = dyn_cast(skipCasts(Current))) { Value = CI->getValue(); } else { skipCasts(Current)->dump(); revng_abort("Unexpected Constant"); } } if (SymbolName) { revng_assert(not llvm::StringRef(*SymbolName).contains('\0')); 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 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 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 MaterializedValues AdvancedValueInfo::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) eraseFromParent(I); else I->deleteValue(); } }; DeleteValueOnReturn X{ FakePhi }; std::set VisitedPhis; std::vector 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(Current.Phi)) { NextValue = Phi->getIncomingValue(NextIndex); NewEdge = { Phi->getIncomingBlock(NextIndex), Phi->getParent() }; } else if (auto *Select = dyn_cast(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(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(Current.Phi)) { IncomingCount = Phi->getNumIncomingValues(); } else if (isa(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(); }