diff options
| author | Akira Hatanaka <ahatanaka@apple.com> | 2017-01-25 22:55:13 +0000 | 
|---|---|---|
| committer | Akira Hatanaka <ahatanaka@apple.com> | 2017-01-25 22:55:13 +0000 | 
| commit | fdcd18b4c90ba59dcb8bd7b8267e61f6e0f016eb (patch) | |
| tree | 8d5f9d462037999812255bea4f69f91158002c96 | |
| parent | 1f5f06435d5a8958127faf454dbe2205d2fc2b73 (diff) | |
| download | bcm5719-llvm-fdcd18b4c90ba59dcb8bd7b8267e61f6e0f016eb.tar.gz bcm5719-llvm-fdcd18b4c90ba59dcb8bd7b8267e61f6e0f016eb.zip  | |
[CodeGen] Suppress emission of lifetime markers if a label has been seen
in the current lexical scope.
clang currently emits the lifetime.start marker of a variable when the
variable comes into scope even though a variable's lifetime starts at
the entry of the block with which it is associated, according to the C
standard. This normally doesn't cause any problems, but in the rare case
where a goto jumps backwards past the variable declaration to an earlier
point in the block (see the test case added to lifetime2.c), it can
cause mis-compilation.
To prevent such mis-compiles, this commit conservatively disables
emitting lifetime variables when a label has been seen in the current
block.
This problem was discussed on cfe-dev here: 
http://lists.llvm.org/pipermail/cfe-dev/2016-July/050066.html
rdar://problem/30153946
Differential Revision: https://reviews.llvm.org/D27680
llvm-svn: 293106
| -rw-r--r-- | clang/lib/CodeGen/CGDecl.cpp | 20 | ||||
| -rw-r--r-- | clang/lib/CodeGen/CodeGenFunction.h | 11 | ||||
| -rw-r--r-- | clang/test/CodeGen/lifetime2.c | 31 | 
3 files changed, 51 insertions, 11 deletions
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 0a88b2310be..b7c1743f62e 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -1022,11 +1022,21 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) {        // Emit a lifetime intrinsic if meaningful. There's no point in doing this        // if we don't have a valid insertion point (?).        if (HaveInsertPoint() && !IsMSCatchParam) { -        // goto or switch-case statements can break lifetime into several -        // regions which need more efforts to handle them correctly. PR28267 -        // This is rare case, but it's better just omit intrinsics than have -        // them incorrectly placed. -        if (!Bypasses.IsBypassed(&D)) { +        // If there's a jump into the lifetime of this variable, its lifetime +        // gets broken up into several regions in IR, which requires more work +        // to handle correctly. For now, just omit the intrinsics; this is a +        // rare case, and it's better to just be conservatively correct. +        // PR28267. +        // +        // We have to do this in all language modes if there's a jump past the +        // declaration. We also have to do it in C if there's a jump to an +        // earlier point in the current block because non-VLA lifetimes begin as +        // soon as the containing block is entered, not when its variables +        // actually come into scope; suppressing the lifetime annotations +        // completely in this case is unnecessarily pessimistic, but again, this +        // is rare. +        if (!Bypasses.IsBypassed(&D) && +            !(!getLangOpts().CPlusPlus && hasLabelBeenSeenInCurrentScope())) {            uint64_t size = CGM.getDataLayout().getTypeAllocSize(allocaTy);            emission.SizeForLifetimeMarkers =                EmitLifetimeStart(size, address.getPointer()); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 82ab5c3781e..8d7fd3f80ba 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -212,6 +212,13 @@ public:    /// value. This is invalid iff the function has no return value.    Address ReturnValue; +  /// Return true if a label was seen in the current scope. +  bool hasLabelBeenSeenInCurrentScope() const { +    if (CurLexicalScope) +      return CurLexicalScope->hasLabels(); +    return !LabelMap.empty(); +  } +    /// AllocaInsertPoint - This is an instruction in the entry block before which    /// we prefer to insert allocas.    llvm::AssertingVH<llvm::Instruction> AllocaInsertPt; @@ -620,6 +627,10 @@ public:          rescopeLabels();      } +    bool hasLabels() const { +      return !Labels.empty(); +    } +      void rescopeLabels();    }; diff --git a/clang/test/CodeGen/lifetime2.c b/clang/test/CodeGen/lifetime2.c index 4374b3c279c..620b43c9e1d 100644 --- a/clang/test/CodeGen/lifetime2.c +++ b/clang/test/CodeGen/lifetime2.c @@ -1,7 +1,7 @@ -// RUN: %clang -S -emit-llvm -o - -O2 %s | FileCheck %s -check-prefixes=CHECK,O2 -// RUN: %clang -S -emit-llvm -o - -O2 -Xclang -disable-lifetime-markers %s \ +// RUN: %clang_cc1 -S -emit-llvm -o - -O2 -disable-llvm-passes %s | FileCheck %s -check-prefixes=CHECK,O2 +// RUN: %clang_cc1 -S -emit-llvm -o - -O2 -disable-lifetime-markers %s \  // RUN:       | FileCheck %s -check-prefixes=CHECK,O0 -// RUN: %clang -S -emit-llvm -o - -O0 %s | FileCheck %s -check-prefixes=CHECK,O0 +// RUN: %clang_cc1 -S -emit-llvm -o - -O0 %s | FileCheck %s -check-prefixes=CHECK,O0  extern int bar(char *A, int n); @@ -25,13 +25,11 @@ void no_goto_bypass() {    char x;  l1:    bar(&x, 1); -  // O2: @llvm.lifetime.start(i64 5 -  // O2: @llvm.lifetime.end(i64 5    char y[5];    bar(y, 5);    goto l1;    // Infinite loop -  // O2-NOT: @llvm.lifetime.end(i64 1 +  // O2-NOT: @llvm.lifetime.end(  }  // CHECK-LABEL: @goto_bypass @@ -91,3 +89,24 @@ void indirect_jump(int n) {  L:    bar(&x, 1);  } + +// O2-LABEL: define i32 @jump_backward_over_declaration( +// O2-NOT: call void @llvm.lifetime.{{.*}}(i64 4, + +extern void foo2(int p); + +int jump_backward_over_declaration(int a) { +  int *p = 0; +label1: +  if (p) { +    foo2(*p); +    return 0; +  } + +  int i = 999; +  if (a != 2) { +    p = &i; +    goto label1; +  } +  return -1; +}  | 

