@@ -178,13 +178,50 @@ template <typename LatticeT> struct DataflowAnalysisState {
178
178
Environment Env;
179
179
};
180
180
181
+ // / A callback to be called with the state before or after visiting a CFG
182
+ // / element.
183
+ template <typename AnalysisT>
184
+ using CFGEltCallback = std::function<void (
185
+ const CFGElement &,
186
+ const DataflowAnalysisState<typename AnalysisT::Lattice> &)>;
187
+
188
+ // / A pair of callbacks to be called with the state before and after visiting a
189
+ // / CFG element.
190
+ // / Either or both of the callbacks may be null.
191
+ template <typename AnalysisT> struct CFGEltCallbacks {
192
+ CFGEltCallback<AnalysisT> Before;
193
+ CFGEltCallback<AnalysisT> After;
194
+ };
195
+
196
+ // / A callback for performing diagnosis on a CFG element, called with the state
197
+ // / before or after visiting that CFG element. Returns a list of diagnostics
198
+ // / to emit (if any).
199
+ template <typename AnalysisT, typename Diagnostic>
200
+ using DiagnosisCallback = llvm::function_ref<llvm::SmallVector<Diagnostic>(
201
+ const CFGElement &, ASTContext &,
202
+ const TransferStateForDiagnostics<typename AnalysisT::Lattice> &)>;
203
+
204
+ // / A pair of callbacks for performing diagnosis on a CFG element, called with
205
+ // / the state before and after visiting that CFG element.
206
+ // / Either or both of the callbacks may be null.
207
+ template <typename AnalysisT, typename Diagnostic> struct DiagnosisCallbacks {
208
+ DiagnosisCallback<AnalysisT, Diagnostic> Before;
209
+ DiagnosisCallback<AnalysisT, Diagnostic> After;
210
+ };
211
+
212
+ // / Default for the maximum number of SAT solver iterations during analysis.
213
+ inline constexpr std::int64_t kDefaultMaxSATIterations = 1'000'000'000 ;
214
+
215
+ // / Default for the maximum number of block visits during analysis.
216
+ inline constexpr std::int32_t kDefaultMaxBlockVisits = 20'000 ;
217
+
181
218
// / Performs dataflow analysis and returns a mapping from basic block IDs to
182
219
// / dataflow analysis states that model the respective basic blocks. The
183
220
// / returned vector, if any, will have the same size as the number of CFG
184
221
// / blocks, with indices corresponding to basic block IDs. Returns an error if
185
222
// / the dataflow analysis cannot be performed successfully. Otherwise, calls
186
- // / `PostVisitCFG ` on each CFG element with the final analysis results at that
187
- // / program point.
223
+ // / `PostAnalysisCallbacks ` on each CFG element with the final analysis results
224
+ // / before and after that program point.
188
225
// /
189
226
// / `MaxBlockVisits` caps the number of block visits during analysis. See
190
227
// / `runTypeErasedDataflowAnalysis` for a full description. The default value is
@@ -194,30 +231,40 @@ template <typename LatticeT> struct DataflowAnalysisState {
194
231
template <typename AnalysisT>
195
232
llvm::Expected<std::vector<
196
233
std::optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>>
197
- runDataflowAnalysis (
198
- const AdornedCFG &ACFG, AnalysisT &Analysis, const Environment &InitEnv,
199
- std::function<void (const CFGElement &, const DataflowAnalysisState<
200
- typename AnalysisT::Lattice> &)>
201
- PostVisitCFG = nullptr ,
202
- std::int32_t MaxBlockVisits = 20'000 ) {
203
- std::function<void (const CFGElement &,
204
- const TypeErasedDataflowAnalysisState &)>
205
- PostVisitCFGClosure = nullptr ;
206
- if (PostVisitCFG) {
207
- PostVisitCFGClosure = [&PostVisitCFG](
208
- const CFGElement &Element,
209
- const TypeErasedDataflowAnalysisState &State) {
210
- auto *Lattice =
211
- llvm::any_cast<typename AnalysisT::Lattice>(&State.Lattice .Value );
212
- // FIXME: we should not be copying the environment here!
213
- // Ultimately the PostVisitCFG only gets a const reference anyway.
214
- PostVisitCFG (Element, DataflowAnalysisState<typename AnalysisT::Lattice>{
215
- *Lattice, State.Env .fork ()});
216
- };
234
+ runDataflowAnalysis (const AdornedCFG &ACFG, AnalysisT &Analysis,
235
+ const Environment &InitEnv,
236
+ CFGEltCallbacks<AnalysisT> PostAnalysisCallbacks,
237
+ std::int32_t MaxBlockVisits = kDefaultMaxBlockVisits ) {
238
+ CFGEltCallbacksTypeErased TypeErasedCallbacks;
239
+ if (PostAnalysisCallbacks.Before ) {
240
+ TypeErasedCallbacks.Before =
241
+ [&PostAnalysisCallbacks](const CFGElement &Element,
242
+ const TypeErasedDataflowAnalysisState &State) {
243
+ auto *Lattice =
244
+ llvm::any_cast<typename AnalysisT::Lattice>(&State.Lattice .Value );
245
+ // FIXME: we should not be copying the environment here!
246
+ // Ultimately the `CFGEltCallback` only gets a const reference anyway.
247
+ PostAnalysisCallbacks.Before (
248
+ Element, DataflowAnalysisState<typename AnalysisT::Lattice>{
249
+ *Lattice, State.Env .fork ()});
250
+ };
251
+ }
252
+ if (PostAnalysisCallbacks.After ) {
253
+ TypeErasedCallbacks.After =
254
+ [&PostAnalysisCallbacks](const CFGElement &Element,
255
+ const TypeErasedDataflowAnalysisState &State) {
256
+ auto *Lattice =
257
+ llvm::any_cast<typename AnalysisT::Lattice>(&State.Lattice .Value );
258
+ // FIXME: we should not be copying the environment here!
259
+ // Ultimately the `CFGEltCallback` only gets a const reference anyway.
260
+ PostAnalysisCallbacks.After (
261
+ Element, DataflowAnalysisState<typename AnalysisT::Lattice>{
262
+ *Lattice, State.Env .fork ()});
263
+ };
217
264
}
218
265
219
266
auto TypeErasedBlockStates = runTypeErasedDataflowAnalysis (
220
- ACFG, Analysis, InitEnv, PostVisitCFGClosure , MaxBlockVisits);
267
+ ACFG, Analysis, InitEnv, TypeErasedCallbacks , MaxBlockVisits);
221
268
if (!TypeErasedBlockStates)
222
269
return TypeErasedBlockStates.takeError ();
223
270
@@ -239,6 +286,22 @@ runDataflowAnalysis(
239
286
return std::move (BlockStates);
240
287
}
241
288
289
+ // / Overload that takes only one post-analysis callback, which is run on the
290
+ // / state after visiting the `CFGElement`. This is provided for backwards
291
+ // / compatibility; new callers should call the overload taking `CFGEltCallbacks`
292
+ // / instead.
293
+ template <typename AnalysisT>
294
+ llvm::Expected<std::vector<
295
+ std::optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>>
296
+ runDataflowAnalysis (
297
+ const AdornedCFG &ACFG, AnalysisT &Analysis, const Environment &InitEnv,
298
+ CFGEltCallback<AnalysisT> PostAnalysisCallbackAfterElt = nullptr ,
299
+ std::int32_t MaxBlockVisits = kDefaultMaxBlockVisits ) {
300
+ return runDataflowAnalysis (ACFG, Analysis, InitEnv,
301
+ {nullptr , PostAnalysisCallbackAfterElt},
302
+ MaxBlockVisits);
303
+ }
304
+
242
305
// Create an analysis class that is derived from `DataflowAnalysis`. This is an
243
306
// SFINAE adapter that allows us to call two different variants of constructor
244
307
// (either with or without the optional `Environment` parameter).
@@ -271,14 +334,11 @@ auto createAnalysis(ASTContext &ASTCtx, Environment &Env)
271
334
// / `runDataflowAnalysis` for a full description and explanation of the default
272
335
// / value.
273
336
template <typename AnalysisT, typename Diagnostic>
274
- llvm::Expected<llvm::SmallVector<Diagnostic>> diagnoseFunction (
275
- const FunctionDecl &FuncDecl, ASTContext &ASTCtx,
276
- llvm::function_ref<llvm::SmallVector<Diagnostic>(
277
- const CFGElement &, ASTContext &,
278
- const TransferStateForDiagnostics<typename AnalysisT::Lattice> &)>
279
- Diagnoser,
280
- std::int64_t MaxSATIterations = 1'000'000'000,
281
- std::int32_t MaxBlockVisits = 20'000) {
337
+ llvm::Expected<llvm::SmallVector<Diagnostic>>
338
+ diagnoseFunction (const FunctionDecl &FuncDecl, ASTContext &ASTCtx,
339
+ DiagnosisCallbacks<AnalysisT, Diagnostic> Diagnoser,
340
+ std::int64_t MaxSATIterations = kDefaultMaxSATIterations ,
341
+ std::int32_t MaxBlockVisits = kDefaultMaxBlockVisits ) {
282
342
llvm::Expected<AdornedCFG> Context = AdornedCFG::build (FuncDecl);
283
343
if (!Context)
284
344
return Context.takeError ();
@@ -288,21 +348,38 @@ llvm::Expected<llvm::SmallVector<Diagnostic>> diagnoseFunction(
288
348
Environment Env (AnalysisContext, FuncDecl);
289
349
AnalysisT Analysis = createAnalysis<AnalysisT>(ASTCtx, Env);
290
350
llvm::SmallVector<Diagnostic> Diagnostics;
351
+ CFGEltCallbacksTypeErased PostAnalysisCallbacks;
352
+ if (Diagnoser.Before ) {
353
+ PostAnalysisCallbacks.Before =
354
+ [&ASTCtx, &Diagnoser,
355
+ &Diagnostics](const CFGElement &Elt,
356
+ const TypeErasedDataflowAnalysisState &State) mutable {
357
+ auto EltDiagnostics = Diagnoser.Before (
358
+ Elt, ASTCtx,
359
+ TransferStateForDiagnostics<typename AnalysisT::Lattice>(
360
+ llvm::any_cast<const typename AnalysisT::Lattice &>(
361
+ State.Lattice .Value ),
362
+ State.Env ));
363
+ llvm::move (EltDiagnostics, std::back_inserter (Diagnostics));
364
+ };
365
+ }
366
+ if (Diagnoser.After ) {
367
+ PostAnalysisCallbacks.After =
368
+ [&ASTCtx, &Diagnoser,
369
+ &Diagnostics](const CFGElement &Elt,
370
+ const TypeErasedDataflowAnalysisState &State) mutable {
371
+ auto EltDiagnostics = Diagnoser.After (
372
+ Elt, ASTCtx,
373
+ TransferStateForDiagnostics<typename AnalysisT::Lattice>(
374
+ llvm::any_cast<const typename AnalysisT::Lattice &>(
375
+ State.Lattice .Value ),
376
+ State.Env ));
377
+ llvm::move (EltDiagnostics, std::back_inserter (Diagnostics));
378
+ };
379
+ }
291
380
if (llvm::Error Err =
292
- runTypeErasedDataflowAnalysis (
293
- *Context, Analysis, Env,
294
- [&ASTCtx, &Diagnoser, &Diagnostics](
295
- const CFGElement &Elt,
296
- const TypeErasedDataflowAnalysisState &State) mutable {
297
- auto EltDiagnostics = Diagnoser (
298
- Elt, ASTCtx,
299
- TransferStateForDiagnostics<typename AnalysisT::Lattice>(
300
- llvm::any_cast<const typename AnalysisT::Lattice &>(
301
- State.Lattice .Value ),
302
- State.Env ));
303
- llvm::move (EltDiagnostics, std::back_inserter (Diagnostics));
304
- },
305
- MaxBlockVisits)
381
+ runTypeErasedDataflowAnalysis (*Context, Analysis, Env,
382
+ PostAnalysisCallbacks, MaxBlockVisits)
306
383
.takeError ())
307
384
return std::move (Err);
308
385
@@ -313,6 +390,21 @@ llvm::Expected<llvm::SmallVector<Diagnostic>> diagnoseFunction(
313
390
return Diagnostics;
314
391
}
315
392
393
+ // / Overload that takes only one diagnosis callback, which is run on the state
394
+ // / after visiting the `CFGElement`. This is provided for backwards
395
+ // / compatibility; new callers should call the overload taking
396
+ // / `DiagnosisCallbacks` instead.
397
+ template <typename AnalysisT, typename Diagnostic>
398
+ llvm::Expected<llvm::SmallVector<Diagnostic>>
399
+ diagnoseFunction (const FunctionDecl &FuncDecl, ASTContext &ASTCtx,
400
+ DiagnosisCallback<AnalysisT, Diagnostic> Diagnoser,
401
+ std::int64_t MaxSATIterations = kDefaultMaxSATIterations ,
402
+ std::int32_t MaxBlockVisits = kDefaultMaxBlockVisits ) {
403
+ DiagnosisCallbacks<AnalysisT, Diagnostic> Callbacks = {nullptr , Diagnoser};
404
+ return diagnoseFunction (FuncDecl, ASTCtx, Callbacks, MaxSATIterations,
405
+ MaxBlockVisits);
406
+ }
407
+
316
408
// / Abstract base class for dataflow "models": reusable analysis components that
317
409
// / model a particular aspect of program semantics in the `Environment`. For
318
410
// / example, a model may capture a type and its related functions.
0 commit comments