Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions clang/lib/Analysis/CFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2085,9 +2085,24 @@ void CFGBuilder::prependAutomaticObjLifetimeWithTerminator(
if (!BuildOpts.AddLifetime)
return;
BumpVectorContext &C = cfg->getBumpVectorContext();

// Note that B is the LocalScope iterator associated with the backpatched
// jump and E is the LocalScope iterator associated with the jump target.
//
// To go from B to E, one first goes up the scopes from B
// to PofB, then sideways in one scope from PofB to PofE and then down
// the scopes from PofE to E.
// The lifetime of all objects between B and PofE end.
//
// Get E's ancestor that has the same LocalScope as one of B's ancestors.
LocalScope::const_iterator PofE = E.shared_parent(B);
int Dist = B.distance(PofE);
if (Dist <= 0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the distance be less than 0?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While Dist cannot be less than 0 if the distance function is called as intended, it is possible to provide arguments to it such that the distance between two LocalScope iterators is less than zero. For example, this can happen if the control comes here for a "forward" jumping goto statement (which should not ideally happen). Moreover, this kind of check is there in two other places too and they have used a <= check (see lines 1770 - 1777).

return;

CFGBlock::iterator InsertPos =
Blk->beginLifetimeEndsInsert(Blk->end(), B.distance(E), C);
for (LocalScope::const_iterator I = B; I != E; ++I) {
Blk->beginLifetimeEndsInsert(Blk->end(), Dist, C);
for (LocalScope::const_iterator I = B; I != PofE; ++I) {
InsertPos =
Blk->insertLifetimeEnds(InsertPos, *I, Blk->getTerminatorStmt());
}
Expand Down
156 changes: 156 additions & 0 deletions clang/test/Analysis/lifetime-cfg-output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -781,3 +781,159 @@ int backpatched_goto() {
goto label;
i++;
}

extern int bar(char *s, int n);

// CHECK: [B3 (ENTRY)]
// CHECK-NEXT: Succs (1): B2
// CHECK: [B1]
// CHECK-NEXT: label:
// CHECK-NEXT: 1: bar
// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(char *, int))
// CHECK-NEXT: 3: x
// CHECK-NEXT: 4: &[B1.3]
// CHECK-NEXT: 5: 1
// CHECK-NEXT: 6: [B1.2]([B1.4], [B1.5])
// CHECK-NEXT: 7: [B2.1] (Lifetime ends)
// CHECK-NEXT: T: goto label;
// CHECK-NEXT: Preds (2): B2 B1
// CHECK-NEXT: Succs (1): B1
// CHECK: [B2]
// CHECK-NEXT: 1: char x;
// CHECK-NEXT: Preds (1): B3
// CHECK-NEXT: Succs (1): B1
// CHECK: [B0 (EXIT)]

// test case from CodeGen/lifetime2.c
void backpatched_goto1() {
{
char x;
label:
bar(&x, 1);
}
goto label;
}


// CHECK: [B4 (ENTRY)]
// CHECK-NEXT: Succs (1): B3
// CHECK: [B1]
// CHECK-NEXT: 1: [B2.2] (Lifetime ends)
// CHECK-NEXT: Succs (1): B0
// CHECK: [B2]
// CHECK-NEXT: 1: (CXXConstructExpr, struct B)
// CHECK-NEXT: 2: B c;
// CHECK-NEXT: 3: [B2.2] (Lifetime ends)
// CHECK-NEXT: T: goto label;
// CHECK-NEXT: Preds (1): B3
// CHECK-NEXT: Succs (1): B3
// CHECK: [B3]
// CHECK-NEXT: label:
// CHECK-NEXT: 1: (CXXConstructExpr, struct B)
// CHECK-NEXT: 2: B b;
// CHECK-NEXT: 3: [B3.2] (Lifetime ends)
// CHECK-NEXT: 4: 1
// CHECK-NEXT: 5: [B3.4] (ImplicitCastExpr, IntegralToBoolean, _Bool)
// CHECK-NEXT: T: if [B3.5]
// CHECK-NEXT: Preds (2): B2 B4
// CHECK-NEXT: Succs (2): B2 B0(Unreachable)
// CHECK: [B0 (EXIT)]
// CHECK-NEXT: Preds (2): B1 B3(Unreachable)

int backpatched_goto2() {
{
label:
B b;
}
if (1){
B c;
goto label;
}
}


// The two test cases below illustrate the need to traverse up
// the tree of nested scopes to find the nearest common ancestor.

// CHECK: [B4 (ENTRY)]
// CHECK-NEXT: Succs (1): B3
// CHECK: [B1]
// CHECK-NEXT: 1: [B2.9] (Lifetime ends)
// CHECK-NEXT: Succs (1): B0
// CHECK: [B2]
// CHECK-NEXT: label:
// CHECK-NEXT: 1: bar
// CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(char *, int))
// CHECK-NEXT: 3: x
// CHECK-NEXT: 4: &[B2.3]
// CHECK-NEXT: 5: 1
// CHECK-NEXT: 6: [B2.2]([B2.4], [B2.5])
// CHECK-NEXT: 7: [B3.1] (Lifetime ends)
// CHECK-NEXT: 8: (CXXConstructExpr, struct B)
// CHECK-NEXT: 9: B b;
// CHECK-NEXT: 10: [B2.9] (Lifetime ends)
// CHECK-NEXT: T: goto label;
// CHECK-NEXT: Preds (2): B3 B2
// CHECK-NEXT: Succs (1): B2
// CHECK: [B3]
// CHECK-NEXT: 1: char x;
// CHECK-NEXT: Preds (1): B4
// CHECK-NEXT: Succs (1): B2
// CHECK: [B0 (EXIT)]
// CHECK-NEXT: Preds (1): B1

void backpatched_goto3() {
{
char x;
label:
bar(&x, 1);
}
{
B b;
goto label;
}
}

// CHECK: [B4 (ENTRY)]
// CHECK-NEXT: Succs (1): B3
// CHECK: [B1]
// CHECK-NEXT: 1: [B2.11] (Lifetime ends)
// CHECK-NEXT: 2: [B2.9] (Lifetime ends)
// CHECK-NEXT: Succs (1): B0
// CHECK: [B2]
// CHECK-NEXT: label:
// CHECK-NEXT: 1: bar
// CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(char *, int))
// CHECK-NEXT: 3: x
// CHECK-NEXT: 4: &[B2.3]
// CHECK-NEXT: 5: 1
// CHECK-NEXT: 6: [B2.2]([B2.4], [B2.5])
// CHECK-NEXT: 7: [B3.1] (Lifetime ends)
// CHECK-NEXT: 8: (CXXConstructExpr, struct B)
// CHECK-NEXT: 9: B c;
// CHECK-NEXT: 10: (CXXConstructExpr, struct B)
// CHECK-NEXT: 11: B b;
// CHECK-NEXT: 12: [B2.11] (Lifetime ends)
// CHECK-NEXT: 13: [B2.9] (Lifetime ends)
// CHECK-NEXT: T: goto label;
// CHECK-NEXT: Preds (2): B3 B2
// CHECK-NEXT: Succs (1): B2
// CHECK: [B3]
// CHECK-NEXT: 1: char x;
// CHECK-NEXT: Preds (1): B4
// CHECK-NEXT: Succs (1): B2
// CHECK: [B0 (EXIT)]
// CHECK-NEXT: Preds (1): B1

void backpatched_goto4() {
{
char x;
label:
bar(&x, 1);
}
B c;
{
B b;
goto label;
}
}