Index: lib/CodeGen/CGDeclCXX.cpp =================================================================== --- lib/CodeGen/CGDeclCXX.cpp +++ lib/CodeGen/CGDeclCXX.cpp @@ -86,13 +86,18 @@ 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. - const CXXRecordDecl *record = nullptr; - if (dtorKind == QualType::DK_cxx_destructor && - (record = type->getAsCXXRecordDecl())) { - assert(!record->hasTrivialDestructor()); - CXXDestructorDecl *dtor = record->getDestructor(); + // 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 = type->getAsCXXRecordDecl(); + bool CanRegisterDestructor = Record && + !CGM.getCXXABI().HasThisReturn(GlobalDecl( + Record->getDestructor(), Dtor_Complete)); + + if (dtorKind == QualType::DK_cxx_destructor && Record && + (CanRegisterDestructor || !CGM.getCodeGenOpts().CXAAtExit)) { + assert(!Record->hasTrivialDestructor()); + CXXDestructorDecl *dtor = Record->getDestructor(); function = CGM.getAddrOfCXXStructor(dtor, StructorType::Complete); argument = llvm::ConstantExpr::getBitCast( Index: test/CodeGenCXX/runtimecc.cpp =================================================================== --- test/CodeGenCXX/runtimecc.cpp +++ test/CodeGenCXX/runtimecc.cpp @@ -22,8 +22,13 @@ 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]] Index: test/CodeGenCXX/static-destructor.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/static-destructor.cpp @@ -0,0 +1,30 @@ +// 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 +// RUN: %clang_cc1 %s -triple=armv7-apple-darwin9 -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)