@@ -75,9 +75,8 @@ using TerminatorVisitorRetTy = std::pair<const Expr *, bool>;
75
75
class TerminatorVisitor
76
76
: public ConstStmtVisitor<TerminatorVisitor, TerminatorVisitorRetTy> {
77
77
public:
78
- TerminatorVisitor (const StmtToEnvMap &StmtToEnv, Environment &Env,
79
- int BlockSuccIdx)
80
- : StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx) {}
78
+ TerminatorVisitor (Environment &Env, int BlockSuccIdx)
79
+ : Env(Env), BlockSuccIdx(BlockSuccIdx) {}
81
80
82
81
TerminatorVisitorRetTy VisitIfStmt (const IfStmt *S) {
83
82
auto *Cond = S->getCond ();
@@ -126,19 +125,12 @@ class TerminatorVisitor
126
125
127
126
private:
128
127
TerminatorVisitorRetTy extendFlowCondition (const Expr &Cond) {
129
- // The terminator sub-expression might not be evaluated.
130
- if (Env.getValue (Cond) == nullptr )
131
- transfer (StmtToEnv, Cond, Env);
132
-
133
128
auto *Val = Env.get <BoolValue>(Cond);
134
- // Value merging depends on flow conditions from different environments
135
- // being mutually exclusive -- that is, they cannot both be true in their
136
- // entirety (even if they may share some clauses). So, we need *some* value
137
- // for the condition expression, even if just an atom.
138
- if (Val == nullptr ) {
139
- Val = &Env.makeAtomicBoolValue ();
140
- Env.setValue (Cond, *Val);
141
- }
129
+ // In transferCFGBlock(), we ensure that we always have a `Value` for the
130
+ // terminator condition, so assert this.
131
+ // We consciously assert ourselves instead of asserting via `cast()` so
132
+ // that we get a more meaningful line number if the assertion fails.
133
+ assert (Val != nullptr );
142
134
143
135
bool ConditionValue = true ;
144
136
// The condition must be inverted for the successor that encompasses the
@@ -152,7 +144,6 @@ class TerminatorVisitor
152
144
return {&Cond, ConditionValue};
153
145
}
154
146
155
- const StmtToEnvMap &StmtToEnv;
156
147
Environment &Env;
157
148
int BlockSuccIdx;
158
149
};
@@ -335,10 +326,8 @@ computeBlockInputState(const CFGBlock &Block, AnalysisContext &AC) {
335
326
// when the terminator is taken. Copy now.
336
327
TypeErasedDataflowAnalysisState Copy = MaybePredState->fork ();
337
328
338
- const StmtToEnvMap StmtToEnv (AC.CFCtx , AC.BlockStates );
339
329
auto [Cond, CondValue] =
340
- TerminatorVisitor (StmtToEnv, Copy.Env ,
341
- blockIndexInPredecessor (*Pred, Block))
330
+ TerminatorVisitor (Copy.Env , blockIndexInPredecessor (*Pred, Block))
342
331
.Visit (PredTerminatorStmt);
343
332
if (Cond != nullptr )
344
333
// FIXME: Call transferBranchTypeErased even if BuiltinTransferOpts
@@ -356,12 +345,13 @@ computeBlockInputState(const CFGBlock &Block, AnalysisContext &AC) {
356
345
357
346
// / Built-in transfer function for `CFGStmt`.
358
347
static void
359
- builtinTransferStatement (const CFGStmt &Elt,
348
+ builtinTransferStatement (unsigned CurBlockID, const CFGStmt &Elt,
360
349
TypeErasedDataflowAnalysisState &InputState,
361
350
AnalysisContext &AC) {
362
351
const Stmt *S = Elt.getStmt ();
363
352
assert (S != nullptr );
364
- transfer (StmtToEnvMap (AC.CFCtx , AC.BlockStates ), *S, InputState.Env );
353
+ transfer (StmtToEnvMap (AC.CFCtx , AC.BlockStates , CurBlockID, InputState), *S,
354
+ InputState.Env );
365
355
}
366
356
367
357
// / Built-in transfer function for `CFGInitializer`.
@@ -428,12 +418,12 @@ builtinTransferInitializer(const CFGInitializer &Elt,
428
418
}
429
419
}
430
420
431
- static void builtinTransfer (const CFGElement &Elt,
421
+ static void builtinTransfer (unsigned CurBlockID, const CFGElement &Elt,
432
422
TypeErasedDataflowAnalysisState &State,
433
423
AnalysisContext &AC) {
434
424
switch (Elt.getKind ()) {
435
425
case CFGElement::Statement:
436
- builtinTransferStatement (Elt.castAs <CFGStmt>(), State, AC);
426
+ builtinTransferStatement (CurBlockID, Elt.castAs <CFGStmt>(), State, AC);
437
427
break ;
438
428
case CFGElement::Initializer:
439
429
builtinTransferInitializer (Elt.castAs <CFGInitializer>(), State);
@@ -477,7 +467,7 @@ transferCFGBlock(const CFGBlock &Block, AnalysisContext &AC,
477
467
AC.Log .enterElement (Element);
478
468
// Built-in analysis
479
469
if (AC.Analysis .builtinOptions ()) {
480
- builtinTransfer (Element, State, AC);
470
+ builtinTransfer (Block. getBlockID (), Element, State, AC);
481
471
}
482
472
483
473
// User-provided analysis
@@ -489,6 +479,32 @@ transferCFGBlock(const CFGBlock &Block, AnalysisContext &AC,
489
479
}
490
480
AC.Log .recordState (State);
491
481
}
482
+
483
+ // If we have a terminator, evaluate its condition.
484
+ // This `Expr` may not appear as a `CFGElement` anywhere else, and it's
485
+ // important that we evaluate it here (rather than while processing the
486
+ // terminator) so that we put the corresponding value in the right
487
+ // environment.
488
+ if (const Expr *TerminatorCond =
489
+ dyn_cast_or_null<Expr>(Block.getTerminatorCondition ())) {
490
+ if (State.Env .getValue (*TerminatorCond) == nullptr )
491
+ // FIXME: This only runs the builtin transfer, not the analysis-specific
492
+ // transfer. Fixing this isn't trivial, as the analysis-specific transfer
493
+ // takes a `CFGElement` as input, but some expressions only show up as a
494
+ // terminator condition, but not as a `CFGElement`. The condition of an if
495
+ // statement is one such example.
496
+ transfer (
497
+ StmtToEnvMap (AC.CFCtx , AC.BlockStates , Block.getBlockID (), State),
498
+ *TerminatorCond, State.Env );
499
+
500
+ // If the transfer function didn't produce a value, create an atom so that
501
+ // we have *some* value for the condition expression. This ensures that
502
+ // when we extend the flow condition, it actually changes.
503
+ if (State.Env .getValue (*TerminatorCond) == nullptr )
504
+ State.Env .setValue (*TerminatorCond, State.Env .makeAtomicBoolValue ());
505
+ AC.Log .recordState (State);
506
+ }
507
+
492
508
return State;
493
509
}
494
510
0 commit comments