Skip to content

Improvements to array bounds inference #226

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 11 commits into from
Sep 7, 2020
35 changes: 32 additions & 3 deletions clang/include/clang/CConv/ABounds.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,36 @@ class ABounds {
BoundsKind Kind;
protected:
ABounds(BoundsKind K): Kind(K) { }
void addBoundsUsedKey(BoundsKey);
public:
virtual ~ABounds() { }

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

// Set that maintains all the bound keys that are used inin
static std::set<BoundsKey> KeysUsedInBounds;

static bool isKeyUsedInBounds(BoundsKey ToCheck);

static ABounds *getBoundsInfo(AVarBoundsInfo *AVBInfo,
BoundsExpr *BExpr, const ASTContext &C);
};

class CountBound : public ABounds {
public:
CountBound(BoundsKey Var) : ABounds(CountBoundKind), CountVar(Var) { }
CountBound(BoundsKey Var) : ABounds(CountBoundKind), CountVar(Var) {
addBoundsUsedKey(Var);
}

virtual ~CountBound() { }

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

static bool classof(const ABounds *S) {
return S->getKind() == CountBoundKind;
Expand All @@ -69,12 +81,16 @@ class CountBound : public ABounds {

class ByteBound : public ABounds {
public:
ByteBound(BoundsKey Var) : ABounds(ByteBoundKind), ByteVar(Var) { }
ByteBound(BoundsKey Var) : ABounds(ByteBoundKind), ByteVar(Var) {
addBoundsUsedKey(Var);
}

virtual ~ByteBound() { }

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

static bool classof(const ABounds *S) {
return S->getKind() == ByteBoundKind;
Expand All @@ -87,13 +103,26 @@ class ByteBound : public ABounds {
class RangeBound : public ABounds {
public:
RangeBound(BoundsKey L, BoundsKey R) : ABounds(RangeBoundKind),
LB(L), UB(R) { }
LB(L), UB(R) {
addBoundsUsedKey(L);
addBoundsUsedKey(R);
}

virtual ~RangeBound() { }

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

BoundsKey getBKey() override {
assert (false && "Not implemented.");
return 0;
}

ABounds* makeCopy(BoundsKey NK) override {
assert (false &&& "Not Implemented");
return nullptr;
}

static bool classof(const ABounds *S) {
return S->getKind() == RangeBoundKind;
}
Expand Down
125 changes: 119 additions & 6 deletions clang/include/clang/CConv/AVarBoundsInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "clang/CConv/ConstraintVariables.h"

class ProgramInfo;
class ConstraintResolver;

// Class that maintains stats about how the bounds of various variables is
// computed.
Expand Down Expand Up @@ -78,6 +79,14 @@ class AVarBoundsStats {

};

// Priority for bounds.
enum BoundsPriority {
Declared = 1, // Highest priority: These are declared by the user.
Allocator, // Second priority: allocator based bounds.
FlowInferred, // Flow based bounds.
Heuristics, // Least-priority, based on heuristics.
Invalid // Invalid priority type.
};

class AVarBoundsInfo;

Expand All @@ -103,6 +112,9 @@ class AvarBoundsInference {
bool predictBounds(BoundsKey K, std::set<BoundsKey> &Neighbours,
ABounds **KB);


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

AVarBoundsInfo *BI;
};

Expand All @@ -115,6 +127,8 @@ class AVarBoundsInfo {
BInfo.clear();
DeclVarMap.clear();
TmpBoundsKey.clear();
CSBoundsKey.clear();
ArrPointersWithArithmetic.clear();
}

typedef std::tuple<std::string, std::string, bool, unsigned> ParamDeclType;
Expand All @@ -123,10 +137,12 @@ class AVarBoundsInfo {
bool isValidBoundVariable(clang::Decl *D);

void insertDeclaredBounds(clang::Decl *D, ABounds *B);
bool mergeBounds(BoundsKey L, ABounds *B);
bool removeBounds(BoundsKey L);
bool replaceBounds(BoundsKey L, ABounds *B);
ABounds *getBounds(BoundsKey L);
bool mergeBounds(BoundsKey L, BoundsPriority P, ABounds *B);
bool removeBounds(BoundsKey L, BoundsPriority P = Invalid);
bool replaceBounds(BoundsKey L, BoundsPriority P, ABounds *B);
ABounds *getBounds(BoundsKey L,
BoundsPriority ReqP = Invalid,
BoundsPriority *RetP = nullptr);
bool updatePotentialCountBounds(BoundsKey BK, std::set<BoundsKey> &CntBK);

// Try and get BoundsKey, into R, for the given declaration. If the declaration
Expand All @@ -143,6 +159,7 @@ class AVarBoundsInfo {
BoundsKey getVariable(clang::VarDecl *VD);
BoundsKey getVariable(clang::ParmVarDecl *PVD);
BoundsKey getVariable(clang::FieldDecl *FD);
BoundsKey getVariable(clang::FunctionDecl *FD);
BoundsKey getConstKey(uint64_t value);

// Generate a random bounds key to be used for inference.
Expand All @@ -153,6 +170,28 @@ class AVarBoundsInfo {
bool addAssignment(clang::Decl *L, clang::Decl *R);
bool addAssignment(clang::DeclRefExpr *L, clang::DeclRefExpr *R);
bool addAssignment(BoundsKey L, BoundsKey R);
bool handlePointerAssignment(clang::Stmt *St, clang::Expr *L,
clang::Expr *R,
ASTContext *C,
ConstraintResolver *CR);
bool handleAssignment(clang::Expr *L, CVarSet &LCVars,
clang::Expr *R, CVarSet &RCVars,
ASTContext *C, ConstraintResolver *CR);
bool handleAssignment(clang::Decl *L, CVarSet &LCVars,
clang::Expr *R, CVarSet &RCVars,
ASTContext *C, ConstraintResolver *CR);
// Handle context sensitive assignment.
bool handleContextSensitiveAssignment(CallExpr *CE, clang::Decl *L,
ConstraintVariable *LCVar,
clang::Expr *R, CVarSet &RCVars,
ASTContext *C, ConstraintResolver *CR);

// Handle the arithmetic expression. This is required to adjust bounds
// for pointers that has pointer arithmetic performed on them.
void recordArithmeticOperation(clang::Expr *E, ConstraintResolver *CR);

// Check if the given bounds key has a pointer arithmetic done on it.
bool hasPointerArithmetic(BoundsKey BK);

// Get the ProgramVar for the provided VarKey.
ProgramVar *getProgramVar(BoundsKey VK);
Expand All @@ -164,6 +203,17 @@ class AVarBoundsInfo {
// Propagate the array bounds information for all array ptrs.
bool performFlowAnalysis(ProgramInfo *PI);

// Reset (i.e., clear) the context sensitive bounds information.
void resetContextSensitiveBoundsKey();
// Create context sensitive BoundsKey variables for the given set of
// ConstraintVariables.
bool contextualizeCVar(CallExpr *CE,
const std::set<ConstraintVariable *> &CV);
// Get the context sensitive BoundsKey for the given key.
// If there exists no context-sensitive bounds key, we just return
// the provided key.
BoundsKey getContextSensitiveBoundsKey(CallExpr *CE, BoundsKey BK);

AVarBoundsStats &getBStats() { return BoundsInferStats; }

// Dump the AVar graph to the provided dot file.
Expand All @@ -176,24 +226,33 @@ class AVarBoundsInfo {

private:
friend class AvarBoundsInference;

friend struct llvm::DOTGraphTraits<AVarGraph>;
// List of bounds priority in descending order of priorities.
static std::vector<BoundsPriority> PrioList;

// Variable that is used to generate new bound keys.
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 BoundsKey and corresponding bounds information.
// 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.
std::map<BoundsKey, ABounds *> BInfo;
std::map<BoundsKey, std::map<BoundsPriority, ABounds *>> BInfo;
// Set that contains BoundsKeys of variables which have invalid bounds.
std::set<BoundsKey> InvalidBounds;
// These are the bounds key of the pointers that has arithmetic operations
// performed on them.
std::set<BoundsKey> ArrPointersWithArithmetic;
// Set of BoundsKeys that correspond to pointers.
std::set<BoundsKey> PointerBoundsKey;
// Set of BoundsKey that correspond to array pointers.
std::set<BoundsKey> ArrPointerBoundsKey;
// Set of BoundsKey that correspond to array pointers with in the program
// being compiled i.e., it does not include array pointers that belong
// to libraries.
std::set<BoundsKey> InProgramArrPtrBoundsKeys;

// These are temporary bound keys generated during inference.
Expand All @@ -204,6 +263,9 @@ class AVarBoundsInfo {
BiMap<PersistentSourceLoc, BoundsKey> DeclVarMap;
// BiMap of parameter keys and BoundsKey for function parameters.
BiMap<ParamDeclType, BoundsKey> ParamDeclVarMap;
// BiMap of function keys and BoundsKey for function return values.
BiMap<std::tuple<std::string, std::string, bool>,
BoundsKey> FuncDeclVarMap;

// Graph of all program variables.
AVarGraph ProgVarGraph;
Expand All @@ -213,6 +275,12 @@ class AVarBoundsInfo {
// which can be the count bounds.
std::map<BoundsKey, std::set<BoundsKey>> PotentialCntBounds;

// Context sensitive bounds key.
// For each call-site a map of original bounds key and the bounds key
// specific to this call-site.
// Note: This map is only active for the compilation unit being parsed.
std::map<CallExpr *, std::map<BoundsKey, BoundsKey>> CSBoundsKey;

// BoundsKey helper function: These functions help in getting bounds key from
// various artifacts.
bool hasVarKey(PersistentSourceLoc &PSL);
Expand All @@ -225,6 +293,16 @@ class AVarBoundsInfo {

void insertProgramVar(BoundsKey NK, ProgramVar *PV);

// Check if the provided bounds key corresponds to function return.
bool isFunctionReturn(BoundsKey BK);

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

// 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.
// The flag FromPB requests the algorithm to use potential length variables.
bool performWorkListInference(std::set<BoundsKey> &ArrNeededBounds,
Expand All @@ -233,4 +311,39 @@ class AVarBoundsInfo {
void insertParamKey(ParamDeclType ParamDecl, BoundsKey NK);
};

// This class creates context sensitive bounds key information that is
// useful to resolve certain bounds information.
// Consider the following example:
// _Arry_ptr<int> foo(unsigned int s) : count(s);
// ....
// int *a, *c;
// unsigned b, d;
// a = foo(b);
// c = foo(d);
// ...
// Here, when we do our analysis we do not know whether b or d is the bounds
// of a.
// The reason for this is because we maintain a single bounds variable for foo,
// consequently, when we do our flow analysis we see that b and d both propagate
// to s (which is the bounds of the return value of foo).
// However, if we maintain context sensitive bounds keys, then we know that
// at a = foo(b), it is b that is passed to s and there by helps us infer that
// the bounds of a should be b i.e., _Array_ptr<a> : count(b).
// This class helps in maintaining the context sensitive bounds information.
class ContextSensitiveBoundsKeyVisitor :
public RecursiveASTVisitor<ContextSensitiveBoundsKeyVisitor> {
public:
explicit ContextSensitiveBoundsKeyVisitor(ASTContext *C, ProgramInfo &I,
ConstraintResolver *CResolver);

virtual ~ContextSensitiveBoundsKeyVisitor();

bool VisitCallExpr(CallExpr *CE);

private:
ASTContext *Context;
ProgramInfo &Info;
ConstraintResolver *CR;
};

#endif // _AVARBOUNDSINFO_H
2 changes: 1 addition & 1 deletion clang/include/clang/CConv/ConstraintResolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ConstraintResolver {
ConsAction CAction = Same_to_Same);

// Check if the set contains any valid constraints.
bool containsValidCons(CVarSet &CVs);
bool containsValidCons(const CVarSet &CVs);
bool isValidCons(ConstraintVariable *CV);
// Try to get the bounds key from the constraint variable set.
bool resolveBoundsKey(CVarSet &CVs, BoundsKey &BK);
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/CConv/ConstraintVariables.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ class ConstraintVariable {
assert(ValidBoundsKey && "No valid Bkey");
return BKey;
}
void setBoundsKey(BoundsKey NK) {
ValidBoundsKey = true;
BKey = NK;
}

virtual bool solutionEqualTo(Constraints &, const ConstraintVariable *) const
= 0;
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/CConv/ProgramVar.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,9 @@ class ProgramVar {
BoundsKey getKey() { return K; }
bool IsNumConstant() { return IsConstant; }
std::string mkString(bool GetKey = false);
std::string getVarName() { return VarName; }
std::string verboseStr();
ProgramVar *makeCopy(BoundsKey NK);
virtual ~ProgramVar() { }
private:
BoundsKey K;
Expand Down
Loading