Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions include/tsar/Analysis/Memory/AssumptionInfo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//===- AssumptionInfo.h ----- Assumption Information ------------*- C++ -*-===//
//
// Traits Static Analyzer (SAPFOR)
//
// Copyright 2022 DVM System Group
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//===----------------------------------------------------------------------===//
//
// This file provides assumption information for memory locations with variable
// bounds.
//
//===----------------------------------------------------------------------===/

#ifndef TSAR_ASSUMPTION_INFO_H
#define TSAR_ASSUMPTION_INFO_H

#include "tsar/Analysis/Memory/Passes.h"
#include <llvm/ADT/DenseMap.h>
#include <llvm/Pass.h>
#include <bcl/utility.h>

namespace llvm {
class Value;
}

namespace tsar {
typedef llvm::Optional<int64_t> AssumptionBound;
struct AssumptionBounds {
AssumptionBound Lower = llvm::None;
AssumptionBound Upper = llvm::None;
};

typedef llvm::DenseMap<llvm::Value *, AssumptionBounds> AssumptionMap;
}

namespace llvm {
class AssumptionInfoPass : public FunctionPass, private bcl::Uncopyable {
public:
static char ID;

AssumptionInfoPass() : FunctionPass(ID) {
initializeAssumptionInfoPassPass(*PassRegistry::getPassRegistry());
}

bool runOnFunction(Function &F) override;
void getAnalysisUsage(AnalysisUsage &AU) const override;
void releaseMemory() override { mAM.clear(); }

const tsar::AssumptionMap & getAssumptionMap() const {
return mAM;
}
private:
tsar::AssumptionMap mAM;
};
}

#endif //TSAR_ASSUMPTION_INFO_H
89 changes: 80 additions & 9 deletions include/tsar/Analysis/Memory/DefinedMemory.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#include <llvm/Pass.h>

