Skip to content

Major refactoring of array bounds code and some performance improvements #282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 27, 2020
Merged
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
10 changes: 5 additions & 5 deletions clang/include/clang/CConv/ABounds.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ class ABounds {
virtual ~ABounds() { }

virtual std::string mkString(AVarBoundsInfo *) = 0;
virtual bool areSame(ABounds *) = 0;
virtual bool areSame(ABounds *, AVarBoundsInfo *) = 0;
virtual BoundsKey getBKey() = 0;
virtual ABounds* makeCopy(BoundsKey NK) = 0;

// Set that maintains all the bound keys that are used inin
// TODO: Is this still needed?
static std::set<BoundsKey> KeysUsedInBounds;

static bool isKeyUsedInBounds(BoundsKey ToCheck);

static ABounds *getBoundsInfo(AVarBoundsInfo *AVBInfo,
Expand All @@ -66,7 +66,7 @@ class CountBound : public ABounds {
virtual ~CountBound() { }

std::string mkString(AVarBoundsInfo *ABI) override ;
bool areSame(ABounds *O) override;
bool areSame(ABounds *O, AVarBoundsInfo *ABI) override;
BoundsKey getBKey() override;
ABounds* makeCopy(BoundsKey NK) override;

Expand All @@ -88,7 +88,7 @@ class ByteBound : public ABounds {
virtual ~ByteBound() { }

std::string mkString(AVarBoundsInfo *ABI) override ;
bool areSame(ABounds *O) override;
bool areSame(ABounds *O, AVarBoundsInfo *ABI) override;
BoundsKey getBKey() override;
ABounds* makeCopy(BoundsKey NK) override;

Expand All @@ -111,7 +111,7 @@ class RangeBound : public ABounds {
virtual ~RangeBound() { }

std::string mkString(AVarBoundsInfo *ABI) override ;
bool areSame(ABounds *O) override;
bool areSame(ABounds *O, AVarBoundsInfo *ABI) override;

BoundsKey getBKey() override {
assert (false && "Not implemented.");
Expand Down
79 changes: 58 additions & 21 deletions clang/include/clang/CConv/AVarBoundsInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,40 +89,69 @@ enum BoundsPriority {
};

class AVarBoundsInfo;

typedef std::map<ABounds::BoundsKind, std::set<BoundsKey>> BndsKindMap;
// The main class that handles figuring out bounds of arr variables.
class AvarBoundsInference {
public:
AvarBoundsInference(AVarBoundsInfo *BoundsInfo) : BI(BoundsInfo) { }
AvarBoundsInference(AVarBoundsInfo *BoundsInfo) : BI(BoundsInfo) {
clearInferredBounds();
}

// Clear all possible inferred bounds for all the BoundsKeys
void clearInferredBounds() {
CurrIterInferBounds.clear();
BKsFailedFlowInference.clear();
}

// Infer bounds for the given key from the set of given ARR atoms.
// The flag FromPB requests the inference to use potential length variables.
bool inferBounds(BoundsKey K, AVarGraph &BKGraph, bool FromPB = false);

// Get a consistent bound for all the arrays whose bounds have been
// inferred.
bool convergeInferredBounds();
private:
bool inferPossibleBounds(BoundsKey K, ABounds *SB,
AVarGraph &BKGraph,
std::set<ABounds *> &EB);
// Find all the reachable variables form FromVarK that are visible
// in DstScope
bool getReachableBoundKeys(const ProgramVarScope *DstScope,
BoundsKey FromVarK,
std::set<BoundsKey> &PotK,
AVarGraph &BKGraph,
bool CheckImmediate = false);

bool intersectBounds(std::set<ProgramVar *> &ProgVars,
ABounds::BoundsKind BK,
std::set<ABounds *> &CurrB);

bool getRelevantBounds(std::set<BoundsKey> &RBKeys,
std::set<ABounds *> &ResBounds);
// Check if bounds specified by Bnds are declared bounds of K.
bool areDeclaredBounds(BoundsKey K,
const std::pair<ABounds::BoundsKind,
std::set<BoundsKey>> &Bnds);

bool predictBounds(BoundsKey K, std::set<BoundsKey> &Neighbours,
AVarGraph &BKGraph,
ABounds **KB);
// Get all the bounds of the given array i.e., BK
bool getRelevantBounds(BoundsKey BK,
BndsKindMap &ResBounds);

// Predict possible bounds for DstArrK from the bounds of Neighbours.
// Return true if there is any change in the captured bounds information.
bool predictBounds(BoundsKey DstArrK, std::set<BoundsKey> &Neighbours,
AVarGraph &BKGraph);

void mergeReachableProgramVars(std::set<ProgramVar *> &AllVars);

void mergeReachableProgramVars(std::set<BoundsKey> &AllVars);

AVarBoundsInfo *BI;

// Potential Bounds for each bounds key inferred for the current iteration.
std::map<BoundsKey, BndsKindMap> CurrIterInferBounds;
// BoundsKey that failed the flow inference.
std::set<BoundsKey> BKsFailedFlowInference;
};

class AVarBoundsInfo {
public:
AVarBoundsInfo() : ProgVarGraph(this), CtxSensProgVarGraph(this) {
AVarBoundsInfo() : ProgVarGraph(this), CtxSensProgVarGraph(this),
RevCtxSensProgVarGraph(this) {
BCount = 1;
PVarInfo.clear();
InProgramArrPtrBoundsKeys.clear();
Expand Down Expand Up @@ -163,6 +192,7 @@ class AVarBoundsInfo {
BoundsKey getVariable(clang::FieldDecl *FD);
BoundsKey getVariable(clang::FunctionDecl *FD);
BoundsKey getConstKey(uint64_t value);
bool fetchAllConstKeys(uint64_t value, std::set<BoundsKey> &AllKeys);

// Generate a random bounds key to be used for inference.
BoundsKey getRandomBKey();
Expand Down Expand Up @@ -227,6 +257,8 @@ class AVarBoundsInfo {
const CVarSet &SrcCVarSet,
bool JsonFormat = false) const;

bool areSameProgramVar(BoundsKey B1, BoundsKey B2);

private:
friend class AvarBoundsInference;

Expand All @@ -238,8 +270,8 @@ class AVarBoundsInfo {
BoundsKey BCount;
// Map of VarKeys and corresponding program variables.
std::map<BoundsKey, ProgramVar *> PVarInfo;
// Map of APSInt (constants) and corresponding VarKeys.
std::map<uint64_t, BoundsKey> ConstVarKeys;
// Map of APSInt (constants) and set of BoundKeys that correspond to it.
std::map<uint64_t, std::set<BoundsKey>> ConstVarKeys;
// Map of BoundsKey and corresponding prioritized bounds information.
// Note that although each PSL could have multiple ConstraintKeys Ex: **p.
// Only the outer most pointer can have bounds.
Expand Down Expand Up @@ -272,9 +304,11 @@ class AVarBoundsInfo {

// Graph of all program variables.
AVarGraph ProgVarGraph;
// Graph that contains only edges between context-sensitive
// BoundsKey and corresponding original BoundsKey.
// Graph that contains only edges from normal BoundsKey to
// context-sensitive BoundsKey.
AVarGraph CtxSensProgVarGraph;
// Same as above but in the reverse direction.
AVarGraph RevCtxSensProgVarGraph;
// Stats on techniques used to find length for various variables.
AVarBoundsStats BoundsInferStats;
// This is the map of pointer variable bounds key and set of bounds key
Expand Down Expand Up @@ -305,19 +339,22 @@ class AVarBoundsInfo {
// Check if the provided bounds key corresponds to function return.
bool isFunctionReturn(BoundsKey BK);

// Of all teh pointer bounds key, find arr pointers.
// Of all the pointer bounds key, find arr pointers.
void computerArrPointers(ProgramInfo *PI, std::set<BoundsKey> &Ret);

// Get all the array pointers that need bounds.
void getBoundsNeededArrPointers(const std::set<BoundsKey> &ArrPtrs,
std::set<BoundsKey> &AB);

// Keep only highest priority bounds for all the provided BoundsKeys
// returns true if any thing changed, else false.
bool keepHighestPriorityBounds(std::set<BoundsKey> &ArrPtrs);

// Perform worklist based inference on the requested array variables using
// the provided graph.
// The flag FromPB requests the algorithm to use potential length variables.
bool performWorkListInference(std::set<BoundsKey> &ArrNeededBounds,
// the provided graph and potential length variables.
bool performWorkListInference(const std::set<BoundsKey> &ArrNeededBounds,
AVarGraph &BKGraph,
bool FromPB = false);
AvarBoundsInference &BI);

void insertParamKey(ParamDeclType ParamDecl, BoundsKey NK);
};
Expand Down
12 changes: 11 additions & 1 deletion clang/include/clang/CConv/ArrayBoundsInferenceConsumer.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@
class LocalVarABVisitor;
class ConstraintResolver;

class AllocBasedBoundsInference : public ASTConsumer {
public:
explicit AllocBasedBoundsInference(ProgramInfo &I, clang::ASTContext *C) : Info(I) { }
virtual void HandleTranslationUnit(ASTContext &Context);

private:
ProgramInfo &Info;
};

// This class handles determining bounds of global array variables.
// i.e., function parameters, structure fields and global variables.
class GlobalABVisitor: public clang::RecursiveASTVisitor<GlobalABVisitor> {
Expand Down Expand Up @@ -96,7 +105,8 @@ class LengthVarInference : public StmtVisitor<LengthVarInference> {
std::unique_ptr<CFG> Cfg;
};

void HandleArrayVariablesBoundsDetection(ASTContext *C, ProgramInfo &I);
void HandleArrayVariablesBoundsDetection(ASTContext *C, ProgramInfo &I,
bool UseHeuristics = true);

// Add constraints based on heuristics to the parameters of the
// provided function.
Expand Down
20 changes: 18 additions & 2 deletions clang/include/clang/CConv/ConstraintsGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ class DataGraph :
N = nullptr;
}
this->Nodes.clear();
invalidateBFSCache();
}

void removeEdge(Data Src, Data Dst) {
Expand All @@ -126,12 +127,14 @@ class DataGraph :
(*NDst)->removeEdge(*E);
delete E;
}
invalidateBFSCache();
}

void addEdge(Data L, Data R) {
NodeType *BL = this->findOrCreateNode(L);
NodeType *BR = this->findOrCreateNode(R);
BL->connectTo(*BR);
invalidateBFSCache();
}

bool getNeighbors(Data D, std::set<Data> &DataSet, bool Succ){
Expand Down Expand Up @@ -161,8 +164,16 @@ class DataGraph :
auto *N = this->findNode(NodeType(Start));
if (N == this->end())
return;
for (auto TNode : llvm::breadth_first(*N))
Fn(TNode->getData());
// Insert into BFS cache.
if (BFSCache.find(Start) == BFSCache.end()) {
std::set<Data> ReachableNodes;
for (auto TNode : llvm::breadth_first(*N)) {
ReachableNodes.insert(TNode->getData());
}
BFSCache[Start] = ReachableNodes;
}
for (auto SN : BFSCache[Start])
Fn(SN);
}

protected:
Expand All @@ -183,6 +194,11 @@ class DataGraph :
template <typename G>
friend struct llvm::GraphTraits;
friend class GraphVizOutputGraph;
std::map<Data, std::set<Data>> BFSCache;

void invalidateBFSCache() {
BFSCache.clear();
}
};

// Specialize the graph for the checked and pointer type constraint graphs. This
Expand Down
25 changes: 19 additions & 6 deletions clang/include/clang/CConv/ProgramVar.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include "clang/AST/ASTContext.h"
#include "clang/CConv/PersistentSourceLoc.h"

// Unique ID for a program variable or constant literal, both of
// which could serve as bounds
typedef uint32_t BoundsKey;

// Class representing scope of a program variable.
Expand All @@ -27,9 +29,9 @@ class ProgramVarScope {
enum ScopeKind {
// Function scope.
FunctionScopeKind,
// Function parameter scope.
// Parameters of a particular function.
FunctionParamScopeKind,
// Context sensitive argument scope.
// All arguments to a particular call
CtxFunctionArgScopeKind,
// Struct scope.
StructScopeKind,
Expand Down Expand Up @@ -215,6 +217,17 @@ class CtxFunctionArgScope : public FunctionParamScope {
const PersistentSourceLoc &CtxPSL) :
FunctionParamScope(FN, IsSt) {
PSL = CtxPSL;
std::string FileName = PSL.getFileName();
CtxIDStr = "";
if (!FileName.empty()) {
llvm::sys::fs::UniqueID UId;
if (llvm::sys::fs::getUniqueID(FileName, UId)) {
CtxIDStr = std::to_string(UId.getDevice()) + ":" +
std::to_string(UId.getFile()) + ":";
}
}
CtxIDStr += std::to_string(PSL.getLineNo()) + ":" +
std::to_string(PSL.getColSNo());
this->Kind = CtxFunctionArgScopeKind;
}

Expand Down Expand Up @@ -261,16 +274,16 @@ class CtxFunctionArgScope : public FunctionParamScope {
}

std::string getStr() const {
return "CtxFuncArg_" + FName;
return FName + "_Ctx_" + CtxIDStr;
}

static const CtxFunctionArgScope *
getCtxFunctionParamScope(const FunctionParamScope *FPS,
const PersistentSourceLoc &PSL);

private:
PersistentSourceLoc PSL;

PersistentSourceLoc PSL; // source code location of this function call
std::string CtxIDStr;
static std::set<CtxFunctionArgScope, PVSComp> AllCtxFnArgScopes;
};

Expand Down Expand Up @@ -354,7 +367,7 @@ class ProgramVar {
BoundsKey K;
std::string VarName;
const ProgramVarScope *VScope;
bool IsConstant;
bool IsConstant; // is a literal integer, not a variable
// TODO: All the ProgramVars may not be used. We should try to figure out
// a way to free unused program vars.
static std::set<ProgramVar *> AllProgramVars;
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/CConv/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ bool isFunctionAllocator(std::string FuncName);
// Is the given variable built in type?
bool isPointerType(clang::ValueDecl *VD);

// Is this a pointer or array type?
bool isPtrOrArrayType(const clang::QualType &QT);

// Check if provided type is a var arg type?
bool isVarArgType(const std::string &TypeName);

Expand Down
16 changes: 8 additions & 8 deletions clang/lib/CConv/ABounds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,10 @@ std::string CountBound::mkString(AVarBoundsInfo *ABI) {
return "count(" + PV->mkString() + ")";
}

bool CountBound::areSame(ABounds *O) {
bool CountBound::areSame(ABounds *O, AVarBoundsInfo *ABI) {
if (O != nullptr) {
if (CountBound *OT = dyn_cast<CountBound>(O)) {
return OT->CountVar == CountVar;
}
if (CountBound *OT = dyn_cast<CountBound>(O))
return ABI->areSameProgramVar(this->CountVar, OT->CountVar);
}
return false;
}
Expand All @@ -86,10 +85,10 @@ std::string ByteBound::mkString(AVarBoundsInfo *ABI) {
return "byte_count(" + PV->mkString() + ")";
}

bool ByteBound::areSame(ABounds *O) {
bool ByteBound::areSame(ABounds *O, AVarBoundsInfo *ABI) {
if (O != nullptr) {
if (ByteBound *BB = dyn_cast<ByteBound>(O)) {
return BB->ByteVar == ByteVar;
return ABI->areSameProgramVar(this->ByteVar, BB->ByteVar);
}
}
return false;
Expand All @@ -111,10 +110,11 @@ std::string RangeBound::mkString(AVarBoundsInfo *ABI) {
return "bounds(" + LBVar->mkString() + ", " + UBVar->mkString() + ")";
}

bool RangeBound::areSame(ABounds *O) {
bool RangeBound::areSame(ABounds *O, AVarBoundsInfo *ABI) {
if (O != nullptr) {
if (RangeBound *RB = dyn_cast<RangeBound>(O)) {
return RB->LB == LB && RB->UB == UB;
return ABI->areSameProgramVar(this->LB, RB->LB) &&
ABI->areSameProgramVar(this->UB, RB->UB);
}
}
return false;
Expand Down
Loading