[cfe-commits] r81670 - in /cfe/trunk: include/clang/AST/Decl.h lib/AST/Decl.cpp lib/CodeGen/CodeGenModule.cpp lib/Frontend/PCHReaderDecl.cpp lib/Frontend/PCHWriterDecl.cpp lib/Sema/SemaDecl.cpp test/CodeGen/inline.c test/CodeGen/inline2.c

Douglas Gregor dgregor at apple.com
Sun Sep 13 00:46:27 PDT 2009


Author: dgregor
Date: Sun Sep 13 02:46:26 2009
New Revision: 81670

URL: http://llvm.org/viewvc/llvm-project?rev=81670&view=rev
Log:
Rework the way we determine whether an externally visible symbol is
generated for an inline function definition, taking into account C99
and GNU inline/extern inline semantics. This solution is simpler,
cleaner, and fixes PR4536.

Added:
    cfe/trunk/test/CodeGen/inline2.c   (with props)
Modified:
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/CodeGen/CodeGenModule.cpp
    cfe/trunk/lib/Frontend/PCHReaderDecl.cpp
    cfe/trunk/lib/Frontend/PCHWriterDecl.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/test/CodeGen/inline.c

Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=81670&r1=81669&r2=81670&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Sun Sep 13 02:46:26 2009
@@ -793,7 +793,6 @@
   // NOTE: VC++ treats enums as signed, avoid using the StorageClass enum
   unsigned SClass : 2;
   bool IsInline : 1;
-  bool C99InlineDefinition : 1;
   bool IsVirtualAsWritten : 1;
   bool IsPure : 1;
   bool HasInheritedPrototype : 1;
@@ -835,7 +834,7 @@
     : DeclaratorDecl(DK, DC, L, N, T, DInfo),
       DeclContext(DK),
       ParamInfo(0), Body(),
-      SClass(S), IsInline(isInline), C99InlineDefinition(false),
+      SClass(S), IsInline(isInline), 
       IsVirtualAsWritten(false), IsPure(false), HasInheritedPrototype(false),
       HasWrittenPrototype(true), IsDeleted(false), IsTrivial(false),
       IsCopyAssignment(false),
@@ -1019,24 +1018,8 @@
   bool isInline() const { return IsInline; }
   void setInline(bool I) { IsInline = I; }
 
-  /// \brief Whether this function is an "inline definition" as
-  /// defined by C99.
-  bool isC99InlineDefinition() const { return C99InlineDefinition; }
-  void setC99InlineDefinition(bool I) { C99InlineDefinition = I; }
-
-  /// \brief Determines whether this function has a gnu_inline
-  /// attribute that affects its semantics.
-  ///
-  /// The gnu_inline attribute only introduces GNU inline semantics
-  /// when all of the inline declarations of the function are marked
-  /// gnu_inline.
-  bool hasActiveGNUInlineAttribute(ASTContext &Context) const;
-
-  /// \brief Determines whether this function is a GNU "extern
-  /// inline", which is roughly the opposite of a C99 "extern inline"
-  /// function.
-  bool isExternGNUInline(ASTContext &Context) const;
-
+  bool isInlineDefinitionExternallyVisible() const;
+                       
   /// isOverloadedOperator - Whether this function declaration
   /// represents an C++ overloaded operator, e.g., "operator+".
   bool isOverloadedOperator() const {

Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=81670&r1=81669&r2=81670&view=diff

==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Sun Sep 13 02:46:26 2009
@@ -580,25 +580,64 @@
   return NumRequiredArgs;
 }
 
-bool FunctionDecl::hasActiveGNUInlineAttribute(ASTContext &Context) const {
-  if (!isInline() || !hasAttr<GNUInlineAttr>())
-    return false;
-
-  for (redecl_iterator I = redecls_begin(), E = redecls_end(); I != E; ++I)
-    if (I->isInline() && !I->hasAttr<GNUInlineAttr>())
-      return false;
-
-  return true;
-}
-
-bool FunctionDecl::isExternGNUInline(ASTContext &Context) const {
-  if (!hasActiveGNUInlineAttribute(Context))
+/// \brief For an inline function definition in C, determine whether the 
+/// definition will be externally visible.
+///
+/// Inline function definitions are always available for inlining optimizations.
+/// However, depending on the language dialect, declaration specifiers, and
+/// attributes, the definition of an inline function may or may not be
+/// "externally" visible to other translation units in the program.
+///
+/// In C99, inline definitions are not externally visible by default. However,
+/// if even one of the globa-scope declarations is marked "extern inline", the
+/// inline definition becomes externally visible (C99 6.7.4p6).
+///
+/// In GNU89 mode, or if the gnu_inline attribute is attached to the function
+/// definition, we use the GNU semantics for inline, which are nearly the 
+/// opposite of C99 semantics. In particular, "inline" by itself will create 
+/// an externally visible symbol, but "extern inline" will not create an 
+/// externally visible symbol.
+bool FunctionDecl::isInlineDefinitionExternallyVisible() const {
+  assert(isThisDeclarationADefinition() && "Must have the function definition");
+  assert(isInline() && "Function must be inline");
+  
+  if (!getASTContext().getLangOptions().C99 || hasAttr<GNUInlineAttr>()) {
+    // GNU inline semantics. Based on a number of examples, we came up with the
+    // following heuristic: if the "inline" keyword is present on a
+    // declaration of the function but "extern" is not present on that
+    // declaration, then the symbol is externally visible. Otherwise, the GNU
+    // "extern inline" semantics applies and the symbol is not externally
+    // visible.
+    for (redecl_iterator Redecl = redecls_begin(), RedeclEnd = redecls_end();
+         Redecl != RedeclEnd;
+         ++Redecl) {
+      if (Redecl->isInline() && Redecl->getStorageClass() != Extern)
+        return true;
+    }
+    
+    // GNU "extern inline" semantics; no externally visible symbol.
     return false;
-
-  for (redecl_iterator I = redecls_begin(), E = redecls_end(); I != E; ++I)
-    if (I->getStorageClass() == Extern && I->hasAttr<GNUInlineAttr>())
-      return true;
-
+  }
+  
+  // C99 6.7.4p6:
+  //   [...] If all of the file scope declarations for a function in a 
+  //   translation unit include the inline function specifier without extern, 
+  //   then the definition in that translation unit is an inline definition.
+  for (redecl_iterator Redecl = redecls_begin(), RedeclEnd = redecls_end();
+       Redecl != RedeclEnd;
+       ++Redecl) {
+    // Only consider file-scope declarations in this test.
+    if (!Redecl->getLexicalDeclContext()->isTranslationUnit())
+      continue;
+    
+    if (!Redecl->isInline() || Redecl->getStorageClass() == Extern) 
+      return true; // Not an inline definition
+  }
+  
+  // C99 6.7.4p6:
+  //   An inline definition does not provide an external definition for the 
+  //   function, and does not forbid an external definition in another 
+  //   translation unit.
   return false;
 }
 

Modified: cfe/trunk/lib/CodeGen/CodeGenModule.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.cpp?rev=81670&r1=81669&r2=81670&view=diff

==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenModule.cpp (original)
+++ cfe/trunk/lib/CodeGen/CodeGenModule.cpp Sun Sep 13 02:46:26 2009
@@ -269,34 +269,19 @@
   if (!FD->isInline())
     return External;
 
-  // If the inline function explicitly has the GNU inline attribute on it, or if
-  // this is C89 mode, we use to GNU semantics.
-  if (!Features.C99 && !Features.CPlusPlus) {
-    // extern inline in GNU mode is like C99 inline.
-    if (FD->getStorageClass() == FunctionDecl::Extern)
-      return CodeGenModule::GVA_C99Inline;
-    // Normal inline is a strong symbol.
-    return CodeGenModule::GVA_StrongExternal;
-  } else if (FD->hasActiveGNUInlineAttribute(Context)) {
-    // GCC in C99 mode seems to use a different decision-making
-    // process for extern inline, which factors in previous
-    // declarations.
-    if (FD->isExternGNUInline(Context))
-      return CodeGenModule::GVA_C99Inline;
-    // Normal inline is a strong symbol.
-    return External;
-  }
+  if (!Features.CPlusPlus || FD->hasAttr<GNUInlineAttr>()) {
+    // GNU or C99 inline semantics. Determine whether this symbol should be
+    // externally visible.
+    if (FD->isInlineDefinitionExternallyVisible())
+      return External;
 
-  // The definition of inline changes based on the language.  Note that we
-  // have already handled "static inline" above, with the GVA_Internal case.
-  if (Features.CPlusPlus)  // inline and extern inline.
-    return CodeGenModule::GVA_CXXInline;
-
-  assert(Features.C99 && "Must be in C99 mode if not in C89 or C++ mode");
-  if (FD->isC99InlineDefinition())
+    // C99 inline semantics, where the symbol is not externally visible.
     return CodeGenModule::GVA_C99Inline;
+  }
 
-  return CodeGenModule::GVA_StrongExternal;
+  // C++ inline semantics
+  assert(Features.CPlusPlus && "Must be in C++ mode");
+  return CodeGenModule::GVA_CXXInline;
 }
 
 /// SetFunctionDefinitionAttributes - Set attributes for a global.

