Skip to content

Commit

Permalink
[analyzer] Rework support for CFGScopeBegin, CFGScopeEnd, CFGLifetime…
Browse files Browse the repository at this point in the history
… elements

This patch reworks generation for the `CFGScopeBegin`, `CFGScopeEnd`,
and `CFGLiftimeEnd`, in a way that they are now compatible with each
other and `CFGAutomaticObjDtor`. All of the above elements are now
generated by a single code path, that conditionally inserts elements if
they are requested.

In addition, the handling of `goto` statements is improved.
The `goto` statement may leave multiple scopes (and trigger destruction
and lifetime end for the affected variables) and enter multiple scopes,
for example:
```lang=C++
{
  int s1;
  {
    int s2;
    goto label; // leaves s1, s2, and enters t1 t1
  }
}
{
  int t1;
  {
    int t2;
label:
  }
}
```
This is performed by first determining the shared parent scope of the
source and destination. And then emitting elements for exiting each
scope between the source and the parent, and entering each scope
between the parent and destination. All such elements are appended
to the source block, as one label may be reached from multiple scopes.

Finally, the approach for handling backward jumps is changed. When
connecting a source block to a destination block that requires the
insertion of additional elements, we put this element into a new block,
which is then linked between the source and the destination block.
For example:
```lang=C++
{
  int t;
label:
  // Destination block referred to as 'DB'
}
{
  // Source block referred to as 'SB'
  Obj s;
  goto label;
}
```

The jump between `SB` with terminator `T: goto` and `DB` should be
coupled with the following CFG elements:
```
CFGAutomaticObjDtor(s)
CFGLifetimeEnd(s)
CFGScopeEnd(s)
CFGScopeBegin(t)
```

To handle such situations, we create a new link (`LB`) that is linked as
the predecessor of `DB`, to which we transfer the terminator (`goto`
statement) of `SB`. Then `LB` is handled in the same manner as the
source block in the case of forward jumps.
This produces CFG that looks like this:
```
SB -> LB (T: goto) -> DB
```

Finally, the resulting block is linked as the successor of `SB`. Such an
approach uses existing handling of the `noreturn` destructors.
As a reminder, for each destructor of an automatic object that is
marked as `noreturn`, a new `noreturn` block (marked `NBn`) is
created, at the destructor is inserted at the end of it.
To illustrate, given two `noreturn` destructors, we will have:
```
SB -> NB1 (noreturn)
NB2 (noreturn)
LB (T:goto) -> DB
```

Reviewed By: ymandel, steakhal

Differential Revision: https://reviews.llvm.org/D153273
  • Loading branch information
tomasz-kaminski-sonarsource authored and Tomasz Kamiński committed Jul 18, 2023
1 parent 2306f89 commit d937836
Show file tree
Hide file tree
Showing 7 changed files with 569 additions and 317 deletions.
47 changes: 0 additions & 47 deletions clang/include/clang/Analysis/CFG.h
Original file line number Diff line number Diff line change
Expand Up @@ -1122,19 +1122,10 @@ class CFGBlock {
Elements.push_back(CFGScopeBegin(VD, S), C);
}

void prependScopeBegin(const VarDecl *VD, const Stmt *S,
BumpVectorContext &C) {
Elements.insert(Elements.rbegin(), 1, CFGScopeBegin(VD, S), C);
}

void appendScopeEnd(const VarDecl *VD, const Stmt *S, BumpVectorContext &C) {
Elements.push_back(CFGScopeEnd(VD, S), C);
}

void prependScopeEnd(const VarDecl *VD, const Stmt *S, BumpVectorContext &C) {
Elements.insert(Elements.rbegin(), 1, CFGScopeEnd(VD, S), C);
}

void appendBaseDtor(const CXXBaseSpecifier *BS, BumpVectorContext &C) {
Elements.push_back(CFGBaseDtor(BS), C);
}
Expand Down Expand Up @@ -1162,44 +1153,6 @@ class CFGBlock {
void appendDeleteDtor(CXXRecordDecl *RD, CXXDeleteExpr *DE, BumpVectorContext &C) {
Elements.push_back(CFGDeleteDtor(RD, DE), C);
}

// Destructors must be inserted in reversed order. So insertion is in two
// steps. First we prepare space for some number of elements, then we insert
// the elements beginning at the last position in prepared space.
iterator beginAutomaticObjDtorsInsert(iterator I, size_t Cnt,
BumpVectorContext &C) {
return iterator(Elements.insert(I.base(), Cnt,
CFGAutomaticObjDtor(nullptr, nullptr), C));
}
iterator insertAutomaticObjDtor(iterator I, VarDecl *VD, Stmt *S) {
*I = CFGAutomaticObjDtor(VD, S);
return ++I;
}

// Scope leaving must be performed in reversed order. So insertion is in two
// steps. First we prepare space for some number of elements, then we insert
// the elements beginning at the last position in prepared space.
iterator beginLifetimeEndsInsert(iterator I, size_t Cnt,
BumpVectorContext &C) {
return iterator(
Elements.insert(I.base(), Cnt, CFGLifetimeEnds(nullptr, nullptr), C));
}
iterator insertLifetimeEnds(iterator I, VarDecl *VD, Stmt *S) {
*I = CFGLifetimeEnds(VD, S);
return ++I;
}

// Scope leaving must be performed in reversed order. So insertion is in two
// steps. First we prepare space for some number of elements, then we insert
// the elements beginning at the last position in prepared space.
iterator beginScopeEndInsert(iterator I, size_t Cnt, BumpVectorContext &C) {
return iterator(
Elements.insert(I.base(), Cnt, CFGScopeEnd(nullptr, nullptr), C));
}
iterator insertScopeEnd(iterator I, VarDecl *VD, Stmt *S) {
*I = CFGScopeEnd(VD, S);
return ++I;
}
};

/// CFGCallback defines methods that should be called when a logical
Expand Down
Loading

0 comments on commit d937836

Please sign in to comment.