namespace llvm {
class AssumptionCache;
class DominatorTree;
class Value;
class Instruction;
Expand Down Expand Up @@ -92,6 +93,11 @@ class DefUseSet {
/// Set of memory locations.
typedef MemorySet<MemoryLocationRange> LocationSet;

/// Set of data flow nodes with which memory locations from the
/// ExplicitAccesses set are associated.
typedef llvm::SmallDenseMap<const llvm::Value *,
llvm::SmallDenseSet<const DFNode *>> ExplicitNodeMap;

/// Returns set of the must defined locations.
const LocationSet & getDefs() const { return mDefs; }

Expand Down Expand Up @@ -209,6 +215,25 @@ class DefUseSet {
return mUses.insert(Uses.begin(), Uses.end());
}

/// Replaced collapsed memory locations with its expanded forms which are
/// of kind `Default`.
void summarizeCollapsedLocations() {
LocationSet DestDefs, DestMayDefs, DestUses;
for (auto &Loc : mDefs) {
if (Loc.Kind == MemoryLocationRange::LocKind::Collapsed)
DestMayDefs.insert(Loc.expand());
else
DestDefs.insert(Loc);
}
for (auto &Loc : mMayDefs)
DestMayDefs.insert(Loc.expand());
for (auto &Loc : mUses)
DestUses.insert(Loc.expand());
mDefs = std::move(DestDefs);
mMayDefs = std::move(DestMayDefs);
mUses = std::move(DestUses);
}

/// Returns locations accesses to which are performed explicitly.
///
/// For example, if p = &x and to access x, *p is used, let us assume that
Expand Down Expand Up @@ -367,6 +392,42 @@ class DefUseSet {
.second;
}

/// Inserts a data flow node associated with a collapsable explicit access
/// ExplLoc. ColLoc is a collapsed memory location created from the ExplLoc.
void addNodeOfCollapsableExplicitAccess(
const MemoryLocationRange &ExplLoc,
const MemoryLocationRange &ColLoc,
const DFNode *N) {
assert(ExplLoc.Ptr && "Location pointer must not be null!");
assert(ColLoc.Kind == MemoryLocationRange::LocKind::Collapsed &&
"Location must be of a `collapsed` kind!");
mCollapsableExplicitAccesses.try_emplace(ExplLoc.Ptr).first->second.insert(N);
}

/// Returns a map of collapsable explicit accesses with associated data flow
/// nodes.
const ExplicitNodeMap & getCollapsableExplicitAccesses() const {
return mCollapsableExplicitAccesses;
}

/// Specifies that there are a collapsable explicit access to all collapsable
/// explicit accesses from a specified map.
void addCollapsableExplicitAccesses(const ExplicitNodeMap &ENM) {
for (auto Itr = ENM.begin(); Itr != ENM.end(); ++Itr) {
auto &NodeList = Itr->second;
mCollapsableExplicitAccesses.try_emplace(Itr->first).
first->second.insert(NodeList.begin(), NodeList.end());
}
}

/// Returns a pointer to a set of data flow nodes associated with the memory
/// location Loc if it is collabsable. Returns `nullptr` otherwise.
const llvm::SmallDenseSet<const DFNode *> *
getNodesOfCollapsableExplicitAccess(const MemoryLocationRange &Loc) const {
auto Itr = mCollapsableExplicitAccesses.find(Loc.Ptr);
return Itr == mCollapsableExplicitAccesses.end() ? nullptr : &Itr->second;
}

private:
LocationSet mDefs;
LocationSet mMayDefs;
Expand All @@ -377,6 +438,7 @@ class DefUseSet {
InstructionSet mExplicitUnknowns;
InstructionSet mAddressUnknowns;
TransitiveMap mAddressTransitives;
ExplicitNodeMap mCollapsableExplicitAccesses;
};

/// This presents information whether a location has definition after a node
Expand Down Expand Up @@ -430,29 +492,32 @@ class ReachDFFwk : private bcl::Uncopyable {
ReachDFFwk(AliasTree &AT, llvm::TargetLibraryInfo &TLI,
const DFRegionInfo &DFI, const llvm::DominatorTree &DT,
const DelinearizeInfo &DI, llvm::ScalarEvolution &SE,
const llvm::DataLayout &DL, const GlobalOptions &GO,
DefinedMemoryInfo &DefInfo) :
const llvm::DataLayout &DL, const AssumptionMap &AM,
const GlobalOptions &GO, DefinedMemoryInfo &DefInfo) :
mAliasTree(&AT), mTLI(&TLI), mRegionInfo(&DFI),
mDT(&DT), mDI(&DI), mSE(&SE), mDL(&DL), mGO(&GO), mDefInfo(&DefInfo) {}
mDT(&DT), mDI(&DI), mSE(&SE), mDL(&DL), mAM(&AM), mGO(&GO),
mDefInfo(&DefInfo) {}

/// Creates data-flow framework.
ReachDFFwk(AliasTree &AT, llvm::TargetLibraryInfo &TLI,
const DFRegionInfo &DFI, const llvm::DominatorTree &DT,
const DelinearizeInfo &DI, llvm::ScalarEvolution &SE,
const llvm::DataLayout &DL, const GlobalOptions &GO,
DefinedMemoryInfo &DefInfo, InterprocDefUseInfo &InterprocDUInfo) :
const llvm::DataLayout &DL, const AssumptionMap &AM,
const GlobalOptions &GO, DefinedMemoryInfo &DefInfo,
InterprocDefUseInfo &InterprocDUInfo) :
mAliasTree(&AT), mTLI(&TLI), mRegionInfo(&DFI), mDT(&DT),
mDI(&DI), mSE(&SE), mDL(&DL), mGO(&GO), mDefInfo(&DefInfo),
mDI(&DI), mSE(&SE), mDL(&DL), mAM(&AM), mGO(&GO), mDefInfo(&DefInfo),
mInterprocDUInfo(&InterprocDUInfo) {}

/// Creates data-flow framework.
ReachDFFwk(AliasTree &AT, llvm::TargetLibraryInfo &TLI,
const llvm::DominatorTree &DT, const DelinearizeInfo &DI,
llvm::ScalarEvolution &SE, const llvm::DataLayout &DL,
const GlobalOptions &GO, DefinedMemoryInfo &DefInfo,
InterprocDefUseInfo &InterprocDUInfo) :
const AssumptionMap &AM, const GlobalOptions &GO,
DefinedMemoryInfo &DefInfo, InterprocDefUseInfo &InterprocDUInfo) :
mAliasTree(&AT), mTLI(&TLI), mDT(&DT), mDI(&DI), mSE(&SE), mDL(&DL),
mGO(&GO), mDefInfo(&DefInfo), mInterprocDUInfo(&InterprocDUInfo) {}
mGO(&GO), mAM(&AM), mDefInfo(&DefInfo),
mInterprocDUInfo(&InterprocDUInfo) {}

/// Return results of interprocedural analysis or nullptr.
InterprocDefUseInfo * getInterprocDefUseInfo() noexcept {
Expand Down Expand Up @@ -491,6 +556,11 @@ class ReachDFFwk : private bcl::Uncopyable {
/// Returns data layout.
const llvm::DataLayout & getDataLayout() const noexcept { return *mDL; }

/// Returns assumption map.
const AssumptionMap * getAssumptionMap() const noexcept {
return mAM;
}

/// Returns global options.
const GlobalOptions & getGlobalOptions() const noexcept { return *mGO; }

Expand All @@ -507,6 +577,7 @@ class ReachDFFwk : private bcl::Uncopyable {
const DelinearizeInfo *mDI;
llvm::ScalarEvolution *mSE;
const llvm::DataLayout *mDL;
const AssumptionMap *mAM;
const GlobalOptions *mGO;
};

Expand Down
77 changes: 62 additions & 15 deletions include/tsar/Analysis/Memory/MemoryLocationRange.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,16 @@
#ifndef TSAR_MEMORY_LOCATION_RANGE_H
#define TSAR_MEMORY_LOCATION_RANGE_H

#include "tsar/Analysis/Memory/AssumptionInfo.h"
#include <llvm/ADT/APInt.h>
#include <llvm/ADT/BitmaskEnum.h>
#include <llvm/Analysis/MemoryLocation.h>

namespace llvm {
class ScalarEvolution;
class SCEV;
}

namespace tsar {

using LocationSize = llvm::LocationSize;
Expand All @@ -42,24 +48,37 @@ LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
/// LowerBound is always 0.
struct MemoryLocationRange {
enum : uint64_t { UnknownSize = llvm::MemoryLocation::UnknownSize };
// There are several kinds of memory locations:
// * Default - the kind of memory locations corresponding to scalar variables
// or array memory locations can potentially be collapsed later.
// * NonCollapsable - the kind of memory locations that are not scalar, for
// which it has already been determined that collapse cannot be performed.
// * Collapsed - the kind of collapsed memory locations.
// * Hint - the kind of memory locations artificially added for correct
// private variables analysis.
// * Auxiliary - the kind of memory locations that intersect with some other
// memory location and are added to MemorySet as auxiliary, in order to take
// into account the fact that the same memory is being accessed.
enum LocKind : uint8_t {
Default = 0,
NonCollapsable = 1u << 0,
Collapsed = 1u << 1,
Hint = 1u << 2,
LLVM_MARK_AS_BITMASK_ENUM(Hint)
Auxiliary = 1u << 3,
LLVM_MARK_AS_BITMASK_ENUM(Auxiliary)
};

struct Dimension {
uint64_t Start;
uint64_t Step;
uint64_t TripCount;
const llvm::SCEV *Start;
const llvm::SCEV *End;
const llvm::SCEV *Step;
uint64_t DimSize;
Dimension() : Start(0), Step(0), TripCount(0), DimSize(0) {}
Dimension() : Start(nullptr), End(nullptr), Step(nullptr), DimSize(0) {}
inline bool operator==(const Dimension &Other) const {
return Start == Other.Start && Step == Other.Step &&
TripCount == Other.TripCount && DimSize == Other.DimSize;
return Start == Other.Start && End == Other.End && Step == Other.Step &&
DimSize == Other.DimSize;
}
void print(llvm::raw_ostream &OS, bool IsDebug = false) const;
};

const llvm::Value * Ptr;
Expand All @@ -68,6 +87,8 @@ struct MemoryLocationRange {
llvm::SmallVector<Dimension, 0> DimList;
llvm::AAMDNodes AATags;
LocKind Kind;
llvm::ScalarEvolution *SE;
const AssumptionMap *AM;

/// Return a location with information about the memory reference by the given
/// instruction.
Expand Down Expand Up @@ -135,37 +156,43 @@ struct MemoryLocationRange {
LocationSize UpperBound = UnknownSize,
const llvm::AAMDNodes &AATags = llvm::AAMDNodes())
: Ptr(Ptr), LowerBound(LowerBound), UpperBound(UpperBound),
AATags(AATags), Kind(LocKind::Default) {}
AATags(AATags), SE(nullptr), AM(nullptr), Kind(LocKind::Default) {}

explicit MemoryLocationRange(const llvm::Value *Ptr,
LocationSize LowerBound,
LocationSize UpperBound,
LocKind Kind,
llvm::ScalarEvolution *SE,
AssumptionMap *AM,
const llvm::AAMDNodes &AATags = llvm::AAMDNodes())
: Ptr(Ptr), LowerBound(LowerBound), UpperBound(UpperBound),
AATags(AATags), Kind(Kind) {}
: Ptr(Ptr), LowerBound(LowerBound), UpperBound(UpperBound), Kind(Kind),
SE(SE), AM(AM), AATags(AATags) {}

MemoryLocationRange(const llvm::MemoryLocation &Loc)
: Ptr(Loc.Ptr), LowerBound(0), UpperBound(Loc.Size), AATags(Loc.AATags),
Kind(LocKind::Default) {}
Kind(LocKind::Default), SE(nullptr), AM(nullptr) {}

MemoryLocationRange(const MemoryLocationRange &Loc)
: Ptr(Loc.Ptr), LowerBound(Loc.LowerBound), UpperBound(Loc.UpperBound),
AATags(Loc.AATags), DimList(Loc.DimList), Kind(Loc.Kind) {}
AATags(Loc.AATags), DimList(Loc.DimList), Kind(Loc.Kind), SE(Loc.SE),
AM(Loc.AM) {}

MemoryLocationRange &operator=(const llvm::MemoryLocation &Loc) {
Ptr = Loc.Ptr;
LowerBound = 0;
UpperBound = Loc.Size;
AATags = Loc.AATags;
Kind = Default;
SE = nullptr;
AM = nullptr;
return *this;
}

bool operator==(const MemoryLocationRange &Other) const {
return Ptr == Other.Ptr && AATags == Other.AATags &&
LowerBound == Other.LowerBound && UpperBound == Other.UpperBound &&
DimList == Other.DimList && Kind == Other.Kind;
DimList == Other.DimList && SE == Other.SE && AM == Other.AM &&
Kind == Other.Kind;
}

std::string getKindAsString() const {
Expand All @@ -183,8 +210,26 @@ struct MemoryLocationRange {
append("Collapsed", KindStr);
if (Kind & Hint)
append("Hint", KindStr);
if (Kind & Auxiliary)
append("Auxiliary", KindStr);
return KindStr;
}

/// If the location is associated with an array, i.e. it has a non-empty list
/// of dimensions, convert it to an ordinary location with an empty list of
/// dimensions and return it. The new location covers the entire memory of
/// the array, even though the dimensions are not completely used.
MemoryLocationRange expand() const {
if (DimList.empty())
return *this;
assert(UpperBound.hasValue() && "UpperBound must have a value!");
auto FullSize = UpperBound.getValue();;
for (std::size_t I = 0; I < DimList.size(); ++I)
FullSize *= DimList[I].DimSize;
if (FullSize == 0)
return MemoryLocationRange(Ptr, 0, UnknownSize, AATags);
return MemoryLocationRange(Ptr, 0, LocationSize(FullSize), AATags);
}
};

/// \brief Finds an intersection between memory locations LHS and RHS.
Expand All @@ -204,8 +249,10 @@ struct MemoryLocationRange {
/// exact differences will not be saved.
/// \return The result of intersection. If it is None, intersection is empty.
/// If it is a location, but `Ptr` of the returned location is `nullptr`, then
/// the intersection may exist but can't be calculated. Otherwise, the
/// returned location is an exact intersection.
/// the intersection may exist but can't be calculated (note that you will get
/// the same result if the intersection is exact but LC or RC is not `nullptr`
/// and we can't find the exact differences). Otherwise, the returned location
/// is an exact intersection.
llvm::Optional<MemoryLocationRange> intersect(
MemoryLocationRange LHS,
MemoryLocationRange RHS,
Expand Down
Loading