rnk updated this revision to Diff 218995. rnk added a comment. - emit an error if we try to clone without a definition
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D67028/new/ https://reviews.llvm.org/D67028 Files: clang/lib/CodeGen/CGVTables.cpp clang/test/CodeGenCXX/linetable-virtual-variadic.cpp clang/test/CodeGenCXX/ms-thunks-variadic-return.cpp clang/test/CodeGenCXX/thunks.cpp
Index: clang/test/CodeGenCXX/thunks.cpp =================================================================== --- clang/test/CodeGenCXX/thunks.cpp +++ 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 @@ // 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 @@ // 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 @@ }; // 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 @@ // 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 @@ 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 @@ void f(B b) { b.f(); } +// No thunk in MS ABI in this case. } namespace Test6 { @@ -178,6 +209,10 @@ // 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 @@ // 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 @@ // 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 @@ // 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 @@ 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 @@ // 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 @@ // 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 @@ 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 @@ // 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 @@ // 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{{.*}} } Index: clang/test/CodeGenCXX/ms-thunks-variadic-return.cpp =================================================================== --- /dev/null +++ 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; Index: clang/test/CodeGenCXX/linetable-virtual-variadic.cpp =================================================================== --- clang/test/CodeGenCXX/linetable-virtual-variadic.cpp +++ 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(...); Index: clang/lib/CodeGen/CGVTables.cpp =================================================================== --- clang/lib/CodeGen/CGVTables.cpp +++ clang/lib/CodeGen/CGVTables.cpp @@ -166,6 +166,15 @@ 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 BaseFn; + } + assert(!BaseFn->isDeclaration() && "cannot clone undefined variadic method"); + // Clone to thunk. llvm::ValueToValueMapTy VMap; @@ -201,6 +210,8 @@ 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 @@ *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 @@ 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);
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits