]> granicus.if.org Git - clang/commitdiff
Always_inline codegen rewrite.
authorEvgeniy Stepanov <eugeni.stepanov@gmail.com>
Sat, 12 Sep 2015 01:07:37 +0000 (01:07 +0000)
committerEvgeniy Stepanov <eugeni.stepanov@gmail.com>
Sat, 12 Sep 2015 01:07:37 +0000 (01:07 +0000)
Current implementation may end up emitting an undefined reference for
an "inline __attribute__((always_inline))" function by generating an
"available_externally alwaysinline" IR function for it and then failing to
inline all the calls. This happens when a call to such function is in dead
code. As the inliner is an SCC pass, it does not process dead code.

Libc++ relies on the compiler never emitting such undefined reference.

With this patch, we emit a pair of
1. internal alwaysinline definition (called F.alwaysinline)
2a. A stub F() { musttail call F.alwaysinline }
  -- or, depending on the linkage --
2b. A declaration of F.

The frontend ensures that F.inlinefunction is only used for direct
calls, and the stub is used for everything else (taking the address of
the function, really). Declaration (2b) is emitted in the case when
"inline" is meant for inlining only (like __gnu_inline__ and some
other cases).

This approach, among other nice properties, ensures that alwaysinline
functions are always internal, making it impossible for a direct call
to such function to produce an undefined symbol reference.

This patch is based on ideas by Chandler Carruth and Richard Smith.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@247494 91177308-0d34-0410-b5e6-96231b3b80d8

16 files changed:
lib/CodeGen/CGCXX.cpp
lib/CodeGen/CGClass.cpp
lib/CodeGen/CGOpenMPRuntime.cpp
lib/CodeGen/CodeGenModule.cpp
lib/CodeGen/CodeGenModule.h
lib/CodeGen/ItaniumCXXABI.cpp
test/CodeGen/always-inline.c
test/CodeGen/always_inline-unused.c [new file with mode: 0644]
test/CodeGen/always_inline-wrappers.c [new file with mode: 0644]
test/CodeGen/always_inline.c
test/CodeGen/function-attributes.c
test/CodeGen/pr9614.c
test/CodeGenCXX/alwaysinline.cpp [new file with mode: 0644]
test/Frontend/optimization-remark-line-directive.c
test/Frontend/optimization-remark.c
test/Modules/cxx-irgen.cpp

index ad30364986785fda8302d1c4d641d389a0835be2..07eb91582affd0f68ea62fc3b3314a3bd84ff303 100644 (file)
@@ -109,6 +109,9 @@ bool CodeGenModule::TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) {
       D->getType()->getAs<FunctionType>()->getCallConv())
     return true;
 
+  if (BaseD->hasAttr<AlwaysInlineAttr>())
+    return true;
+
   return TryEmitDefinitionAsAlias(GlobalDecl(D, Dtor_Base),
                                   GlobalDecl(BaseD, Dtor_Base),
                                   false);
@@ -161,14 +164,7 @@ bool CodeGenModule::TryEmitDefinitionAsAlias(GlobalDecl AliasDecl,
 
   // Instead of creating as alias to a linkonce_odr, replace all of the uses
   // of the aliasee.
-  if (llvm::GlobalValue::isDiscardableIfUnused(Linkage) &&
-     (TargetLinkage != llvm::GlobalValue::AvailableExternallyLinkage ||
-      !TargetDecl.getDecl()->hasAttr<AlwaysInlineAttr>())) {
-    // FIXME: An extern template instantiation will create functions with
-    // linkage "AvailableExternally". In libc++, some classes also define
-    // members with attribute "AlwaysInline" and expect no reference to
-    // be generated. It is desirable to reenable this optimisation after
-    // corresponding LLVM changes.
+  if (llvm::GlobalValue::isDiscardableIfUnused(Linkage)) {
     Replacements[MangledName] = Aliasee;
     return false;
   }
index b11814f21e17fa5fc513a62c360c483804bb0ea4..b0916ed3dcbbe3aa563203d016c5ddcd550f0749 100644 (file)
@@ -1557,7 +1557,7 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
     // -fapple-kext must inline any call to this dtor into
     // the caller's body.
     if (getLangOpts().AppleKext)
-      CurFn->addFnAttr(llvm::Attribute::AlwaysInline);
+      CGM.AddAlwaysInlineFunction(CurFn);
 
     break;
   }
index a9e7a9c55528cb22f0fe7410d1ed332d41139d75..f85f66bd1349b63dc1c4c706aa0c9e3d30ad7366 100644 (file)
@@ -2114,7 +2114,7 @@ emitTaskPrivateMappingFunction(CodeGenModule &CGM, SourceLocation Loc,
       ".omp_task_privates_map.", &CGM.getModule());
   CGM.SetLLVMFunctionAttributes(/*D=*/nullptr, TaskPrivatesMapFnInfo,
                                 TaskPrivatesMap);
-  TaskPrivatesMap->addFnAttr(llvm::Attribute::AlwaysInline);
+  CGM.AddAlwaysInlineFunction(TaskPrivatesMap);
   CodeGenFunction CGF(CGM);
   CGF.disableDebugInfo();
   CGF.StartFunction(GlobalDecl(), C.VoidTy, TaskPrivatesMap,
index bbc01cba63371a9064216f5e0cf0dfcaca18c39e..4fe25ff3f3173853bb0626f44a57f056bb8c60aa 100644 (file)
@@ -448,6 +448,109 @@ void CodeGenModule::Release() {
   EmitVersionIdentMetadata();
 
   EmitTargetMetadata();
+
+  RewriteAlwaysInlineFunctions();
+}
+
+void CodeGenModule::AddAlwaysInlineFunction(llvm::Function *Fn) {
+  AlwaysInlineFunctions.push_back(Fn);
+}
+
+/// Find all uses of GV that are not direct calls or invokes.
+static void FindNonDirectCallUses(llvm::GlobalValue *GV,
+                                  llvm::SmallVectorImpl<llvm::Use *> *Uses) {
+  llvm::GlobalValue::use_iterator UI = GV->use_begin(), E = GV->use_end();
+  for (; UI != E;) {
+    llvm::Use &U = *UI;
+    ++UI;
+
+    llvm::CallSite CS(U.getUser());
+    bool isDirectCall = (CS.isCall() || CS.isInvoke()) && CS.isCallee(&U);
+    if (!isDirectCall)
+      Uses->push_back(&U);
+  }
+}
+
+/// Replace a list of uses.
+static void ReplaceUsesWith(const llvm::SmallVectorImpl<llvm::Use *> &Uses,
+                            llvm::GlobalValue *V,
+                            llvm::GlobalValue *Replacement) {
+  for (llvm::Use *U : Uses) {
+    auto *C = dyn_cast<llvm::Constant>(U->getUser());
+    if (C && !isa<llvm::GlobalValue>(C))
+      C->handleOperandChange(V, Replacement, U);
+    else
+      U->set(Replacement);
+  }
+}
+
+void CodeGenModule::RewriteAlwaysInlineFunction(llvm::Function *Fn) {
+  std::string Name = Fn->getName();
+  std::string InlineName = Name + ".alwaysinline";
+  Fn->setName(InlineName);
+
+  llvm::SmallVector<llvm::Use *, 8> NonDirectCallUses;
+  Fn->removeDeadConstantUsers();
+  FindNonDirectCallUses(Fn, &NonDirectCallUses);
+  // Do not create the wrapper if there are no non-direct call uses, and we are
+  // not required to emit an external definition.
+  if (NonDirectCallUses.empty() && Fn->isDiscardableIfUnused())
+    return;
+
+  llvm::FunctionType *FT = Fn->getFunctionType();
+  llvm::LLVMContext &Ctx = getModule().getContext();
+  llvm::Function *StubFn =
+      llvm::Function::Create(FT, Fn->getLinkage(), Name, &getModule());
+  assert(StubFn->getName() == Name && "name was uniqued!");
+
+  // Insert the stub immediately after the original function. Helps with the
+  // fragile tests, among other things.
+  StubFn->removeFromParent();
+  TheModule.getFunctionList().insertAfter(Fn, StubFn);
+
+  StubFn->copyAttributesFrom(Fn);
+  StubFn->setPersonalityFn(nullptr);
+
+  // AvailableExternally functions are replaced with a declaration.
+  // Everyone else gets a wrapper that musttail-calls the original function.
+  if (Fn->hasAvailableExternallyLinkage()) {
+    StubFn->setLinkage(llvm::GlobalValue::ExternalLinkage);
+  } else {
+    llvm::BasicBlock *BB = llvm::BasicBlock::Create(Ctx, "entry", StubFn);
+    std::vector<llvm::Value *> Args;
+    for (llvm::Function::arg_iterator ai = StubFn->arg_begin();
+         ai != StubFn->arg_end(); ++ai)
+      Args.push_back(&*ai);
+    llvm::CallInst *CI = llvm::CallInst::Create(Fn, Args, "", BB);
+    CI->setCallingConv(Fn->getCallingConv());
+    CI->setTailCallKind(llvm::CallInst::TCK_MustTail);
+    CI->setAttributes(Fn->getAttributes());
+    if (FT->getReturnType()->isVoidTy())
+      llvm::ReturnInst::Create(Ctx, BB);
+    else
+      llvm::ReturnInst::Create(Ctx, CI, BB);
+  }
+
+  if (Fn->hasComdat())
+    StubFn->setComdat(Fn->getComdat());
+
+  ReplaceUsesWith(NonDirectCallUses, Fn, StubFn);
+
+  // Replace all metadata uses with the stub. This is primarily to reattach
+  // DISubprogram metadata to the stub, because that's what will be emitted in
+  // the object file.
+  if (Fn->isUsedByMetadata())
+    llvm::ValueAsMetadata::handleRAUW(Fn, StubFn);
+}
+
+void CodeGenModule::RewriteAlwaysInlineFunctions() {
+  for (llvm::Function *Fn : AlwaysInlineFunctions) {
+    RewriteAlwaysInlineFunction(Fn);
+    Fn->setLinkage(llvm::GlobalValue::InternalLinkage);
+    Fn->addFnAttr(llvm::Attribute::AlwaysInline);
+    Fn->setDLLStorageClass(llvm::GlobalVariable::DefaultStorageClass);
+    Fn->setVisibility(llvm::GlobalValue::DefaultVisibility);
+  }
 }
 
 void CodeGenModule::UpdateCompletedType(const TagDecl *TD) {
@@ -772,7 +875,7 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
              !F->getAttributes().hasAttribute(llvm::AttributeSet::FunctionIndex,
                                               llvm::Attribute::NoInline)) {
     // (noinline wins over always_inline, and we can't specify both in IR)
-    B.addAttribute(llvm::Attribute::AlwaysInline);
+    AddAlwaysInlineFunction(F);
   }
 
   if (D->hasAttr<ColdAttr>()) {
index 754e3d1d29f3e8cbfa91877846b0ba2ecae37768..e579f8bd5ecde98f72abf3805e6204fd10ee403b 100644 (file)
@@ -489,6 +489,8 @@ private:
   /// MDNodes.
   llvm::DenseMap<QualType, llvm::Metadata *> MetadataIdMap;
 
+  llvm::SmallVector<llvm::Function*, 8> AlwaysInlineFunctions;
+
 public:
   CodeGenModule(ASTContext &C, const HeaderSearchOptions &headersearchopts,
                 const PreprocessorOptions &ppopts,
@@ -1131,6 +1133,8 @@ public:
   /// \breif Get the declaration of std::terminate for the platform.
   llvm::Constant *getTerminateFn();
 
+  void AddAlwaysInlineFunction(llvm::Function *Fn);
+
 private:
   llvm::Constant *
   GetOrCreateLLVMFunction(StringRef MangledName, llvm::Type *Ty, GlobalDecl D,
@@ -1226,6 +1230,12 @@ private:
   /// Emits target specific Metadata for global declarations.
   void EmitTargetMetadata();
 
+  /// Replaces alwaysinline functions with a pair of internal xxx.inlinefunction
+  /// for direct calls, and a stub for indirect calls, and rewrites all uses of
+  /// those.
+  void RewriteAlwaysInlineFunctions();
+  void RewriteAlwaysInlineFunction(llvm::Function *Fn);
+
   /// Emit the llvm.gcov metadata used to tell LLVM where to emit the .gcno and
   /// .gcda files in a way that persists in .bc files.
   void EmitCoverageFile();
index 0c6a6d751c7b6eec3f9cf616efc2574e99920cc9..fa714d7ddc77eeda393b4c954769f4640f1200fb 100644 (file)
@@ -3311,6 +3311,9 @@ static StructorCodegen getCodegenToUse(CodeGenModule &CGM,
   if (MD->getParent()->getNumVBases())
     return StructorCodegen::Emit;
 
+  if (MD->hasAttr<AlwaysInlineAttr>())
+    return StructorCodegen::Emit;
+
   GlobalDecl AliasDecl;
   if (const auto *DD = dyn_cast<CXXDestructorDecl>(MD)) {
     AliasDecl = GlobalDecl(DD, Dtor_Complete);
index c9fd1ae2d80d83698a2a6b712082738d6ba85885..ed8ecc7bfa75c948ab777197ff08428351bddb23 100644 (file)
@@ -1,7 +1,9 @@
 // RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s
 // RUN: %clang_cc1 -fno-inline -emit-llvm %s -o - | FileCheck %s
 
+// CHECK-LABEL: define void @i_want_bar()
 // CHECK-NOT: foo
+// CHECK: ret void
 
 void bar() {
 }
diff --git a/test/CodeGen/always_inline-unused.c b/test/CodeGen/always_inline-unused.c
new file mode 100644 (file)
index 0000000..eb733f7
--- /dev/null
@@ -0,0 +1,31 @@
+// Test alwaysinline definitions w/o any non-direct-call uses.
+// None of the declarations are emitted. Stub are only emitted when the original
+// function can not be discarded.
+
+// RUN: %clang_cc1 -disable-llvm-optzns -emit-llvm %s -o - | FileCheck %s
+
+void __attribute__((__always_inline__)) f1() {}
+inline void __attribute__((__always_inline__)) f2() {}
+static inline void __attribute__((__always_inline__)) f3() {}
+inline void __attribute__((gnu_inline, __always_inline__)) f4() {}
+static inline void __attribute__((gnu_inline, __always_inline__)) f5() {}
+inline void __attribute__((visibility("hidden"), __always_inline__)) f6() {}
+inline void __attribute__((visibility("hidden"), gnu_inline, __always_inline__)) f7() {}
+
+void g() {
+  f1();
+  f2();
+  f3();
+  f4();
+  f5();
+  f6();
+  f7();
+}
+
+// CHECK: define void @f1()
+// CHECK-NOT: void @f2()
+// CHECK-NOT: void @f3()
+// CHECK: define void @f4()
+// CHECK-NOT: void @f5()
+// CHECK-NOT: void @f6()
+// CHECK: define hidden void @f7()
diff --git a/test/CodeGen/always_inline-wrappers.c b/test/CodeGen/always_inline-wrappers.c
new file mode 100644 (file)
index 0000000..ba0b148
--- /dev/null
@@ -0,0 +1,108 @@
+// Test different kinds of alwaysinline definitions.
+
+// RUN: %clang_cc1 -disable-llvm-optzns -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-INLINE
+// RUN: %clang_cc1 -disable-llvm-optzns -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-USE
+// RUN: %clang_cc1 -disable-llvm-optzns -fno-inline -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -disable-llvm-optzns -fno-inline -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-USE
+
+void __attribute__((__always_inline__)) f1() {}
+inline void __attribute__((__always_inline__)) f2() {}
+static inline void __attribute__((__always_inline__)) f3() {}
+inline void __attribute__((gnu_inline, __always_inline__)) f4() {}
+static inline void __attribute__((gnu_inline, __always_inline__)) f5() {}
+inline void __attribute__((visibility("hidden"), __always_inline__)) f6() {}
+inline void __attribute__((visibility("hidden"), gnu_inline, __always_inline__)) f7() {}
+
+void g() {
+  f1();
+  f2();
+  f3();
+  f4();
+  f5();
+  f6();
+  f7();
+}
+
+void (*p)(void);
+void h() {
+  p = f1;
+  p = f2;
+  p = f3;
+  p = f4;
+  p = f5;
+  p = f6;
+  p = f7;
+}
+
+void (*const cp1)(void) = f1;
+void (*p1)(void) = f1;
+void (*p2)(int) = (void (*)(int))f1;
+
+void __attribute__((__always_inline__)) f8(void(*f)(void)) {}
+
+void call() {
+  f8(f1);
+}
+
+// CHECK-DAG: define internal void @f1.alwaysinline() #[[AI:[0-9]+]]
+// CHECK-DAG: define internal void @f2.alwaysinline() #[[AI_IH:[0-9]+]]
+// CHECK-DAG: define internal void @f3.alwaysinline() #[[AI_IH]]
+// CHECK-DAG: define internal void @f4.alwaysinline() #[[AI_IH]]
+// CHECK-DAG: define internal void @f5.alwaysinline() #[[AI_IH]]
+// CHECK-DAG: define internal void @f6.alwaysinline() #[[AI_IH]]
+// CHECK-DAG: define internal void @f7.alwaysinline() #[[AI_IH]]
+
+
+// CHECK-DAG: define void @f1() #[[NOAI:[01-9]+]]
+// CHECK-DAG: musttail call void @f1.alwaysinline()
+
+// CHECK-DAG: declare void @f2() #[[NOAI:[01-9]+]]
+
+// CHECK-DAG: define internal void @f3() #[[NOAI:[01-9]+]]
+// CHECK-DAG: musttail call void @f3.alwaysinline()
+
+// CHECK-DAG: define void @f4() #[[NOAI:[01-9]+]]
+// CHECK-DAG: musttail call void @f4.alwaysinline()
+
+// CHECK-DAG: define internal void @f5() #[[NOAI:[01-9]+]]
+// CHECK-DAG: musttail call void @f5.alwaysinline()
+
+// CHECK-DAG: declare hidden void @f6() #[[NOAI:[01-9]+]]
+
+// CHECK-DAG: define hidden void @f7() #[[NOAI:[01-9]+]]
+// CHECK-DAG: musttail call void @f7.alwaysinline()
+
+
+// CHECK-DAG: @cp1 = constant void ()* @f1, align
+// CHECK-DAG: @p1 = global void ()* @f1, align
+// CHECK-DAG: @p2 = global void (i32)* bitcast (void ()* @f1 to void (i32)*), align
+
+// CHECK: attributes #[[AI]] = {{.*alwaysinline.*}}
+// CHECK-INLINE: attributes #[[AI_IH]] = {{.*alwaysinline.*inlinehint.*}}
+// CHECK-NOT: attributes #[[NOAI]] = {{.*alwaysinline.*}}
+
+// CHECK-USE-LABEL: define void @g()
+// CHECK-USE-NOT:      ret void
+// CHECK-USE:          call void @f1.alwaysinline()
+// CHECK-USE-NEXT:     call void @f2.alwaysinline()
+// CHECK-USE-NEXT:     call void @f3.alwaysinline()
+// CHECK-USE-NEXT:     call void @f4.alwaysinline()
+// CHECK-USE-NEXT:     call void @f5.alwaysinline()
+// CHECK-USE-NEXT:     call void @f6.alwaysinline()
+// CHECK-USE-NEXT:     call void @f7.alwaysinline()
+// CHECK-USE-NEXT:     ret void
+
+// CHECK-USE-LABEL: define void @h()
+// CHECK-USE-NOT:      ret void
+// CHECK-USE:          store void ()* @f1,
+// CHECK-USE-NEXT:     store void ()* @f2,
+// CHECK-USE-NEXT:     store void ()* @f3,
+// CHECK-USE-NEXT:     store void ()* @f4,
+// CHECK-USE-NEXT:     store void ()* @f5,
+// CHECK-USE-NEXT:     store void ()* @f6,
+// CHECK-USE-NEXT:     store void ()* @f7,
+// CHECK-USE-NEXT:     ret void
+
+// CHECK-USE-LABEL:  define void @call()
+// CHECK-USE:           call void @f8.alwaysinline(void ()* @f1)
+// CHECK-USE:           ret void
index c91fd43f2761cd49adc9dd0e7df63d18f7ea53a1..00b4234dc71c98d69f0a629e4f51c6b264482cab 100644 (file)
@@ -1,8 +1,5 @@
-// RUN: %clang -emit-llvm -S -o %t %s
-// RUN: not grep '@f0' %t
-// RUN: not grep 'call ' %t
-// RUN: %clang -mllvm -disable-llvm-optzns -emit-llvm -S -o %t %s
-// RUN: grep '@f0' %t | count 2
+// RUN: %clang -target x86_64-pc-linux-gnu -emit-llvm -S -o - %s | FileCheck %s
+// RUN: %clang -target x86_64-pc-linux-gnu -mllvm -disable-llvm-optzns -emit-llvm -S -o - %s | FileCheck %s --check-prefix=CHECK-NO-OPTZNS
 
 //static int f0() { 
 static int __attribute__((always_inline)) f0() { 
@@ -18,3 +15,14 @@ inline int f2() __attribute__((always_inline));
 int f2() { return 7; }
 int f3(void) { return f2(); }
 
+// CHECK-LABEL: define i32 @f1()
+// CHECK: ret i32 1
+// CHECK-LABEL: define i32 @f2()
+// CHECK: ret i32 7
+// CHECK-LABEL: define i32 @f3()
+// CHECK: ret i32 7
+
+// CHECK-NO-OPTZNS: define i32 @f3()
+// CHECK-NO-OPTZNS-NOT: ret i32
+// CHECK-NO-OPTZNS:   call i32 @f2.alwaysinline()
+// CHECK-NO-OPTZNS-NEXT:   ret i32
index 177ad848f74389a95f40d36da6ea02c5969bc401..4a7d4fc0edb2101aa2ab7fea3f83044af347a623 100644 (file)
@@ -25,8 +25,8 @@ void f6(signed short x) { }
 
 void f7(unsigned short x) { }
 
-// CHECK-LABEL: define void @f8()
-// CHECK: [[AI:#[0-9]+]]
+// CHECK: define void @f8()
+// CHECK: [[NUW:#[0-9]+]]
 // CHECK: {
 void __attribute__((always_inline)) f8(void) { }
 
@@ -129,7 +129,6 @@ void f20(void) {
 }
 
 // CHECK: attributes [[NUW]] = { nounwind optsize readnone{{.*}} }
-// CHECK: attributes [[AI]] = { alwaysinline nounwind optsize readnone{{.*}} }
 // CHECK: attributes [[ALIGN]] = { nounwind optsize readnone alignstack=16{{.*}} }
 // CHECK: attributes [[RT]] = { nounwind optsize returns_twice{{.*}} }
 // CHECK: attributes [[NR]] = { noreturn nounwind optsize }
index 63cb5af1868c1bb5da151ead94ef658e61089dd1..d264ef3a9ee0b0f48cb54260eab5c0614973acff 100644 (file)
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-pc-linux -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-pc-linux -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK0
+// RUN: %clang_cc1 -triple x86_64-pc-linux -O1 -disable-llvm-optzns -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK1
 
 extern void foo_alias (void) __asm ("foo");
 inline void foo (void) {
@@ -24,7 +25,7 @@ extern inline __attribute__((__always_inline__, __gnu_inline__)) void *memchr(vo
 
 void f(void) {
   foo();
-  abs(0);
+  int x = abs(0);
   strrchr_foo("", '.');
   prefetch();
   memchr("", '.', 0);
@@ -32,9 +33,10 @@ void f(void) {
 
 // CHECK-LABEL: define void @f()
 // CHECK: call void @foo()
-// CHECK: call i32 @abs(i32 0)
+// CHECK: call i32 @abs(
 // CHECK: call i8* @strrchr(
-// CHECK: call void @llvm.prefetch(
+// CHECK0: call void @llvm.prefetch(
+// CHECK1: call void @prefetch.alwaysinline(
 // CHECK: call i8* @memchr(
 // CHECK: ret void
 
diff --git a/test/CodeGenCXX/alwaysinline.cpp b/test/CodeGenCXX/alwaysinline.cpp
new file mode 100644 (file)
index 0000000..c694ae4
--- /dev/null
@@ -0,0 +1,68 @@
+// Test different kinds of alwaysinline *structor definitions.
+
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -disable-llvm-optzns -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -disable-llvm-optzns -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-CALL
+
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -mconstructor-aliases -disable-llvm-optzns -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -mconstructor-aliases -disable-llvm-optzns -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-CALL
+
+struct A1 {
+  __attribute__((__always_inline__)) A1() {}
+  __attribute__((__always_inline__)) ~A1() {}
+};
+
+void g1() {
+  A1 a1;
+}
+
+struct A2 {
+  inline __attribute__((__always_inline__)) A2() {}
+  inline __attribute__((__always_inline__)) ~A2() {}
+};
+
+void g2() {
+  A2 a2;
+}
+
+struct A3 {
+  inline __attribute__((gnu_inline, __always_inline__)) A3() {}
+  inline __attribute__((gnu_inline, __always_inline__)) ~A3() {}
+};
+
+void g3() {
+  A3 a3;
+}
+
+// CHECK-DAG: define internal void @_ZN2A1C1Ev.alwaysinline(%struct.A1* %this) unnamed_addr #[[AI:[01-9]+]]
+// CHECK-DAG: define internal void @_ZN2A1C2Ev.alwaysinline(%struct.A1* %this) unnamed_addr #[[AI]]
+// CHECK-DAG: define internal void @_ZN2A1D1Ev.alwaysinline(%struct.A1* %this) unnamed_addr #[[AI]]
+// CHECK-DAG: define internal void @_ZN2A1D2Ev.alwaysinline(%struct.A1* %this) unnamed_addr #[[AI]]
+
+// CHECK-DAG: define internal void @_ZN2A2C1Ev.alwaysinline(%struct.A2* %this) unnamed_addr #[[AIIH:[01-9]+]]
+// CHECK-DAG: define internal void @_ZN2A2C2Ev.alwaysinline(%struct.A2* %this) unnamed_addr #[[AIIH]]
+// CHECK-DAG: define internal void @_ZN2A2D1Ev.alwaysinline(%struct.A2* %this) unnamed_addr #[[AIIH]]
+// CHECK-DAG: define internal void @_ZN2A2D2Ev.alwaysinline(%struct.A2* %this) unnamed_addr #[[AIIH]]
+
+// CHECK-DAG: define internal void @_ZN2A3C1Ev.alwaysinline(%struct.A3* %this) unnamed_addr #[[AIIH]]
+// CHECK-DAG: define internal void @_ZN2A3C2Ev.alwaysinline(%struct.A3* %this) unnamed_addr #[[AIIH]]
+// CHECK-DAG: define internal void @_ZN2A3D1Ev.alwaysinline(%struct.A3* %this) unnamed_addr #[[AIIH]]
+// CHECK-DAG: define internal void @_ZN2A3D2Ev.alwaysinline(%struct.A3* %this) unnamed_addr #[[AIIH]]
+
+// CHECK-DAG: attributes #[[AI]] = {{.*alwaysinline.*}}
+// CHECK-DAG: attributes #[[AIIH]] = {{.*alwaysinline.*inlinehint.*}}
+// CHECK-NOT: attributes #[[NOAI]] = {{.*alwaysinline.*}}
+
+// CHECK-CALL-LABEL: define void @_Z2g1v()
+// CHECK-CALL:       call void @_ZN2A1C1Ev.alwaysinline
+// CHECK-CALL:       call void @_ZN2A1D1Ev.alwaysinline
+// CHECK-CALL:       ret void
+
+// CHECK-CALL-LABEL: define void @_Z2g2v()
+// CHECK-CALL:       call void @_ZN2A2C1Ev.alwaysinline
+// CHECK-CALL:       call void @_ZN2A2D1Ev.alwaysinline
+// CHECK-CALL:       ret void
+
+// CHECK-CALL-LABEL: define void @_Z2g3v()
+// CHECK-CALL:       call void @_ZN2A3C1Ev.alwaysinline
+// CHECK-CALL:       call void @_ZN2A3D1Ev.alwaysinline
+// CHECK-CALL:       ret void
index f4c0011fb4f8d7c11fda7009a5e56d760ca185ee..23d63c9aa1e56953c4e53c7c741a346ed004a0c0 100644 (file)
@@ -5,8 +5,9 @@
 // RUN: %clang_cc1 %s -Rpass=inline -gline-tables-only -dwarf-column-info -emit-llvm-only -verify
 
 int foo(int x, int y) __attribute__((always_inline));
+// expected-remark@+1 {{foo.alwaysinline inlined into foo}}
 int foo(int x, int y) { return x + y; }
 
-// expected-remark@+2 {{foo inlined into bar}} expected-note@+2 {{could not determine the original source location for /bad/path/to/original.c:1230:25}}
+// expected-remark@+2 {{foo.alwaysinline inlined into bar}} expected-note@+2 {{could not determine the original source location for /bad/path/to/original.c:1230:25}}
 #line 1230 "/bad/path/to/original.c"
 int bar(int j) { return foo(j, j - 2); }
index 6ada0030a700d90cf1f52d426b43d5fe4d5a1d2a..604fec00204db6bdd67c5b2ffd2c37aecab154b7 100644 (file)
@@ -32,6 +32,8 @@
 // CHECK-NOT: !llvm.dbg.cu = !{
 
 int foo(int x, int y) __attribute__((always_inline));
+// expected-remark@+2 {{foo.alwaysinline should always be inlined}}
+// expected-remark@+1 {{foo.alwaysinline inlined into foo}}
 int foo(int x, int y) { return x + y; }
 
 float foz(int x, int y) __attribute__((noinline));
@@ -45,7 +47,7 @@ int bar(int j) {
 // expected-remark@+5 {{foz will not be inlined into bar}}
 // expected-remark@+4 {{foz should never be inlined}}
 // expected-remark@+3 {{foz will not be inlined into bar}}
-// expected-remark@+2 {{foo should always be inlined}}
-// expected-remark@+1 {{foo inlined into bar}}
+// expected-remark@+2 {{foo.alwaysinline should always be inlined}}
+// expected-remark@+1 {{foo.alwaysinline inlined into bar}}
   return foo(j, j - 2) * foz(j - 2, j);
 }
index 7cdb0d6eb5950e4b7815d52a8aec1aef84bfa5ea..73d1746789291760cd88574af1db1d3b3c48cc54 100644 (file)
@@ -26,14 +26,14 @@ namespace EmitInlineMethods {
   };
 }
 
-// CHECK-DAG: define available_externally hidden {{.*}}{{signext i32|i32}} @_ZN1SIiE1gEv({{.*}} #[[ALWAYS_INLINE:.*]] align
+// CHECK-DAG: define internal i32 @_ZN1SIiE1gEv.alwaysinline() #[[ALWAYS_INLINE:.*]] align
 int a = S<int>::g();
 
 int b = h();
 
 // CHECK-DAG: define linkonce_odr {{.*}}{{signext i32|i32}} @_Z3minIiET_S0_S0_(i32
 int c = min(1, 2);
-// CHECK: define available_externally {{.*}}{{signext i32|i32}} @_ZN1SIiE1fEv({{.*}} #[[ALWAYS_INLINE]] align
+// CHECK-DAG: define internal {{.*}}{{signext i32|i32}} @_ZN1SIiE1fEv.alwaysinline() #[[ALWAYS_INLINE]] align
 
 namespace ImplicitSpecialMembers {
   // CHECK-LABEL: define {{.*}} @_ZN22ImplicitSpecialMembers1BC2ERKS0_(