Index: cfe/trunk/lib/CodeGen/CGDeclCXX.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGDeclCXX.cpp +++ cfe/trunk/lib/CodeGen/CGDeclCXX.cpp @@ -86,11 +86,15 @@ 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(); Index: cfe/trunk/test/CodeGenCXX/runtimecc.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/runtimecc.cpp +++ cfe/trunk/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: cfe/trunk/test/CodeGenCXX/static-destructor.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/static-destructor.cpp +++ cfe/trunk/test/CodeGenCXX/static-destructor.cpp @@ -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)