Modified: cfe/trunk/lib/Frontend/PCHReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/PCHReaderDecl.cpp?rev=81670&r1=81669&r2=81670&view=diff

==============================================================================
--- cfe/trunk/lib/Frontend/PCHReaderDecl.cpp (original)
+++ cfe/trunk/lib/Frontend/PCHReaderDecl.cpp Sun Sep 13 02:46:26 2009
@@ -225,7 +225,6 @@
                    cast_or_null<FunctionDecl>(Reader.GetDecl(Record[Idx++])));
   FD->setStorageClass((FunctionDecl::StorageClass)Record[Idx++]);
   FD->setInline(Record[Idx++]);
-  FD->setC99InlineDefinition(Record[Idx++]);
   FD->setVirtualAsWritten(Record[Idx++]);
   FD->setPure(Record[Idx++]);
   FD->setHasInheritedPrototype(Record[Idx++]);

Modified: cfe/trunk/lib/Frontend/PCHWriterDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/PCHWriterDecl.cpp?rev=81670&r1=81669&r2=81670&view=diff

==============================================================================
--- cfe/trunk/lib/Frontend/PCHWriterDecl.cpp (original)
+++ cfe/trunk/lib/Frontend/PCHWriterDecl.cpp Sun Sep 13 02:46:26 2009
@@ -225,7 +225,6 @@
   Writer.AddDeclRef(D->getPreviousDeclaration(), Record);
   Record.push_back(D->getStorageClass()); // FIXME: stable encoding
   Record.push_back(D->isInline());
-  Record.push_back(D->isC99InlineDefinition());
   Record.push_back(D->isVirtualAsWritten());
   Record.push_back(D->isPure());
   Record.push_back(D->hasInheritedPrototype());

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=81670&r1=81669&r2=81670&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Sun Sep 13 02:46:26 2009
@@ -917,25 +917,10 @@
   MergeAttributes(New, Old, Context);
 
   // Merge the storage class.
-  if (Old->getStorageClass() != FunctionDecl::Extern)
+  if (Old->getStorageClass() != FunctionDecl::Extern &&
+      Old->getStorageClass() != FunctionDecl::None)
     New->setStorageClass(Old->getStorageClass());
 
-  // Merge "inline"
-  if (Old->isInline())
-    New->setInline(true);
-
-  // If this function declaration by itself qualifies as a C99 inline
-  // definition (C99 6.7.4p6), but the previous definition did not,
-  // then the function is not a C99 inline definition.
-  if (New->isC99InlineDefinition() && !Old->isC99InlineDefinition())
-    New->setC99InlineDefinition(false);
-  else if (Old->isC99InlineDefinition() && !New->isC99InlineDefinition()) {
-    // Mark all preceding definitions as not being C99 inline definitions.
-    for (const FunctionDecl *Prev = Old; Prev;
-         Prev = Prev->getPreviousDeclaration())
-      const_cast<FunctionDecl *>(Prev)->setC99InlineDefinition(false);
-  }
-
   // Merge "pure" flag.
   if (Old->isPure())
     New->setPure();
@@ -2859,24 +2844,6 @@
       return NewFD->setInvalidDecl();
   }
 
