Skip to content

Commit 1e37ced

Browse files
committed
[clang][dataflow] Make cap on block visits configurable by caller.
Previously, we hard-coded the cap on block visits inside the framework. This patch enables the caller to specify the cap in the APIs for running an analysis.
1 parent 04952c5 commit 1e37ced

File tree

3 files changed

+42
-22
lines changed

3 files changed

+42
-22
lines changed

clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,14 @@ template <typename LatticeT> struct DataflowAnalysisState {
186186
/// the dataflow analysis cannot be performed successfully. Otherwise, calls
187187
/// `PostVisitCFG` on each CFG element with the final analysis results at that
188188
/// program point.
189+
///
190+
/// `MaxBlockVisits` caps the number of block visits during analysis. It doesn't
191+
/// distinguish between repeat visits to the same block and visits to distinct
192+
/// blocks. This parameter is a backstop to prevent infintite loops, in the case
193+
/// of bugs in the lattice and/or transfer functions that prevent the analysis
194+
/// from converging. The default value is essentially arbitrary -- large enough
195+
/// to accomodate what seems like any reasonable CFG, but still small enough to
196+
/// limit the cost of hitting the limit.
189197
template <typename AnalysisT>
190198
llvm::Expected<std::vector<
191199
std::optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>>
@@ -194,7 +202,8 @@ runDataflowAnalysis(
194202
const Environment &InitEnv,
195203
std::function<void(const CFGElement &, const DataflowAnalysisState<
196204
typename AnalysisT::Lattice> &)>
197-
PostVisitCFG = nullptr) {
205+
PostVisitCFG = nullptr,
206+
std::int32_t MaxBlockVisits = 20'000) {
198207
std::function<void(const CFGElement &,
199208
const TypeErasedDataflowAnalysisState &)>
200209
PostVisitCFGClosure = nullptr;
@@ -212,7 +221,7 @@ runDataflowAnalysis(
212221
}
213222

214223
auto TypeErasedBlockStates = runTypeErasedDataflowAnalysis(
215-
CFCtx, Analysis, InitEnv, PostVisitCFGClosure);
224+
CFCtx, Analysis, InitEnv, PostVisitCFGClosure, MaxBlockVisits);
216225
if (!TypeErasedBlockStates)
217226
return TypeErasedBlockStates.takeError();
218227

@@ -261,14 +270,23 @@ auto createAnalysis(ASTContext &ASTCtx, Environment &Env)
261270
/// iterations.
262271
/// - This limit is still low enough to keep runtimes acceptable (on typical
263272
/// machines) in cases where we hit the limit.
273+
///
274+
/// `MaxBlockVisits` caps the number of block visits during analysis. It doesn't
275+
/// distinguish between repeat visits to the same block and visits to distinct
276+
/// blocks. This parameter is a backstop to prevent infintite loops, in the case
277+
/// of bugs in the lattice and/or transfer functions that prevent the analysis
278+
/// from converging. The default value is essentially arbitrary -- large enough
279+
/// to accomodate what seems like any reasonable CFG, but still small enough to
280+
/// limit the cost of hitting the limit.
264281
template <typename AnalysisT, typename Diagnostic>
265282
llvm::Expected<llvm::SmallVector<Diagnostic>> diagnoseFunction(
266283
const FunctionDecl &FuncDecl, ASTContext &ASTCtx,
267284
llvm::function_ref<llvm::SmallVector<Diagnostic>(
268285
const CFGElement &, ASTContext &,
269286
const TransferStateForDiagnostics<typename AnalysisT::Lattice> &)>
270287
Diagnoser,
271-
std::int64_t MaxSATIterations = 1'000'000'000) {
288+
std::int64_t MaxSATIterations = 1'000'000'000,
289+
std::int32_t MaxBlockVisits = 20'000) {
272290
llvm::Expected<ControlFlowContext> Context =
273291
ControlFlowContext::build(FuncDecl);
274292
if (!Context)
@@ -293,7 +311,8 @@ llvm::Expected<llvm::SmallVector<Diagnostic>> diagnoseFunction(
293311
State.Lattice.Value),
294312
State.Env));
295313
llvm::move(EltDiagnostics, std::back_inserter(Diagnostics));
296-
})
314+
},
315+
MaxBlockVisits)
297316
.takeError())
298317
return std::move(Err);
299318

clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,20 @@ struct TypeErasedDataflowAnalysisState {
138138
/// dataflow analysis cannot be performed successfully. Otherwise, calls
139139
/// `PostVisitCFG` on each CFG element with the final analysis results at that
140140
/// program point.
141+
///
142+
/// `MaxBlockVisits` caps the number of block visits during analysis. It doesn't
143+
/// distinguish between repeat visits to the same block and visits to distinct
144+
/// blocks. This parameter is a backstop to prevent infintite loops, in the case
145+
/// of bugs in the lattice and/or transfer functions that prevent the analysis
146+
/// from converging.
141147
llvm::Expected<std::vector<std::optional<TypeErasedDataflowAnalysisState>>>
142148
runTypeErasedDataflowAnalysis(
143149
const ControlFlowContext &CFCtx, TypeErasedDataflowAnalysis &Analysis,
144150
const Environment &InitEnv,
145151
std::function<void(const CFGElement &,
146152
const TypeErasedDataflowAnalysisState &)>
147-
PostVisitCFG = nullptr);
153+
PostVisitCFG,
154+
std::int32_t MaxBlockVisits);
148155

149156
} // namespace dataflow
150157
} // namespace clang

clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,8 @@ runTypeErasedDataflowAnalysis(
498498
const Environment &InitEnv,
499499
std::function<void(const CFGElement &,
500500
const TypeErasedDataflowAnalysisState &)>
501-
PostVisitCFG) {
501+
PostVisitCFG,
502+
std::int32_t MaxBlockVisits) {
502503
PrettyStackTraceAnalysis CrashInfo(CFCtx, "runTypeErasedDataflowAnalysis");
503504

504505
std::optional<Environment> MaybeStartingEnv;
@@ -524,27 +525,20 @@ runTypeErasedDataflowAnalysis(
524525

525526
AnalysisContext AC(CFCtx, Analysis, StartingEnv, BlockStates);
526527

527-
// Bugs in lattices and transfer functions can prevent the analysis from
528-
// converging. To limit the damage (infinite loops) that these bugs can cause,
529-
// limit the number of iterations.
530-
// FIXME: Consider making the maximum number of iterations configurable.
531-
// FIXME: Consider restricting the number of backedges followed, rather than
532-
// iterations.
533-
// FIXME: Set up statistics (see llvm/ADT/Statistic.h) to count average number
534-
// of iterations, number of functions that time out, etc.
535-
static constexpr uint32_t MaxAverageVisitsPerBlock = 4;
536-
static constexpr uint32_t AbsoluteMaxIterations = 1 << 16;
537-
const uint32_t RelativeMaxIterations =
528+
// FIXME: remove relative cap. There isn't really any good setting for
529+
// `MaxAverageVisitsPerBlock`, so it has no clear value over using
530+
// `MaxBlockVisits` directly.
531+
static constexpr std::int32_t MaxAverageVisitsPerBlock = 4;
532+
const std::int32_t RelativeMaxBlockVisits =
538533
MaxAverageVisitsPerBlock * BlockStates.size();
539-
const uint32_t MaxIterations =
540-
std::min(RelativeMaxIterations, AbsoluteMaxIterations);
541-
uint32_t Iterations = 0;
534+
MaxBlockVisits = std::min(RelativeMaxBlockVisits, MaxBlockVisits);
535+
std::int32_t BlockVisits = 0;
542536
while (const CFGBlock *Block = Worklist.dequeue()) {
543537
LLVM_DEBUG(llvm::dbgs()
544538
<< "Processing Block " << Block->getBlockID() << "\n");
545-
if (++Iterations > MaxIterations) {
539+
if (++BlockVisits > MaxBlockVisits) {
546540
return llvm::createStringError(std::errc::timed_out,
547-
"maximum number of iterations reached");
541+
"maximum number of blocks processed");
548542
}
549543

550544
const std::optional<TypeErasedDataflowAnalysisState> &OldBlockState =

0 commit comments

Comments
 (0)