summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/lib/CodeGen/CGVTables.cpp52
-rw-r--r--clang/test/CodeGenCXX/linetable-virtual-variadic.cpp5
-rw-r--r--clang/test/CodeGenCXX/ms-thunks-variadic-return.cpp13
-rw-r--r--clang/test/CodeGenCXX/thunks.cpp133
4 files changed, 184 insertions, 19 deletions
diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp
index 3cb3d354483..3055ea3bfd7 100644
--- a/clang/lib/CodeGen/CGVTables.cpp
+++ b/clang/lib/CodeGen/CGVTables.cpp
@@ -166,6 +166,15 @@ CodeGenFunction::GenerateVarArgsThunk(llvm::Function *Fn,
llvm::Value *Callee = CGM.GetAddrOfFunction(GD, Ty, /*ForVTable=*/true);
llvm::Function *BaseFn = cast<llvm::Function>(Callee);
+ // Cloning can't work if we don't have a definition. The Microsoft ABI may
+ // require thunks when a definition is not available. Emit an error in these
+ // cases.
+ if (!MD->isDefined()) {
+ CGM.ErrorUnsupported(MD, "return-adjusting thunk with variadic arguments");
+ return Fn;
+ }
+ assert(!BaseFn->isDeclaration() && "cannot clone undefined variadic method");
+
// Clone to thunk.
llvm::ValueToValueMapTy VMap;
@@ -201,6 +210,8 @@ CodeGenFunction::GenerateVarArgsThunk(llvm::Function *Fn,
Builder.SetInsertPoint(&*ThisStore);
llvm::Value *AdjustedThisPtr =
CGM.getCXXABI().performThisAdjustment(*this, ThisPtr, Thunk.This);
+ AdjustedThisPtr = Builder.CreateBitCast(AdjustedThisPtr,
+ ThisStore->getOperand(0)->getType());
ThisStore->setOperand(0, AdjustedThisPtr);
if (!Thunk.Return.isEmpty()) {
@@ -291,14 +302,17 @@ void CodeGenFunction::EmitCallAndReturnForThunk(llvm::FunctionCallee Callee,
*this, LoadCXXThisAddress(), Thunk->This)
: LoadCXXThis();
- if (CurFnInfo->usesInAlloca() || IsUnprototyped) {
- // We don't handle return adjusting thunks, because they require us to call
- // the copy constructor. For now, fall through and pretend the return
- // adjustment was empty so we don't crash.
+ // If perfect forwarding is required a variadic method, a method using
+ // inalloca, or an unprototyped thunk, use musttail. Emit an error if this
+ // thunk requires a return adjustment, since that is impossible with musttail.
+ if (CurFnInfo->usesInAlloca() || CurFnInfo->isVariadic() || IsUnprototyped) {
if (Thunk && !Thunk->Return.isEmpty()) {
if (IsUnprototyped)
CGM.ErrorUnsupported(
MD, "return-adjusting thunk with incomplete parameter type");
+ else if (CurFnInfo->isVariadic())
+ llvm_unreachable("shouldn't try to emit musttail return-adjusting "
+ "thunks for variadic functions");
else
CGM.ErrorUnsupported(
MD, "non-trivial argument copy for return-adjusting thunk");
@@ -549,16 +563,32 @@ llvm::Constant *CodeGenVTables::maybeEmitThunk(GlobalDecl GD,
CGM.SetLLVMFunctionAttributesForDefinition(GD.getDecl(), ThunkFn);
+ // Thunks for variadic methods are special because in general variadic
+ // arguments cannot be perferctly forwarded. In the general case, clang
+ // implements such thunks by cloning the original function body. However, for
+ // thunks with no return adjustment on targets that support musttail, we can
+ // use musttail to perfectly forward the variadic arguments.
+ bool ShouldCloneVarArgs = false;
if (!IsUnprototyped && ThunkFn->isVarArg()) {
- // Varargs thunks are special; we can't just generate a call because
- // we can't copy the varargs. Our implementation is rather
- // expensive/sucky at the moment, so don't generate the thunk unless
- // we have to.
- // FIXME: Do something better here; GenerateVarArgsThunk is extremely ugly.
+ ShouldCloneVarArgs = true;
+ if (TI.Return.isEmpty()) {
+ switch (CGM.getTriple().getArch()) {
+ case llvm::Triple::x86_64:
+ case llvm::Triple::x86:
+ case llvm::Triple::aarch64:
+ ShouldCloneVarArgs = false;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (ShouldCloneVarArgs) {
if (UseAvailableExternallyLinkage)
return ThunkFn;
- ThunkFn = CodeGenFunction(CGM).GenerateVarArgsThunk(ThunkFn, FnInfo, GD,
- TI);
+ ThunkFn =
+ CodeGenFunction(CGM).GenerateVarArgsThunk(ThunkFn, FnInfo, GD, TI);
} else {
// Normal thunk body generation.
CodeGenFunction(CGM).generateThunk(ThunkFn, FnInfo, GD, TI, IsUnprototyped);
diff --git a/clang/test/CodeGenCXX/linetable-virtual-variadic.cpp b/clang/test/CodeGenCXX/linetable-virtual-variadic.cpp
index 60dee5fe61b..fd65e981f5f 100644
--- a/clang/test/CodeGenCXX/linetable-virtual-variadic.cpp
+++ b/clang/test/CodeGenCXX/linetable-virtual-variadic.cpp
@@ -1,5 +1,6 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -debug-info-kind=line-tables-only %s -o - | FileCheck %s
-// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -debug-info-kind=line-directives-only %s -o - | FileCheck %s
+// Sparc64 is used because AArch64 and X86_64 would both use musttail.
+// RUN: %clang_cc1 -triple sparc64-linux-gnu -emit-llvm -debug-info-kind=line-tables-only %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple sparc64-linux-gnu -emit-llvm -debug-info-kind=line-directives-only %s -o - | FileCheck %s
// Crasher for PR22929.
class Base {
virtual void VariadicFunction(...);
diff --git a/clang/test/CodeGenCXX/ms-thunks-variadic-return.cpp b/clang/test/CodeGenCXX/ms-thunks-variadic-return.cpp
new file mode 100644
index 00000000000..ae306ab4e0b
--- /dev/null
+++ b/clang/test/CodeGenCXX/ms-thunks-variadic-return.cpp
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -fno-rtti-data -triple x86_64-windows-msvc -emit-llvm-only %s -verify
+
+// Verify that we error out on this return adjusting thunk that we can't emit.
+
+struct A {
+ virtual A *clone(const char *f, ...) = 0;
+};
+struct B : virtual A {
+ // expected-error@+1 2 {{cannot compile this return-adjusting thunk with variadic arguments yet}}
+ B *clone(const char *f, ...) override;
+};
+struct C : B { int c; };
+C c;
diff --git a/clang/test/CodeGenCXX/thunks.cpp b/clang/test/CodeGenCXX/thunks.cpp
index cc4b5e8d2f1..fa3e6161e3e 100644
--- a/clang/test/CodeGenCXX/thunks.cpp
+++ b/clang/test/CodeGenCXX/thunks.cpp
@@ -1,6 +1,20 @@
-// RUN: %clang_cc1 %s -triple=x86_64-pc-linux-gnu -munwind-tables -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-NONOPT %s
-// RUN: %clang_cc1 %s -triple=x86_64-pc-linux-gnu -debug-info-kind=standalone -dwarf-version=5 -munwind-tables -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-NONOPT --check-prefix=CHECK-DBG %s
-// RUN: %clang_cc1 %s -triple=x86_64-pc-linux-gnu -munwind-tables -emit-llvm -o - -O1 -disable-llvm-passes | FileCheck --check-prefix=CHECK --check-prefix=CHECK-OPT %s
+// Sparc64 doesn't support musttail (yet), so it uses method cloning for
+// variadic thunks. Use it for testing.
+// RUN: %clang_cc1 %s -triple=sparc64-pc-linux-gnu -munwind-tables -emit-llvm -o - \
+// RUN: | FileCheck --check-prefixes=CHECK,CHECK-CLONE,CHECK-NONOPT %s
+// RUN: %clang_cc1 %s -triple=sparc64-pc-linux-gnu -debug-info-kind=standalone -dwarf-version=5 -munwind-tables -emit-llvm -o - \
+// RUN: | FileCheck --check-prefixes=CHECK,CHECK-CLONE,CHECK-NONOPT,CHECK-DBG %s
+// RUN: %clang_cc1 %s -triple=sparc64-pc-linux-gnu -munwind-tables -emit-llvm -o - -O1 -disable-llvm-passes \
+// RUN: | FileCheck --check-prefixes=CHECK,CHECK-CLONE,CHECK-OPT %s
+
+// Test x86_64, which uses musttail for variadic thunks.
+// RUN: %clang_cc1 %s -triple=x86_64-pc-linux-gnu -munwind-tables -emit-llvm -o - -O1 -disable-llvm-passes \
+// RUN: | FileCheck --check-prefixes=CHECK,CHECK-TAIL,CHECK-OPT %s
+
+// Finally, reuse these tests for the MS ABI.
+// RUN: %clang_cc1 %s -triple=x86_64-windows-msvc -munwind-tables -emit-llvm -o - -O1 -disable-llvm-passes \
+// RUN: | FileCheck --check-prefixes=WIN64 %s
+
namespace Test1 {
@@ -23,6 +37,11 @@ struct C : A, B {
// CHECK-LABEL: define void @_ZThn8_N5Test11C1fEv(
// CHECK-DBG-NOT: dbg.declare
// CHECK: ret void
+//
+// WIN64-LABEL: define dso_local void @"?f@C@Test1@@UEAAXXZ"(
+// WIN64-LABEL: define linkonce_odr dso_local void @"?f@C@Test1@@W7EAAXXZ"(
+// WIN64: getelementptr i8, i8* {{.*}}, i32 -8
+// WIN64: ret void
void C::f() { }
}
@@ -45,6 +64,10 @@ struct B : virtual A {
// CHECK: ret void
void B::f() { }
+// No thunk is used for this case in the MS ABI.
+// WIN64-LABEL: define dso_local void @"?f@B@Test2@@UEAAXXZ"(
+// WIN64-NOT: define {{.*}} void @"?f@B@Test2
+
}
namespace Test3 {
@@ -65,6 +88,7 @@ struct B : A {
};
// CHECK: define %{{.*}}* @_ZTch0_v0_n24_N5Test31B1fEv(
+// WIN64: define weak_odr dso_local %{{.*}} @"?f@B@Test3@@QEAAPEAUV1@2@XZ"(
V2 *B::f() { return 0; }
}
@@ -92,6 +116,10 @@ struct __attribute__((visibility("protected"))) C : A, B {
// CHECK: ret void
void C::f() { }
+// Visibility doesn't matter on COFF, but whatever. We could add an ELF test
+// mode later.
+// WIN64-LABEL: define protected void @"?f@C@Test4@@UEAAXXZ"(
+// WIN64-LABEL: define linkonce_odr protected void @"?f@C@Test4@@W7EAAXXZ"(
}
// Check that the thunk gets internal linkage.
@@ -119,6 +147,8 @@ namespace Test4B {
c.f();
}
}
+// Not sure why this isn't delayed like in Itanium.
+// WIN64-LABEL: define internal void @"?f@C@?A0xAEF74CE7@Test4B@@UEAAXXZ"(
namespace Test5 {
@@ -134,6 +164,7 @@ struct B : virtual A {
void f(B b) {
b.f();
}
+// No thunk in MS ABI in this case.
}
namespace Test6 {
@@ -178,6 +209,10 @@ namespace Test6 {
// CHECK: {{call void @_ZN5Test66Thunks1fEv.*sret}}
// CHECK: ret void
X Thunks::f() { return X(); }
+
+ // WIN64-LABEL: define linkonce_odr dso_local void @"?f@Thunks@Test6@@WBA@EAA?AUX@2@XZ"({{.*}} sret %{{.*}})
+ // WIN64-NOT: memcpy
+ // WIN64: tail call void @"?f@Thunks@Test6@@UEAA?AUX@2@XZ"({{.*}} sret %{{.*}})
}
namespace Test7 {
@@ -224,6 +259,8 @@ namespace Test7 {
// CHECK-NOT: memcpy
// CHECK: ret void
void testD() { D d; }
+
+ // MS C++ ABI doesn't use a thunk, so this case isn't interesting.
}
namespace Test8 {
@@ -241,6 +278,8 @@ namespace Test8 {
// CHECK-NOT: memcpy
// CHECK: ret void
void C::bar(NonPOD var) {}
+
+ // MS C++ ABI doesn't use a thunk, so this case isn't interesting.
}
// PR7241: Emitting thunks for a method shouldn't require the vtable for
@@ -287,6 +326,16 @@ namespace Test11 {
// CHECK: define {{.*}} @_ZTch0_v0_n32_N6Test111C1fEv(
// CHECK-DBG-NOT: dbg.declare
// CHECK: ret
+
+ // WIN64-LABEL: define dso_local %{{.*}}* @"?f@C@Test11@@UEAAPEAU12@XZ"(i8*
+
+ // WIN64-LABEL: define weak_odr dso_local %{{.*}}* @"?f@C@Test11@@QEAAPEAUA@2@XZ"(i8*
+ // WIN64: call %{{.*}}* @"?f@C@Test11@@UEAAPEAU12@XZ"(i8* %{{.*}})
+ //
+ // Match the vbtable return adjustment.
+ // WIN64: load i32*, i32** %{{[^,]*}}, align 8
+ // WIN64: getelementptr inbounds i32, i32* %{{[^,]*}}, i32 1
+ // WIN64: load i32, i32* %{{[^,]*}}, align 4
}
// Varargs thunk test.
@@ -301,7 +350,8 @@ namespace Test12 {
virtual void c();
virtual C* f(int x, ...);
};
- C* C::f(int x, ...) { return this; }
+ C* makeC();
+ C* C::f(int x, ...) { return makeC(); }
// C::f
// CHECK: define {{.*}} @_ZN6Test121C1fEiz
@@ -312,6 +362,28 @@ namespace Test12 {
// CHECK-DBG-NOT: dbg.declare
// CHECK: getelementptr inbounds i8, i8* {{.*}}, i64 -8
// CHECK: getelementptr inbounds i8, i8* {{.*}}, i64 8
+
+ // The vtable layout goes:
+ // C vtable in A:
+ // - f impl, no adjustment
+ // C vtable in B:
+ // - f thunk 2, covariant, clone
+ // - f thunk 2, musttail this adjust to impl
+ // FIXME: The weak_odr linkage is probably not necessary and just an artifact
+ // of Itanium ABI details.
+ // WIN64-LABEL: define dso_local {{.*}} @"?f@C@Test12@@UEAAPEAU12@HZZ"(
+ // WIN64: call %{{.*}}* @"?makeC@Test12@@YAPEAUC@1@XZ"()
+ //
+ // This thunk needs return adjustment, clone.
+ // WIN64-LABEL: define weak_odr dso_local {{.*}} @"?f@C@Test12@@W7EAAPEAUB@2@HZZ"(
+ // WIN64: call %{{.*}}* @"?makeC@Test12@@YAPEAUC@1@XZ"()
+ // WIN64: getelementptr inbounds i8, i8* %{{.*}}, i32 8
+ //
+ // Musttail call back to the A implementation after this adjustment from B to A.
+ // WIN64-LABEL: define linkonce_odr dso_local %{{.*}}* @"?f@C@Test12@@W7EAAPEAU12@HZZ"(
+ // WIN64: getelementptr i8, i8* %{{[^,]*}}, i32 -8
+ // WIN64: musttail call {{.*}} @"?f@C@Test12@@UEAAPEAU12@HZZ"(
+ C c;
}
// PR13832
@@ -339,6 +411,17 @@ namespace Test13 {
// CHECK: getelementptr inbounds i8, i8* {{.*}}, i64 -24
// CHECK: getelementptr inbounds i8, i8* {{.*}}, i64 8
// CHECK: ret %"struct.Test13::D"*
+
+ // WIN64-LABEL: define weak_odr dso_local dereferenceable(32) %"struct.Test13::D"* @"?foo1@D@Test13@@$4PPPPPPPE@A@EAAAEAUB1@2@XZ"(
+ // This adjustment.
+ // WIN64: getelementptr inbounds i8, i8* {{.*}}, i64 -12
+ // Call implementation.
+ // WIN64: call {{.*}} @"?foo1@D@Test13@@UEAAAEAU12@XZ"(i8* {{.*}})
+ // Virtual + nonvirtual return adjustment.
+ // WIN64: load i32*, i32** %{{[^,]*}}, align 8
+ // WIN64: getelementptr inbounds i32, i32* %{{[^,]*}}, i32 1
+ // WIN64: load i32, i32* %{{[^,]*}}, align 4
+ // WIN64: getelementptr inbounds i8, i8* %{{[^,]*}}, i32 %{{[^,]*}}
}
namespace Test14 {
@@ -374,9 +457,16 @@ namespace Test15 {
void C::c() {}
// C::c
- // CHECK: declare void @_ZN6Test151C1fEiz
+ // CHECK-CLONE: declare void @_ZN6Test151C1fEiz
// non-virtual thunk to C::f
- // CHECK: declare void @_ZThn8_N6Test151C1fEiz
+ // CHECK-CLONE: declare void @_ZThn8_N6Test151C1fEiz
+
+ // If we have musttail, then we emit the thunk as available_externally.
+ // CHECK-TAIL: declare void @_ZN6Test151C1fEiz
+ // CHECK-TAIL: define available_externally void @_ZThn8_N6Test151C1fEiz({{.*}})
+ // CHECK-TAIL: musttail call void (%"struct.Test15::C"*, i32, ...) @_ZN6Test151C1fEiz({{.*}}, ...)
+
+ // MS C++ ABI doesn't use a thunk, so this case isn't interesting.
}
namespace Test16 {
@@ -398,6 +488,33 @@ D::~D() {}
// CHECK: ret void
}
+namespace Test17 {
+class A {
+ virtual void f(const char *, ...);
+};
+class B {
+ virtual void f(const char *, ...);
+};
+class C : A, B {
+ virtual void anchor();
+ void f(const char *, ...) override;
+};
+// Key method and object anchor vtable for Itanium and MSVC.
+void C::anchor() {}
+C c;
+
+// CHECK-CLONE-LABEL: declare void @_ZThn8_N6Test171C1fEPKcz(
+
+// CHECK-TAIL-LABEL: define available_externally void @_ZThn8_N6Test171C1fEPKcz(
+// CHECK-TAIL: getelementptr inbounds i8, i8* %{{.*}}, i64 -8
+// CHECK-TAIL: musttail call {{.*}} @_ZN6Test171C1fEPKcz({{.*}}, ...)
+
+// MSVC-LABEL: define linkonce_odr dso_local void @"?f@C@Test17@@G7EAAXPEBDZZ"
+// MSVC-SAME: (%"class.Test17::C"* %this, i8* %[[ARG:[^,]+]], ...)
+// MSVC: getelementptr i8, i8* %{{.*}}, i32 -8
+// MSVC: musttail call void (%"class.Test17::C"*, i8*, ...) @"?f@C@Test17@@EEAAXPEBDZZ"(%"class.Test17::C"* %{{.*}}, i8* %[[ARG]], ...)
+}
+
/**** The following has to go at the end of the file ****/
// checking without opt
@@ -421,5 +538,9 @@ D::~D() {}
// CHECK-OPT-LABEL: define linkonce_odr void @_ZN6Test101C3fooEv
// CHECK-OPT-LABEL: define linkonce_odr void @_ZThn8_N6Test101C3fooEv
+// This is from Test10:
+// WIN64-LABEL: define linkonce_odr dso_local void @"?foo@C@Test10@@UEAAXXZ"(
+// WIN64-LABEL: define linkonce_odr dso_local void @"?foo@C@Test10@@W7EAAXXZ"(
+
// CHECK-NONOPT: attributes [[NUW]] = { noinline nounwind optnone uwtable{{.*}} }
// CHECK-OPT: attributes [[NUW]] = { nounwind uwtable{{.*}} }
OpenPOWER on IntegriCloud