Skip to content

Commit faf4704

Browse files
authored
Bounds context [1/n] (#807)
* Add an updated bounds context member UC to CheckingState * Dump the bounds context in DumpCheckingState * Add GetDeclaredBounds method to incorporate declared bounds into a bounds context * Get the incoming CheckingState bounds context for a CFG block, including declared bounds in a statement * Only dump checking state after top-level CFG statements * Fix equiv-exprs test * Add bounds-context test to test dumping the updated bounds context after checking statements * Check for null predecessor blocks * Sort bounds context keys using std::tie * Remove unused DeclaredBounds variable * Use the bounds context to infer bounds in CheckDeclRefExpr * Revert "Sort bounds context keys using std::tie" This reverts commit 1a38095. * Use find instead of map lookup * Add comments * Use count in CheckDeclRefExpr
1 parent c69e376 commit faf4704

File tree

3 files changed

+351
-419
lines changed

3 files changed

+351
-419
lines changed

clang/lib/Sema/SemaBounds.cpp

Lines changed: 152 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,10 @@ namespace {
475475
}
476476

477477
namespace {
478+
// BoundsContextTy denotes a map of a variable or parameter declaration
479+
// to the variable or parameter's current known bounds.
480+
using BoundsContextTy = llvm::DenseMap<DeclaratorDecl *, BoundsExpr *>;
481+
478482
// EqualExprTy denotes a set of expressions that produce the same value
479483
// as an expression e.
480484
using EqualExprTy = SmallVector<Expr *, 4>;
@@ -488,6 +492,9 @@ namespace {
488492
// and are updated while checking individual expressions.
489493
class CheckingState {
490494
public:
495+
// UC is a map of variables or parameters to their current known bounds.
496+
BoundsContextTy UC;
497+
491498
// UEQ stores sets of expressions that are equivalent to each other
492499
// after checking an expression e.
493500
EquivExprSets UEQ;
@@ -498,6 +505,35 @@ namespace {
498505
};
499506
}
500507

508+
namespace {
509+
class DeclaredBoundsHelper : public RecursiveASTVisitor<DeclaredBoundsHelper> {
510+
private:
511+
Sema &SemaRef;
512+
BoundsContextTy &BoundsContextRef;
513+
514+
public:
515+
DeclaredBoundsHelper(Sema &SemaRef, BoundsContextTy &Context) :
516+
SemaRef(SemaRef),
517+
BoundsContextRef(Context) {}
518+
519+
bool VisitDeclaratorDecl(DeclaratorDecl *D) {
520+
if (!D)
521+
return true;
522+
BoundsExpr *Bounds = D->getBoundsExpr();
523+
if (Bounds)
524+
BoundsContextRef[D] = Bounds;
525+
return true;
526+
}
527+
};
528+
529+
// GetDeclaredBounds modifies the bounds context to map any variables
530+
// declared in S to their declared bounds (if any).
531+
void GetDeclaredBounds(Sema &SemaRef, BoundsContextTy &Context, Stmt *S) {
532+
DeclaredBoundsHelper Declared(SemaRef, Context);
533+
Declared.TraverseStmt(S);
534+
}
535+
}
536+
501537
namespace {
502538
class CheckBoundsDeclarations {
503539
private:
@@ -598,6 +634,9 @@ namespace {
598634
OS << "\nStatement S:\n";
599635
S->dump(OS);
600636

637+
OS << "Bounds context after checking S:\n";
638+
DumpBoundsContext(OS, State.UC);
639+
601640
OS << "Sets of equivalent expressions after checking S:\n";
602641
if (State.UEQ.size() == 0)
603642
OS << "{ }\n";
@@ -614,6 +653,39 @@ namespace {
614653
DumpEqualExpr(OS, State.G);
615654
}
616655

656+
void DumpBoundsContext(raw_ostream &OS, BoundsContextTy UC) {
657+
if (UC.empty())
658+
OS << "{ }\n";
659+
else {
660+
// The keys in an llvm::DenseMap are unordered. Create a set of
661+
// variable declarations in the context ordered first by name,
662+
// then by location in order to guarantee a deterministic output
663+
// so that printing the bounds context can be tested.
664+
std::vector<DeclaratorDecl *> OrderedDecls;
665+
for (auto Pair : UC)
666+
OrderedDecls.push_back(Pair.first);
667+
llvm::sort(OrderedDecls.begin(), OrderedDecls.end(),
668+
[] (DeclaratorDecl *A, DeclaratorDecl *B) {
669+
if (A->getNameAsString() == B->getNameAsString())
670+
return A->getLocation() < B->getLocation();
671+
else
672+
return A->getNameAsString() < B->getNameAsString();
673+
});
674+
675+
OS << "{\n";
676+
for (auto I = OrderedDecls.begin(); I != OrderedDecls.end(); ++I) {
677+
DeclaratorDecl *Variable = *I;
678+
if (!UC[Variable])
679+
continue;
680+
OS << "Variable:\n";
681+
Variable->dump(OS);
682+
OS << "Bounds:\n";
683+
UC[Variable]->dump(OS);
684+
}
685+
OS << "}\n";
686+
}
687+
}
688+
617689
void DumpEqualExpr(raw_ostream &OS, EqualExprTy G) {
618690
if (G.size() == 0)
619691
OS << "{ }\n";
@@ -1887,7 +1959,7 @@ namespace {
18871959
// Walk the CFG, traversing basic blocks in reverse post-oder.
18881960
// For each element of a block, check bounds declarations. Skip
18891961
// CFG elements that are subexpressions of other CFG elements.
1890-
void TraverseCFG(AvailableFactsAnalysis& AFA) {
1962+
void TraverseCFG(AvailableFactsAnalysis& AFA, FunctionDecl *FD) {
18911963
assert(Cfg && "expected CFG to exist");
18921964
#if TRACE_CFG
18931965
llvm::outs() << "Dumping AST";
@@ -1896,6 +1968,23 @@ namespace {
18961968
Cfg->print(llvm::outs(), S.getLangOpts(), true);
18971969
llvm::outs() << "Traversing CFG:\n";
18981970
#endif
1971+
1972+
// Map each function parameter to its declared bounds (if any) before
1973+
// checking the body of the function. The context formed by the declared
1974+
// parameter bounds is the initial context for checking the function body.
1975+
CheckingState ParamsState;
1976+
for (auto I = FD->param_begin(); I != FD->param_end(); ++I) {
1977+
ParmVarDecl *Param = *I;
1978+
BoundsExpr *Bounds = Param->getBoundsExpr();
1979+
if (Bounds)
1980+
ParamsState.UC[Param] = Bounds;
1981+
}
1982+
1983+
// Store a checking state for each CFG block in order to track
1984+
// the variables with bounds declarations that are in scope.
1985+
llvm::DenseMap<unsigned int, CheckingState> BlockStates;
1986+
BlockStates[Cfg->getEntry().getBlockID()] = ParamsState;
1987+
18991988
StmtSet NestedElements;
19001989
FindNestedElements(NestedElements);
19011990
StmtSet MemoryCheckedStmts;
@@ -1905,6 +1994,7 @@ namespace {
19051994
ResetFacts();
19061995
for (const CFGBlock *Block : POView) {
19071996
AFA.GetFacts(Facts);
1997+
CheckingState BlockState = GetIncomingBlockState(Block, BlockStates);
19081998
for (CFGElement Elem : *Block) {
19091999
if (Elem.getKind() == CFGElement::Statement) {
19102000
CFGStmt CS = Elem.castAs<CFGStmt>();
@@ -1934,10 +2024,20 @@ namespace {
19342024
S->dump(llvm::outs());
19352025
llvm::outs().flush();
19362026
#endif
1937-
Check(S, CSS);
2027+
// Incorporate any bounds declared in S into the initial bounds
2028+
// context before checking S. TODO: save this context in a
2029+
// declared context DC.
2030+
GetDeclaredBounds(this->S, BlockState.UC, S);
2031+
Check(S, CSS, BlockState);
2032+
// TODO: validate the updated context BlockState.UC against
2033+
// the declared context DC.
2034+
if (DumpState)
2035+
DumpCheckingState(llvm::outs(), S, BlockState);
19382036
}
19392037
}
19402038
AFA.Next();
2039+
if (Block->getBlockID() != Cfg->getEntry().getBlockID())
2040+
BlockStates[Block->getBlockID()] = BlockState;
19412041
}
19422042
}
19432043

@@ -2075,9 +2175,6 @@ namespace {
20752175
break;
20762176
}
20772177

2078-
if (DumpState)
2079-
DumpCheckingState(llvm::outs(), S, State);
2080-
20812178
if (Expr *E = dyn_cast<Expr>(S)) {
20822179
// Bounds expressions are not null ptrs.
20832180
if (isa<BoundsExpr>(E))
@@ -2156,9 +2253,6 @@ namespace {
21562253
break;
21572254
}
21582255

2159-
if (DumpState)
2160-
DumpCheckingState(llvm::outs(), E, State);
2161-
21622256
// The type for inferring the target bounds cannot ever be an array
21632257
// type, as these are dealt with by an array conversion, not an lvalue
21642258
// conversion. The bounds for an array conversion are the same as the
@@ -2867,7 +2961,10 @@ namespace {
28672961
BoundsExpr *B = nullptr;
28682962
InteropTypeExpr *IT = nullptr;
28692963
if (VD) {
2870-
B = VD->getBoundsExpr();
2964+
if (State.UC.count(VD))
2965+
B = State.UC[VD];
2966+
else
2967+
B = VD->getBoundsExpr();
28712968
IT = VD->getInteropTypeExpr();
28722969
}
28732970

@@ -3315,6 +3412,51 @@ namespace {
33153412
G.push_back(CreateTemporaryUse(Temp));
33163413
}
33173414

3415+
// GetIncomingBlockState returns the checking state that is true at
3416+
// the beginning of the block by taking the intersection of the UC
3417+
// contexts that were true after each of the block's predecessors.
3418+
//
3419+
// BlockStates stores the checking state including the bounds context
3420+
// for each CFG block in order to track the variables with bounds
3421+
// declarations that are in scope. This preserves lexically scoped
3422+
// information that is otherwise lost during CFG traversal.
3423+
CheckingState GetIncomingBlockState(const CFGBlock *Block,
3424+
llvm::DenseMap<unsigned int, CheckingState> BlockStates) {
3425+
CheckingState BlockState;
3426+
bool IntersectionEmpty = true;
3427+
for (const CFGBlock *PredBlock : Block->preds()) {
3428+
// Prevent null or non-traversed (e.g. unreachable) blocks from
3429+
// causing the incoming UC for a block to be empty.
3430+
if (!PredBlock)
3431+
continue;
3432+
if (BlockStates.find(PredBlock->getBlockID()) == BlockStates.end())
3433+
continue;
3434+
CheckingState PredState = BlockStates[PredBlock->getBlockID()];
3435+
if (IntersectionEmpty) {
3436+
BlockState.UC = PredState.UC;
3437+
IntersectionEmpty = false;
3438+
}
3439+
else
3440+
BlockState.UC = IntersectUC(PredState.UC, BlockState.UC);
3441+
}
3442+
return BlockState;
3443+
}
3444+
3445+
// IntersectUC returns a bounds context resulting from taking the
3446+
// intersection of the contexts UC1 and UC2.
3447+
BoundsContextTy IntersectUC(BoundsContextTy UC1, BoundsContextTy UC2) {
3448+
BoundsContextTy IntersectedUC;
3449+
for (auto Pair : UC1) {
3450+
if (!Pair.second || !UC2[Pair.first])
3451+
continue;
3452+
if (Pair.second->isUnknown() || UC2[Pair.first]->isUnknown())
3453+
IntersectedUC[Pair.first] = CreateBoundsUnknown();
3454+
else if (EqualValue(S.Context, Pair.second, UC2[Pair.first], nullptr))
3455+
IntersectedUC[Pair.first] = Pair.second;
3456+
}
3457+
return IntersectedUC;
3458+
}
3459+
33183460
// If E appears in a set F in EQ, GetEqualExprSetContainingExpr
33193461
// returns F. Otherwise, it returns an empty set.
33203462
EqualExprTy GetEqualExprSetContainingExpr(Expr *E, EquivExprSets EQ) {
@@ -4124,7 +4266,7 @@ void Sema::CheckFunctionBodyBoundsDecls(FunctionDecl *FD, Stmt *Body) {
41244266
Collector.Analyze();
41254267
if (getLangOpts().DumpExtractedComparisonFacts)
41264268
Collector.DumpComparisonFacts(llvm::outs(), FD->getNameInfo().getName().getAsString());
4127-
Checker.TraverseCFG(Collector);
4269+
Checker.TraverseCFG(Collector, FD);
41284270
}
41294271
else {
41304272
// A CFG couldn't be constructed. CFG construction doesn't support

0 commit comments

Comments
 (0)