-  // C99 6.7.4p6:
-  //   [... ] For a function with external linkage, the following
-  //   restrictions apply: [...] If all of the file scope declarations
-  //   for a function in a translation unit include the inline
-  //   function specifier without extern, then the definition in that
-  //   translation unit is an inline definition. An inline definition
-  //   does not provide an external definition for the function, and
-  //   does not forbid an external definition in another translation
-  //   unit.
-  //
-  // Here we determine whether this function, in isolation, would be a
-  // C99 inline definition. MergeCompatibleFunctionDecls looks at
-  // previous declarations.
-  if (NewFD->isInline() && getLangOptions().C99 &&
-      NewFD->getStorageClass() == FunctionDecl::None &&
-      NewFD->getDeclContext()->getLookupContext()->isTranslationUnit())
-    NewFD->setC99InlineDefinition(true);
-
   // Check for a previous declaration of this name.
   if (!PrevDecl && NewFD->isExternC()) {
     // Since we did not find anything by this name and we're declaring

Modified: cfe/trunk/test/CodeGen/inline.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/inline.c?rev=81670&r1=81669&r2=81670&view=diff

==============================================================================
--- cfe/trunk/test/CodeGen/inline.c (original)
+++ cfe/trunk/test/CodeGen/inline.c Sun Sep 13 02:46:26 2009
@@ -1,5 +1,5 @@
-// RUN: echo "C89 tests:" &&
-// RUN: clang %s -emit-llvm -S -o %t -std=c89 &&
+// RUN: echo "GNU89 tests:" &&
+// RUN: clang %s -emit-llvm -S -o %t -std=gnu89 &&
 // RUN: grep "define available_externally i32 @ei()" %t &&
 // RUN: grep "define i32 @foo()" %t &&
 // RUN: grep "define i32 @bar()" %t &&
@@ -24,9 +24,9 @@
 // RUN: grep "define available_externally void @gnu_ei_inline()" %t &&
 // RUN: grep "define i32 @test1" %t &&
 // RUN: grep "define i32 @test2" %t &&
-// RUN: grep "define available_externally void @test3" %t &&
+// RUN: grep "define void @test3" %t &&
 // RUN: grep "define available_externally i32 @test4" %t &&
-// RUN: grep "define i32 @test5" %t &&
+// RUN: grep "define available_externally i32 @test5" %t &&
 
 // RUN: echo "\nC++ tests:" &&
 // RUN: clang %s -emit-llvm -S -o %t -std=c++98 &&
@@ -67,9 +67,7 @@
 
 // PR3989
 extern __inline void test3() __attribute__((gnu_inline));
-__inline void test3() {}
-
-void test_test3() { test3(); }
+__inline void __attribute__((gnu_inline)) test3() {}
 
 extern int test4(void);
 extern __inline __attribute__ ((__gnu_inline__)) int test4(void)
@@ -79,7 +77,7 @@
 
 void test_test4() { test4(); }
 
-extern __inline int test5(void);
+extern __inline int test5(void)  __attribute__ ((__gnu_inline__));
 extern __inline int __attribute__ ((__gnu_inline__)) test5(void)
 {
   return 0;

Added: cfe/trunk/test/CodeGen/inline2.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/inline2.c?rev=81670&view=auto

==============================================================================
--- cfe/trunk/test/CodeGen/inline2.c (added)
+++ cfe/trunk/test/CodeGen/inline2.c Sun Sep 13 02:46:26 2009
@@ -0,0 +1,62 @@
+// RUN: clang-cc -std=gnu89 -triple i386-apple-darwin9 -emit-llvm %s -o - | FileCheck -check-prefix GNU89 %s &&
+// RUN: clang-cc -std=c99 -triple i386-apple-darwin9 -emit-llvm %s -o - | FileCheck -check-prefix C99 %s
+
+// CHECK-GNU89: define i32 @f0()
+// CHECK-C99: define i32 @f0()
+int f0(void);
+int f0(void) { return 0; }
+
+// CHECK-GNU89: define i32 @f1()
+// CHECK-C99: define i32 @f1()
+inline int f1(void);
+int f1(void) { return 0; }
+
+// CHECK-GNU89: define i32 @f2()
+// CHECK-C99: define i32 @f2()
+int f2(void);
+inline int f2(void) { return 0; }
+
+// CHECK-GNU89: define i32 @f3()
+// CHECK-C99: define i32 @f3()
+extern inline int f3(void);
+int f3(void) { return 0; }
+
+// CHECK-GNU89: define i32 @f5()
+// CHECK-C99: define i32 @f5()
+extern inline int f5(void);
+inline int f5(void) { return 0; }
+
+// CHECK-GNU89: define i32 @f6()
+// CHECK-C99: define i32 @f6()
+inline int f6(void);
+extern inline int f6(void) { return 0; }
+
+// CHECK-GNU89: define i32 @f7()
+// CHECK-C99: define i32 @f7()
+extern inline int f7(void);
+extern int f7(void) { return 0; }
+
+// CHECK-GNU89: define i32 @fA()
+inline int fA(void) { return 0; }
+
+// CHECK-GNU89: define available_externally i32 @f4()
+// CHECK-C99: define i32 @f4()
+int f4(void);
+extern inline int f4(void) { return 0; }
+
+// CHECK-GNU89: define available_externally i32 @f8()
+// CHECK-C99: define i32 @f8()
+extern int f8(void);
+extern inline int f8(void) { return 0; }
+
+// CHECK-GNU89: define available_externally i32 @f9()
+// CHECK-C99: define i32 @f9()
+extern inline int f9(void);
+extern inline int f9(void) { return 0; }
+
+// CHECK-C99: define available_externally i32 @fA()
+
+int test_all() { 
+  return f0() + f1() + f2() + f3() + f4() + f5() + f6() + f7() + f8() + f9() 
+    + fA();
+}

Propchange: cfe/trunk/test/CodeGen/inline2.c

------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cfe/trunk/test/CodeGen/inline2.c

------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cfe/trunk/test/CodeGen/inline2.c

------------------------------------------------------------------------------
    svn:mime-type = text/plain





More information about the cfe-commits mailing list