Index: lib/CodeGen/CGCXXABI.h =================================================================== --- lib/CodeGen/CGCXXABI.h +++ lib/CodeGen/CGCXXABI.h @@ -106,6 +106,16 @@ virtual bool hasMostDerivedReturn(GlobalDecl GD) const { return false; } + /// Returns true if the target allows calling a function through a pointer + /// with a different signature than the actual function (or equivalently, + /// bitcasting a function or function pointer to a different function type). + /// In principle in the most general case this could depend on the target, the + /// calling convention, and the actual types of the arguments and return + /// value. Here it just means whether the signature mismatch could *ever* be + /// allowed; in other words, does the target do strict checking of signatures + /// for all calls. + virtual bool canCallMismatchedFunctionType() const { return true; } + /// If the C++ ABI requires the given type be returned in a particular way, /// this method sets RetAI and returns true. virtual bool classifyReturnType(CGFunctionInfo &FI) const = 0; Index: lib/CodeGen/CGDeclCXX.cpp =================================================================== --- lib/CodeGen/CGDeclCXX.cpp +++ lib/CodeGen/CGDeclCXX.cpp @@ -86,13 +86,21 @@ 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. + // Under some ABIs, destructors return this instead of void, and cannot be + // passed directly to __cxa_atexit if the target does not allow this mismatch. + const CXXRecordDecl *Record = type->getAsCXXRecordDecl(); + bool CanRegisterDestructor = + Record && (!CGM.getCXXABI().HasThisReturn( + GlobalDecl(Record->getDestructor(), Dtor_Complete)) || + CGM.getCXXABI().canCallMismatchedFunctionType()); + // If __cxa_atexit is disabled via a flag, a different helper function is + // generated elsewhere which uses atexit instead, and it takes the destructor + // directly. + bool UsingExternalHelper = !CGM.getCodeGenOpts().CXAAtExit; + if (Record && (CanRegisterDestructor || UsingExternalHelper)) { + assert(!Record->hasTrivialDestructor()); + CXXDestructorDecl *dtor = Record->getDestructor(); function = CGM.getAddrOfCXXStructor(dtor, StructorType::Complete); argument = llvm::ConstantExpr::getBitCast( Index: lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- lib/CodeGen/ItaniumCXXABI.cpp +++ lib/CodeGen/ItaniumCXXABI.cpp @@ -443,6 +443,7 @@ (isa(GD.getDecl()) && GD.getDtorType() != Dtor_Deleting); } + bool canCallMismatchedFunctionType() const override { return false; } }; } Index: test/CodeGenCXX/static-destructor.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/static-destructor.cpp @@ -0,0 +1,36 @@ +// 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=ARM %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) + +// ARM destructors return this, but can be registered directly with __cxa_atexit +// because the calling conventions tolerate the mismatch. +// ARM: define internal void @__cxx_global_var_init() +// ARM-NEXT: entry: +// ARM-NEXT: %0 = call i32 @__cxa_atexit(void (i8*)* bitcast (%class.Foo* (%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)