@@ -37,6 +37,10 @@ void BoundsAnalysis::WidenBounds(FunctionDecl *FD) {
37
37
// iterate WorkList.
38
38
WorkList.append (EB);
39
39
BlockMap[B] = EB;
40
+
41
+ // Mark the In set for the Entry block as "empty". The Out set for the
42
+ // Entry block would be marked as "empty" in ComputeOutSets.
43
+ EB->IsInSetEmpty = B == &Cfg->getEntry ();
40
44
}
41
45
42
46
// At this time, BlockMap only contains reachable blocks. We iterate through
@@ -83,13 +87,48 @@ void BoundsAnalysis::InitInOutSets() {
83
87
}
84
88
}
85
89
90
+ bool BoundsAnalysis::CheckIsSwitchCaseNull (ElevatedCFGBlock *EB) {
91
+ if (const auto *CS = dyn_cast_or_null<CaseStmt>(EB->Block ->getLabel ())) {
92
+
93
+ // We mimic how clang (in SemaStmt.cpp) gets the value of a switch case. It
94
+ // invokes EvaluateKnownConstInt and we do the same here. SemaStmt has
95
+ // already extended/truncated the case value to fit the integer range and
96
+ // EvaluateKnownConstInt gives us that value.
97
+ llvm::APSInt LHSVal = CS->getLHS ()->EvaluateKnownConstInt (Ctx);
98
+ llvm::APSInt LHSZero (LHSVal.getBitWidth (), LHSVal.isUnsigned ());
99
+ if (llvm::APSInt::compareValues (LHSVal, LHSZero) == 0 )
100
+ return true ;
101
+
102
+ // Check if the case statement is of the form "case LHS ... RHS" (a GNU
103
+ // extension).
104
+ if (CS->caseStmtIsGNURange ()) {
105
+ llvm::APSInt RHSVal = CS->getRHS ()->EvaluateKnownConstInt (Ctx);
106
+ llvm::APSInt RHSZero (RHSVal.getBitWidth (), RHSVal.isUnsigned ());
107
+ if (llvm::APSInt::compareValues (RHSVal, RHSZero) == 0 )
108
+ return true ;
109
+
110
+ // Check if 0 if contained within the range [LHS, RHS].
111
+ return (LHSVal <= LHSZero && RHSZero <= RHSVal) ||
112
+ (LHSVal >= LHSZero && RHSZero >= RHSVal);
113
+ }
114
+ return false ;
115
+ }
116
+ return true ;
117
+ }
118
+
86
119
void BoundsAnalysis::ComputeGenSets () {
87
120
// If there is an edge B1->B2 and the edge condition is of the form
88
121
// "if (*(p + i))" then Gen[B1] = {B2, p:i} .
89
122
123
+ // Here, EB is B2.
90
124
for (const auto item : BlockMap) {
91
125
ElevatedCFGBlock *EB = item.second ;
92
126
127
+ // Check if this is a switch case and whether the case label is non-null.
128
+ // We can only widen the bounds for a non-null case label.
129
+ bool IsSwitchCaseNull = CheckIsSwitchCaseNull (EB);
130
+
131
+ // Iterate through all preds of EB.
93
132
for (const CFGBlock *pred : EB->Block ->preds ()) {
94
133
if (SkipBlock (pred))
95
134
continue ;
@@ -103,10 +142,39 @@ void BoundsAnalysis::ComputeGenSets() {
103
142
// Here we have the edges (B1->B2) and (B1->B3). We can add "p:i" only
104
143
// on the true edge. Which means we will add the following entry to
105
144
// Gen[B1]: {B2, p:i}
106
- if (!pred->succ_empty () && *pred->succs ().begin () == EB->Block )
107
- // Get the edge condition and fill the Gen set.
108
- if (Expr *E = GetTerminatorCondition (pred))
145
+
146
+ // Check if EB is on a true edge of pred. The false edge (including the
147
+ // default case for a switch) is always the last edge in the list of
148
+ // edges. So we check that EB is not on the last edge for pred.
149
+
150
+ // TODO: Allow bounds widening for the default case of a switch-case.
151
+ // If we establish that another label in a switch statement tests for 0,
152
+ // then the default case will handle non-zero case, and the bounds can be
153
+ // widened there. The following github issue tracks this:
154
+ // https://github.com/microsoft/checkedc-clang/issues/818.
155
+
156
+ if (pred->succ_size () && EB->Block != *(pred->succs ().end () - 1 )) {
157
+ // Get the edge condition and fill the Gen set.
158
+ if (Expr *E = GetTerminatorCondition (pred)) {
159
+
160
+ // Check if the pred ends in a switch statement.
161
+ if (isa<SwitchStmt>(pred->getTerminatorStmt ())) {
162
+ // We can widen the bounds only if the current block has a non-null
163
+ // case statement.
164
+ if (IsSwitchCaseNull)
165
+ continue ;
166
+
167
+ // If the switch expression is integral, strip off the
168
+ // IntegralCast.
169
+ if (auto *CE = dyn_cast<CastExpr>(E)) {
170
+ if (CE->getCastKind () == CastKind::CK_IntegralCast)
171
+ E = CE->getSubExpr ();
172
+ }
173
+ }
174
+
109
175
FillGenSet (E, BlockMap[pred], EB);
176
+ }
177
+ }
110
178
}
111
179
}
112
180
}
@@ -643,6 +711,10 @@ void BoundsAnalysis::ComputeOutSets(ElevatedCFGBlock *EB,
643
711
644
712
EB->Out [succ] = Union (Diff, EB->Gen [succ]);
645
713
714
+ // The Out set on an edge is marked "empty" if the In set is marked "empty"
715
+ // and the Gen set on that edge is empty.
716
+ EB->IsOutSetEmpty [succ] = EB->IsInSetEmpty && !EB->Gen [succ].size ();
717
+
646
718
if (Differ (OldOut, EB->Out [succ]))
647
719
WorkList.append (BlockMap[succ]);
648
720
}
@@ -666,6 +738,8 @@ Expr *BoundsAnalysis::GetTerminatorCondition(const CFGBlock *B) const {
666
738
return const_cast <Expr *>(WhileS->getCond ());
667
739
if (const auto *ForS = dyn_cast<ForStmt>(S))
668
740
return const_cast <Expr *>(ForS->getCond ());
741
+ if (const auto *SwitchS = dyn_cast<SwitchStmt>(S))
742
+ return const_cast <Expr *>(SwitchS->getCond ());
669
743
}
670
744
return nullptr ;
671
745
}
0 commit comments