/// \file Element.cpp /// \brief // // This file is distributed under the MIT License. See LICENSE.md for details. // // Local libraries includes #include "revng/Support/Debug.h" // Local includes #include "Element.h" using llvm::Module; Logger<> SaDiffLog("sa-diff"); RunningStatistics AddressSpaceSizeStats("AddressSpaceSizeStats"); Logger<> SaVerboseLog("sa-verbose"); static size_t combineHash(size_t A, size_t B) { return (A << 1 | A >> 31) ^ B; } namespace StackAnalysis { size_t ASID::hash() const { return std::hash()(ID); } bool ASSlot::lowerThanOrEqual(const ASSlot &Other) const { return cmp(Other, nullptr) == 0; } template unsigned ASSlot::cmp(const ASSlot &Other, const Module *M) const { revng_assert(!this->isInvalid() and !Other.isInvalid()); LoggerIndent<> Y(SaDiffLog); bool Result = not(AS.lowerThanOrEqual(Other.AS) && Offset == Other.Offset); if (SaDiffLog.isEnabled() && Result && Diff) { Other.dump(M, SaDiffLog); SaDiffLog << " does not contain "; dump(M, SaDiffLog); SaDiffLog << DoLog; } return Result; } size_t ASSlot::hash() const { return combineHash(std::hash()(AS), std::hash()(Offset)); } namespace Intraprocedural { bool Value::lowerThanOrEqual(const Value &Other) const { return cmp(Other, nullptr) == 0; } template unsigned Value::cmp(const Value &Other, const Module *M) const { LoggerIndent<> Y(SaDiffLog); unsigned Result = 0; if (hasDirectContent() && Other.hasDirectContent()) { // Force equality // TODO: is this correct? shouldn't we assert DirectContent == // Other.DirectContent? ROA((DirectContent.cmp(Other.DirectContent, M)), { revng_log(SaDiffLog, "DirectContent vs DirectContent"); }); } // hasDirectContent() && !Other.hasDirectContent() is fine // Other has direct content and we don't, it's more specific than us ROA(!hasDirectContent() && Other.hasDirectContent(), { revng_log(SaDiffLog, "RHS has direct content, LHS doesn't"); }); // Losing the name is fine, acquiring it is not ROA(!hasTag() && Other.hasTag(), { revng_log(SaDiffLog, "RHS has tag, LHS doesn't"); }); ROA(hasTag() && Other.hasTag() && !TheTag.lowerThanOrEqual(Other.TheTag), { revng_log(SaDiffLog, "Tag"); }); return Result; } size_t Value::hash() const { size_t Result = 0; Result = combineHash(Result, hasDirectContent()); if (hasDirectContent()) Result = combineHash(Result, std::hash()(*directContent())); else Result = combineHash(Result, Result); Result = combineHash(Result, hasTag()); if (hasTag()) Result = combineHash(Result, std::hash()(*tag())); else Result = combineHash(Result, Result); return Result; } bool AddressSpace::lowerThanOrEqual(const AddressSpace &Other) const { return cmp(Other, nullptr) == 0; } template unsigned AddressSpace::cmp(const AddressSpace &Other, const Module *M) const { LoggerIndent<> Y(SaDiffLog); unsigned Result = 0; for (auto &P : ASOContent) { auto It = Other.ASOContent.find(P.first); // Check if Other has it if (It != Other.ASOContent.end()) { // Check the actual value ROA((P.second.cmp(It->second, M)), { slot(P.first).dump(M, SaDiffLog); SaDiffLog << DoLog; }); } } for (auto &P : Other.ASOContent) { auto It = ASOContent.find(P.first); // TODO: assert this matters in the PruneLog ROA(It == ASOContent.end() && P.second.hasDirectContent(), { slot(P.first).dump(M, SaDiffLog); SaDiffLog << " is absent in the LHS and has direct content on the"; revng_log(SaDiffLog, " RHS"); }); } return Result; } size_t AddressSpace::hash() const { size_t Result = 0; for (auto &P : ASOContent) { Result = combineHash(Result, P.first); Result = combineHash(Result, std::hash()(P.second)); } return Result; } std::set Element::collectSlots(int32_t CSVCount) const { ASID CPU = ASID::cpuID(); std::set SlotsPool; if (State.size() > CPU.id()) for (auto &P : State[CPU.id()].ASOContent) if (P.first < CSVCount) SlotsPool.insert(ASSlot::create(CPU, P.first)); return SlotsPool; } template unsigned Element::cmp(const Element &Other, const Module *M) const; bool Element::lowerThanOrEqual(const Element &Other) const { return cmp(Other, nullptr) == 0; } template unsigned Element::cmp(const Element &Other, const Module *M) const { LoggerIndent<> Y(SaDiffLog); unsigned Result = 0; if (Other.State.size() == 0) return 0; if (State.size() == 0) return 1; revng_assert(State.size() == Other.State.size()); size_t TotalASCount = State.size(); for (unsigned I = 0; I < TotalASCount; I++) { ROA((State[I].cmp(Other.State[I], M)), { ASID(I).dump(SaDiffLog); SaDiffLog << DoLog; }); } // TODO: we're ignoring FrameSizeAtCallSite and ABI return Result; } size_t Element::hash() const { size_t Result = 0; for (const AddressSpace &AS : State) Result = combineHash(Result, std::hash()(AS)); return Result; } Element &Element::combine(const Element &Other) { if (isBottom()) { *this = Other.copy(); return *this; } revng_assert(State.size() == Other.State.size()); for (unsigned I = 0; I < State.size(); I++) mergeASState(State[I], Other.State[I]); return *this; } void Element::cleanup() { for (AddressSpace &AS : State) { for (auto It = AS.ASOContent.begin(); It != AS.ASOContent.end(); /**/) { if (const ASSlot *TheTag = It->second.tag()) { if (*TheTag == ASSlot::create(AS.ID, It->first)) { It = AS.ASOContent.erase(It); continue; } } It++; } } } void Element::apply(const Element &Other) { revng_assert(State.size() == Other.State.size()); ASID CPU = ASID::cpuID(); const AddressSpace &OtherCPU = Other.State[CPU.id()]; for (auto &P : OtherCPU.ASOContent) store(Value::fromSlot(CPU, P.first), P.second); } std::set Element::computeCalleeSavedSlots() const { std::set Result; // Look in the stack leftovers uint32_t CPUID = ASID::cpuID().id(); uint32_t StackID = ASID::stackID().id(); if (State.size() > StackID and State.size() > CPUID) { std::set StackLeftovers; for (auto &P : State[StackID].ASOContent) { // Do we have direct content with a name? if (const ASSlot *T = P.second.tag()) { // Is the tag referreing to a CSV? if (T->addressSpace() == ASID::cpuID()) StackLeftovers.insert(*T); } } for (auto &P : State[CPUID].ASOContent) { // Do we have direct content with a name? if (const ASSlot *T = P.second.tag()) { // Is the name the same as the current slot? ASSlot Slot = ASSlot::create(ASID::cpuID(), P.first); if (*T == Slot and StackLeftovers.count(Slot) != 0) Result.insert(Slot); } } } return Result; } void Element::mergeASState(AddressSpace &ThisState, const AddressSpace &OtherState) { // The following implementation can be easily replaced by any other // implementation using a data structure allowing to iterate over a sorted // pair of pairs. In particular, instead of a std::map we could use // a sorted std::vector of pairs. // Iterate in parallel auto ThisIt = ThisState.ASOContent.begin(); auto ThisEndIt = ThisState.ASOContent.end(); auto OtherIt = OtherState.ASOContent.begin(); auto OtherEndIt = OtherState.ASOContent.end(); std::vector> NewEntries; bool ThisDone = ThisIt == ThisEndIt; bool OtherDone = OtherIt == OtherEndIt; while (!ThisDone || !OtherDone) { Value *ThisContent = nullptr; const Value *OtherContent = nullptr; Value TmpContent = Value::empty(); if (ThisDone || (!OtherDone && ThisIt->first > OtherIt->first)) { // Only Other has the current offset: create a new default entry for // delayed appending in this and merge it with OtherContent auto ASO = ASSlot::create(ThisState.id(), OtherIt->first); NewEntries.emplace_back(OtherIt->first, ThisState.load(ASO)); ThisContent = &NewEntries.back().second; OtherContent = &OtherIt->second; OtherIt++; } else if (OtherDone || (!ThisDone && OtherIt->first > ThisIt->first)) { // Only this has the current offset: create a default OtherContent and // merge with ThisContent auto ASO = ASSlot::create(OtherState.id(), ThisIt->first); TmpContent = OtherState.load(ASO); ThisContent = &ThisIt->second; OtherContent = &TmpContent; ThisIt++; } else { // Both have the current offset: update ThisContent with OtherContent revng_assert(ThisIt != ThisEndIt && OtherIt != OtherEndIt); revng_assert(ThisIt->first == OtherIt->first); ThisContent = &ThisIt->second; OtherContent = &OtherIt->second; ThisIt++; OtherIt++; } // Perform the merge ThisContent->combine(*OtherContent); ThisDone = ThisIt == ThisEndIt; OtherDone = OtherIt == OtherEndIt; } for (std::pair &P : NewEntries) ThisState.ASOContent[P.first] = P.second; // Cleanup phase for (auto It = ThisState.ASOContent.begin(); It != ThisState.ASOContent.end(); /**/) { if (!It->second.hasDirectContent() && !It->second.hasTag()) It = ThisState.eraseASO(It); else It++; } } } // namespace Intraprocedural } // namespace StackAnalysis