Author: dschuff Date: Tue May 10 12:44:46 2016 New Revision: 269085 URL: http://llvm.org/viewvc/llvm-project?rev=269085&view=rev Log: Do not register incompatible C++ destructors with __cxa_atexit
Summary: For a static object with a nontrivial destructor, clang generates an initializer function (__cxx_global_var_init) which registers that object's destructor using __cxa_atexit. However some ABIs (ARM, WebAssembly) use destructors that return 'this' instead of having void return (which does not match the signature of function pointers passed to __cxa_atexit). This results in undefined behavior when the destructors are called. All the calling conventions I know of on ARM can tolerate this, but WebAssembly requires the signatures of indirect calls to match the called function. This patch disables that direct registration of destructors for ABIs that have this-returning destructors. Subscribers: aemerson, jfb, cfe-commits, dschuff Differential Revision: http://reviews.llvm.org/D19275 Added: cfe/trunk/test/CodeGenCXX/static-destructor.cpp Modified: cfe/trunk/lib/CodeGen/CGDeclCXX.cpp cfe/trunk/test/CodeGenCXX/runtimecc.cpp Modified: cfe/trunk/lib/CodeGen/CGDeclCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDeclCXX.cpp?rev=269085&r1=269084&r2=269085&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CGDeclCXX.cpp (original) +++ cfe/trunk/lib/CodeGen/CGDeclCXX.cpp Tue May 10 12:44:46 2016 @@ -86,11 +86,15 @@ static void EmitDeclDestroy(CodeGenFunct llvm::Constant *function; llvm::Constant *argument; - // Special-case non-array C++ destructors, where there's a function - // with the right signature that we can just call. + // Special-case non-array C++ destructors, if they have the right signature + // that can be directly registered with __cxa_atexit. If __cxa_atexit is + // disabled via a flag, a different helper function is generated anyway. const CXXRecordDecl *record = nullptr; if (dtorKind == QualType::DK_cxx_destructor && - (record = type->getAsCXXRecordDecl())) { + (record = type->getAsCXXRecordDecl()) && + (!CGM.getCXXABI().HasThisReturn( + GlobalDecl(record->getDestructor(), Dtor_Complete)) || + !CGM.getCodeGenOpts().CXAAtExit)) { assert(!record->hasTrivialDestructor()); CXXDestructorDecl *dtor = record->getDestructor(); Modified: cfe/trunk/test/CodeGenCXX/runtimecc.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/runtimecc.cpp?rev=269085&r1=269084&r2=269085&view=diff ============================================================================== --- cfe/trunk/test/CodeGenCXX/runtimecc.cpp (original) +++ cfe/trunk/test/CodeGenCXX/runtimecc.cpp Tue May 10 12:44:46 2016 @@ -22,8 +22,13 @@ namespace test0 { A global; // CHECK-LABEL: define internal void @__cxx_global_var_init() // CHECK: call [[A]]* @_ZN5test01AC1Ev([[A]]* @_ZN5test06globalE) -// CHECK-NEXT: call i32 @__cxa_atexit(void (i8*)* bitcast ([[A]]* ([[A]]*)* @_ZN5test01AD1Ev to void (i8*)*), i8* bitcast ([[A]]* @_ZN5test06globalE to i8*), i8* @__dso_handle) [[NOUNWIND:#[0-9]+]] +// CHECK-NEXT: call i32 @__cxa_atexit(void (i8*)* @__cxx_global_array_dtor, i8* null, i8* @__dso_handle) [[NOUNWIND:#[0-9]+]] // CHECK-NEXT: ret void + +// CHECK-LABEL: define internal void @__cxx_global_array_dtor(i8*) +// CHECK: call [[A]]* @_ZN5test01AD1Ev([[A]]* @_ZN5test06globalE) +// CHECK-NEXT: ret void + } // CHECK: declare i32 @__cxa_atexit(void (i8*)*, i8*, i8*) [[NOUNWIND]] Added: cfe/trunk/test/CodeGenCXX/static-destructor.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/static-destructor.cpp?rev=269085&view=auto ============================================================================== --- cfe/trunk/test/CodeGenCXX/static-destructor.cpp (added) +++ cfe/trunk/test/CodeGenCXX/static-destructor.cpp Tue May 10 12:44:46 2016 @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 %s -triple=x86_64-pc-linux -emit-llvm -o - | FileCheck --check-prefix=X86 %s +// RUN: %clang_cc1 %s -triple=wasm32 -emit-llvm -o - | FileCheck --check-prefix=WASM %s + +// Test that destructors are not passed directly to __cxa_atexit when their +// signatures do not match the type of its first argument. +// e.g. ARM and WebAssembly have destructors that return this instead of void. + + +class Foo { + public: + ~Foo() { + } +}; + +Foo global; + +// X86 destructors have void return, and are registered directly with __cxa_atexit. +// X86: define internal void @__cxx_global_var_init() +// X86-NEXT: entry: +// X86-NEXT: %0 = call i32 @__cxa_atexit(void (i8*)* bitcast (void (%class.Foo*)* @_ZN3FooD1Ev to void (i8*)*), i8* getelementptr inbounds (%class.Foo, %class.Foo* @global, i32 0, i32 0), i8* @__dso_handle) + +// Wasm destructors return this, and use a wrapper function, which is registered +// with __cxa_atexit. +// WASM: define internal void @__cxx_global_var_init() +// WASM-NEXT: entry: +// WASM-NEXT: %0 = call i32 @__cxa_atexit(void (i8*)* @__cxx_global_array_dtor, i8* null, i8* @__dso_handle) + +// WASM: define internal void @__cxx_global_array_dtor(i8*) +// WASM: %call = call %class.Foo* @_ZN3FooD1Ev(%class.Foo* @global) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits