https://github.com/Fznamznon created https://github.com/llvm/llvm-project/pull/139566
While working on vector deleting destructors support (GH19772), I noticed that MSVC produces different code in scalar deleting destructor body depending on whether class defined its own operator delete. In MSABI deleting destructors accept an additional implicit flag parameter allowing some sort of flexibility. The mismatch I noticed is that whenever a global operator delete is called, i.e. ::delete, in the code produced by MSVC the implicit flag argument has a value that makes the 3rd bit set, i.e. "5" for scalar deleting destructors "7" for vector deleting destructors. Prior to this patch, clang handled ::delete via calling global operator delete direct after the destructor call and not calling class operator delete in scalar deleting destructor body by passing "0" as implicit flag argument value. This is fine until there is an attempt to link binaries compiled with clang with binaries compiled with MSVC. The problem is that in binaries produced by MSVC the callsite of the destructor won't call global operator delete because it is assumed that the destructor will do that and a destructor body generated by clang will never do. This patch removes call to global operator delete from the callsite and add additional check of the 3rd bit of the implicit parameter inside of scalar deleting destructor body. >From 1b0b6242e5749e776f02581ba8600d853bfef322 Mon Sep 17 00:00:00 2001 From: "Podchishchaeva, Mariya" <mariya.podchishcha...@intel.com> Date: Mon, 12 May 2025 07:25:57 -0700 Subject: [PATCH] [win][clang] Align scalar deleting destructors with MSABI While working on vector deleting destructors support (GH19772), I noticed that MSVC produces different code in scalar deleting destructor body depending on whether class defined its own operator delete. In MSABI deleting destructors accept an additional implicit flag parameter allowing some sort of flexibility. The mismatch I noticed is that whenever a global operator delete is called, i.e. ::delete, in the code produced by MSVC the implicit flag argument has a value that makes the 3rd bit set, i.e. "5" for scalar deleting destructors "7" for vector deleting destructors. Prior to this patch, clang handled ::delete via calling global operator delete direct after the destructor call and not calling class operator delete in scalar deleting destructor body by passing "0" as implicit flag argument value. This is fine until there is an attempt to link binaries compiled with clang with binaries compiled with MSVC. The problem is that in binaries produced by MSVC the callsite of the destructor won't call global operator delete because it is assumed that the destructor will do that and a destructor body generated by clang will never do. This patch removes call to global operator delete from the callsite and add additional check of the 3rd bit of the implicit parameter inside of scalar deleting destructor body. --- clang/include/clang/AST/DeclCXX.h | 11 +++ clang/include/clang/Sema/Sema.h | 10 ++- clang/lib/CodeGen/CGClass.cpp | 77 +++++++++++++++---- clang/lib/CodeGen/MicrosoftCXXABI.cpp | 11 +-- clang/lib/Sema/SemaDeclCXX.cpp | 15 ++++ clang/lib/Sema/SemaExprCXX.cpp | 19 +++-- .../CodeGenCXX/cxx2a-destroying-delete.cpp | 59 ++++++++++++-- .../CodeGenCXX/microsoft-abi-structors.cpp | 44 ++++++++++- 8 files changed, 202 insertions(+), 44 deletions(-) diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index b7980137002aa..cc2832ea99d4a 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -2855,6 +2855,7 @@ class CXXDestructorDecl : public CXXMethodDecl { // FIXME: Don't allocate storage for these except in the first declaration // of a virtual destructor. FunctionDecl *OperatorDelete = nullptr; + FunctionDecl *OperatorGlobalDelete = nullptr; Expr *OperatorDeleteThisArg = nullptr; CXXDestructorDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, @@ -2885,6 +2886,16 @@ class CXXDestructorDecl : public CXXMethodDecl { return getCanonicalDecl()->OperatorDelete; } + const FunctionDecl *getOperatorGlobalDelete() const { + return getCanonicalDecl()->OperatorGlobalDelete; + } + + void setOperatorGlobalDelete(FunctionDecl *OD) { + auto *First = cast<CXXDestructorDecl>(getFirstDecl()); + if (OD && !First->OperatorGlobalDelete) + First->OperatorGlobalDelete = OD; + } + Expr *getOperatorDeleteThisArg() const { return getCanonicalDecl()->OperatorDeleteThisArg; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6ea7ee281e14d..5f2ceebcd42e3 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8495,10 +8495,12 @@ class Sema final : public SemaBase { bool Diagnose = true); FunctionDecl *FindUsualDeallocationFunction(SourceLocation StartLoc, ImplicitDeallocationParameters, - DeclarationName Name); - FunctionDecl *FindDeallocationFunctionForDestructor(SourceLocation StartLoc, - CXXRecordDecl *RD, - bool Diagnose = true); + DeclarationName Name, + bool Diagnose = true); + FunctionDecl * + FindDeallocationFunctionForDestructor(SourceLocation StartLoc, + CXXRecordDecl *RD, bool Diagnose = true, + bool LookForGlobal = false); /// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in: /// @code ::delete ptr; @endcode diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index befbfc64a680c..360876244cad9 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -1589,25 +1589,74 @@ namespace { void EmitConditionalDtorDeleteCall(CodeGenFunction &CGF, llvm::Value *ShouldDeleteCondition, bool ReturnAfterDelete) { + const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CGF.CurCodeDecl); + const CXXRecordDecl *ClassDecl = Dtor->getParent(); + const FunctionDecl *OD = Dtor->getOperatorDelete(); + assert(OD->isDestroyingOperatorDelete() == ReturnAfterDelete && + "unexpected value for ReturnAfterDelete"); + auto *CondTy = cast<llvm::IntegerType>(ShouldDeleteCondition->getType()); + if (OD->isDestroyingOperatorDelete()) { + llvm::BasicBlock *CallDtor = CGF.createBasicBlock("dtor.call_dtor"); + llvm::BasicBlock *DontCallDtor = CGF.createBasicBlock("dtor.entry_cont"); + // Third bit set signals that global operator delete is called. That means + // despite class having destroying operator delete which is responsible + // for calling dtor, we need to call dtor because global operator delete + // won't do that. + llvm::Value *Check3rdBit = CGF.Builder.CreateAnd( + ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 4)); + llvm::Value *ShouldCallDtor = CGF.Builder.CreateIsNull(Check3rdBit); + CGF.Builder.CreateCondBr(ShouldCallDtor, DontCallDtor, CallDtor); + CGF.EmitBlock(CallDtor); + QualType ThisTy = Dtor->getFunctionObjectParameterType(); + CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false, + /*Delegating=*/false, CGF.LoadCXXThisAddress(), + ThisTy); + CGF.Builder.CreateBr(DontCallDtor); + CGF.EmitBlock(DontCallDtor); + } llvm::BasicBlock *callDeleteBB = CGF.createBasicBlock("dtor.call_delete"); llvm::BasicBlock *continueBB = CGF.createBasicBlock("dtor.continue"); - llvm::Value *ShouldCallDelete - = CGF.Builder.CreateIsNull(ShouldDeleteCondition); + // First bit set signals that operator delete must be called. + llvm::Value *Check1stBit = CGF.Builder.CreateAnd( + ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 1)); + llvm::Value *ShouldCallDelete = CGF.Builder.CreateIsNull(Check1stBit); CGF.Builder.CreateCondBr(ShouldCallDelete, continueBB, callDeleteBB); CGF.EmitBlock(callDeleteBB); - const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CGF.CurCodeDecl); - const CXXRecordDecl *ClassDecl = Dtor->getParent(); - CGF.EmitDeleteCall(Dtor->getOperatorDelete(), - LoadThisForDtorDelete(CGF, Dtor), - CGF.getContext().getTagDeclType(ClassDecl)); - assert(Dtor->getOperatorDelete()->isDestroyingOperatorDelete() == - ReturnAfterDelete && - "unexpected value for ReturnAfterDelete"); - if (ReturnAfterDelete) - CGF.EmitBranchThroughCleanup(CGF.ReturnBlock); - else - CGF.Builder.CreateBr(continueBB); + auto EmitDeleteAndGoToEnd = [&](const FunctionDecl *DeleteOp) { + CGF.EmitDeleteCall(DeleteOp, LoadThisForDtorDelete(CGF, Dtor), + CGF.getContext().getTagDeclType(ClassDecl)); + if (ReturnAfterDelete) + CGF.EmitBranchThroughCleanup(CGF.ReturnBlock); + else + CGF.Builder.CreateBr(continueBB); + }; + // If Sema only found a global operator delete previously, the dtor can + // always call it. Otherwise we need to check the third bit and call the + // appropriate operator delete, i.e. global or class-specific. + if (isa<CXXMethodDecl>(OD)) { + // Third bit set signals that global operator delete is called, i.e. + // ::delete appears on the callsite. + llvm::Value *CheckTheBitForGlobDeleteCall = CGF.Builder.CreateAnd( + ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 4)); + llvm::Value *ShouldCallGlobDelete = + CGF.Builder.CreateIsNull(CheckTheBitForGlobDeleteCall); + llvm::BasicBlock *GlobDelete = + CGF.createBasicBlock("dtor.call_glob_delete"); + llvm::BasicBlock *ClassDelete = + CGF.createBasicBlock("dtor.call_class_delete"); + CGF.Builder.CreateCondBr(ShouldCallGlobDelete, ClassDelete, GlobDelete); + CGF.EmitBlock(GlobDelete); + + const FunctionDecl *GlobOD = Dtor->getOperatorGlobalDelete(); + if (GlobOD) + EmitDeleteAndGoToEnd(GlobOD); + else + CGF.Builder.CreateUnreachable(); + + CGF.EmitBlock(ClassDelete); + } + EmitDeleteAndGoToEnd(OD); CGF.EmitBlock(continueBB); } diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index aa9a55ae05927..4128b7306687a 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -893,13 +893,8 @@ void MicrosoftCXXABI::emitVirtualObjectDelete(CodeGenFunction &CGF, QualType ElementType, const CXXDestructorDecl *Dtor) { // FIXME: Provide a source location here even though there's no - // CXXMemberCallExpr for dtor call. - bool UseGlobalDelete = DE->isGlobalDelete(); - CXXDtorType DtorType = UseGlobalDelete ? Dtor_Complete : Dtor_Deleting; - llvm::Value *MDThis = EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr, DE, - /*CallOrInvoke=*/nullptr); - if (UseGlobalDelete) - CGF.EmitDeleteCall(DE->getOperatorDelete(), MDThis, ElementType); + EmitVirtualDestructorCall(CGF, Dtor, Dtor_Deleting, Ptr, DE, + /*CallOrInvoke=*/nullptr); } void MicrosoftCXXABI::emitRethrow(CodeGenFunction &CGF, bool isNoReturn) { @@ -2016,7 +2011,7 @@ llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall( ASTContext &Context = getContext(); llvm::Value *ImplicitParam = llvm::ConstantInt::get( llvm::IntegerType::getInt32Ty(CGF.getLLVMContext()), - DtorType == Dtor_Deleting); + (DtorType == Dtor_Deleting) | 4 * (D && D->isGlobalDelete())); QualType ThisTy; if (CE) { diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index cbccb567e2adf..258d78c2c9425 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -11400,6 +11400,21 @@ bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) { DiagnoseUseOfDecl(OperatorDelete, Loc); MarkFunctionReferenced(Loc, OperatorDelete); Destructor->setOperatorDelete(OperatorDelete, ThisArg); + + if (isa<CXXMethodDecl>(OperatorDelete) && + Context.getTargetInfo().getCXXABI().isMicrosoft()) { + // In Microsoft ABI whenever a class has a defined operator delete, + // scalar deleting destructors check the 3rd bit of the implicit + // parameter and if it is set, then, global operator delete must be + // called instead of class-specific one. Find and save global operator + // for that case. Do not diagnose because even if we fail to find the + // operator, it won't be possible to compile and execute the code that + // requires it. + FunctionDecl *GlobalOperatorDelete = + FindDeallocationFunctionForDestructor(Loc, RD, /*Diagnose*/ false, + /*LookForGlobal*/ true); + Destructor->setOperatorGlobalDelete(GlobalOperatorDelete); + } } } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index b2a982e953012..e6b4a09db9d62 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -3565,7 +3565,7 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name, FunctionDecl * Sema::FindUsualDeallocationFunction(SourceLocation StartLoc, ImplicitDeallocationParameters IDP, - DeclarationName Name) { + DeclarationName Name, bool Diagnose) { DeclareGlobalNewDelete(); LookupResult FoundDelete(*this, Name, StartLoc, LookupOrdinaryName); @@ -3580,7 +3580,7 @@ Sema::FindUsualDeallocationFunction(SourceLocation StartLoc, if (!Result) return nullptr; - if (CheckDeleteOperator(*this, StartLoc, StartLoc, /*Diagnose=*/true, + if (CheckDeleteOperator(*this, StartLoc, StartLoc, Diagnose, FoundDelete.getNamingClass(), Result.Found, Result.FD)) return nullptr; @@ -3591,7 +3591,8 @@ Sema::FindUsualDeallocationFunction(SourceLocation StartLoc, FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc, CXXRecordDecl *RD, - bool Diagnose) { + bool Diagnose, + bool LookForGlobal) { DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Delete); FunctionDecl *OperatorDelete = nullptr; @@ -3600,18 +3601,20 @@ FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc, DeallocType, ShouldUseTypeAwareOperatorNewOrDelete(), AlignedAllocationMode::No, SizedDeallocationMode::No}; - if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete, IDP, Diagnose)) - return nullptr; + if (!LookForGlobal) { + if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete, IDP, Diagnose)) + return nullptr; - if (OperatorDelete) - return OperatorDelete; + if (OperatorDelete) + return OperatorDelete; + } // If there's no class-specific operator delete, look up the global // non-array delete. IDP.PassAlignment = alignedAllocationModeFromBool( hasNewExtendedAlignment(*this, DeallocType)); IDP.PassSize = SizedDeallocationMode::Yes; - return FindUsualDeallocationFunction(Loc, IDP, Name); + return FindUsualDeallocationFunction(Loc, IDP, Name, Diagnose); } bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, diff --git a/clang/test/CodeGenCXX/cxx2a-destroying-delete.cpp b/clang/test/CodeGenCXX/cxx2a-destroying-delete.cpp index f6f4a2ff735cc..8af2b42e98e7b 100644 --- a/clang/test/CodeGenCXX/cxx2a-destroying-delete.cpp +++ b/clang/test/CodeGenCXX/cxx2a-destroying-delete.cpp @@ -159,20 +159,43 @@ H::~H() { call_in_dtor(); } // CHECK-ITANIUM-NOT: call // CHECK-ITANIUM: } -// CHECK-MSABI64-LABEL: define {{.*}} @"??_GH@@UEAAPEAXI@Z"( +// CHECK-MSABI64-LABEL: define {{.*}} @"??_GH@@UEAAPEAXI@Z"({{.*}}, // CHECK-MSABI32-LABEL: define {{.*}} @"??_GH@@UAEPAXI@Z"( +// CHECK-MSABI-SAME: i32 noundef %[[IP:.*]]) // CHECK-MSABI-NOT: call{{ }} -// CHECK-MSABI: load i32 -// CHECK-MSABI: icmp eq i32 {{.*}}, 0 -// CHECK-MSABI: br i1 +// CHECK-MSABI: store i32 %[[IP]], ptr %[[IP_ALLOCA:.*]] +// CHECK-MSABI: %[[IMP_PARAM:.*]] = load i32, ptr %[[IP_ALLOCA]] +// CHECK-MSABI-NEXT: %[[THIRDBIT:.*]] = and i32 %[[IMP_PARAM]], 4 +// CHECK-MSABI-NEXT: %[[CHCK:.*]] = icmp eq i32 %[[THIRDBIT]], 0 +// CHECK-MSABI-NEXT: br i1 %[[CHCK]], label %dtor.entry_cont, label %dtor.call_dtor +// +// CHECK_MSABI-LABEL: dtor.call_dtor: +// CHECK_MSABI-NEXT: call void @"??1H@@UEAA@XZ"({{.*}}) +// CHECK_MSABI-NEXT: br label %dtor.entry_cont +// +// CHECK_MSABI-LABEL: dtor.entry_cont: +// CHECK_MSABI-NEXT: %[[FIRSTBIT:.*]] = and i32 %[[IMP_PARAM]], 1 +// CHECK_MSABI-NEXT: %[[CHCK1:.*]] = icmp eq i32 %[[FIRSTBIT]], 0 +// CHECK_MSABI-NEXT: br i1 %[[CHCK1]], label %dtor.continue, label %dtor.call_delete +// +// CHECK_MSABI-LABEL: dtor.call_delete: +// CHECK-MSABI: %[[THIRDBIT1:.*]] = and i32 %[[IMP_PARAM]], 4 +// CHECK-MSABI-NEXT: %[[CHCK2:.*]] = icmp eq i32 %[[THIRDBIT1]], 0 +// CHECK-MSABI-NEXT: br i1 %[[CHCK2]], label %dtor.call_class_delete, label %dtor.call_glob_delete +// +// CHECK-MSABI-LABEL: dtor.call_glob_delete: +// CHECK-MSABI64: call void @"??3@YAXPEAX_K@Z"(ptr noundef %{{.*}}, i64 noundef 48) +// CHECK-MSABI32: call void @"??3@YAXPAXIW4align_val_t@std@@@Z"(ptr noundef %{{.*}}, i32 noundef 32, i32 noundef 16) +// CHECK-MSABI-NEXT: br label %[[RETURN:.*]] // +// CHECK-MSABI: dtor.call_class_delete: // CHECK-MSABI-NOT: call{{ }} // CHECK-MSABI64: getelementptr {{.*}}, i64 24 // CHECK-MSABI32: getelementptr {{.*}}, i32 20 // CHECK-MSABI-NOT: call{{ }} // CHECK-MSABI64: call void @"??3F@@SAXPEAU0@Udestroying_delete_t@std@@_KW4align_val_t@2@@Z"({{.*}}, i64 noundef 48, i64 noundef 16) // CHECK-MSABI32: call void @"??3F@@SAXPAU0@Udestroying_delete_t@std@@IW4align_val_t@2@@Z"({{.*}}, i32 noundef 32, i32 noundef 16) -// CHECK-MSABI: br label %[[RETURN:.*]] +// CHECK-MSABI: br label %[[RETURN:]] // // CHECK-MSABI64: call void @"??1H@@UEAA@XZ"( // CHECK-MSABI32: call x86_thiscallcc void @"??1H@@UAE@XZ"( @@ -194,9 +217,31 @@ I::~I() { call_in_dtor(); } // CHECK-MSABI32-LABEL: define {{.*}} @"??_GI@@UAEPAXI@Z"( // CHECK-MSABI-NOT: call{{ }} // CHECK-MSABI: load i32 -// CHECK-MSABI: icmp eq i32 {{.*}}, 0 -// CHECK-MSABI: br i1 +// CHECK-MSABI-NEXT: and i32 %[[IMP_PARAM:.*]], 4 +// CHECK-MSABI-NEXT: icmp eq i32 {{.*}}, 0 +// CHECK-MSABI-NEXT: br i1 %[[CHCK]], label %dtor.entry_cont, label %dtor.call_dtor +// +// CHECK-MSABI: dtor.call_dtor: +// CHECK-MSABI64-NEXT: call void @"??1I@@UEAA@XZ"({{.*}}) +// CHECK-MSABI32-NEXT: call x86_thiscallcc void @"??1I@@UAE@XZ"({{.*}}) +// CHECK-MSABI-NEXT: br label %dtor.entry_cont +// +// CHECK-MSABI: dtor.entry_cont: +// CHECK-MSABI-NEXT: and i32 %[[IMP_PARAM]], 1 +// CHECK-MSABI-NEXT: icmp eq i32 %{{.*}}, 0 +// CHECK-MSABI-NEXT: br i1 %{{.*}}, label %dtor.continue, label %dtor.call_delete +// +// CHECK-MSABI: dtor.call_delete: +// CHECK-MSABI-NEXT: %[[THIRDBIT1:.*]] = and i32 %[[IMP_PARAM]], 4 +// CHECK-MSABI-NEXT: %[[CHCK2:.*]] = icmp eq i32 %[[THIRDBIT1]], 0 +// CHECK-MSABI-NEXT: br i1 %[[CHCK2]], label %dtor.call_class_delete, label %dtor.call_glob_delete +// +// CHECK-MSABI-LABEL: dtor.call_glob_delete: +// CHECK-MSABI64: call void @"??3@YAXPEAX_KW4align_val_t@std@@@Z"(ptr noundef %{{.*}}, i64 noundef 96, i64 noundef 32) +// CHECK-MSABI32: call void @"??3@YAXPAXIW4align_val_t@std@@@Z"(ptr noundef %{{.*}}, i32 noundef 64, i32 noundef 32) +// CHECK-MSABI-NEXT: br label %[[RETURN:.*]] // +// CHECK_MSABI: dtor.call_class_delete: // CHECK-MSABI-NOT: call{{ }} // CHECK-MSABI64: getelementptr {{.*}}, i64 24 // CHECK-MSABI32: getelementptr {{.*}}, i32 20 diff --git a/clang/test/CodeGenCXX/microsoft-abi-structors.cpp b/clang/test/CodeGenCXX/microsoft-abi-structors.cpp index 07abc3d065e5e..9b43483439cf8 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-structors.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-structors.cpp @@ -52,7 +52,8 @@ struct C { // DTORS: store ptr %{{.*}}, ptr %[[RETVAL:retval]] // DTORS: %[[SHOULD_DELETE_VALUE:[0-9a-z._]+]] = load i32, ptr %[[SHOULD_DELETE_VAR]] // DTORS: call x86_thiscallcc void @"??1C@basic@@UAE@XZ"(ptr {{[^,]*}} %[[THIS:[0-9a-z]+]]) -// DTORS-NEXT: %[[CONDITION:[0-9]+]] = icmp eq i32 %[[SHOULD_DELETE_VALUE]], 0 +// DTORS-NEXT: %[[AND:[0-9]+]] = and i32 %[[SHOULD_DELETE_VALUE]], 1 +// DTORS-NEXT: %[[CONDITION:[0-9]+]] = icmp eq i32 %[[AND]], 0 // DTORS-NEXT: br i1 %[[CONDITION]], label %[[CONTINUE_LABEL:[0-9a-z._]+]], label %[[CALL_DELETE_LABEL:[0-9a-z._]+]] // // DTORS: [[CALL_DELETE_LABEL]] @@ -113,8 +114,7 @@ void call_deleting_dtor_and_global_delete(C *obj_ptr) { // CHECK-NEXT: %[[VTABLE:.*]] = load ptr, ptr %[[OBJ_PTR_VALUE]] // CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds ptr, ptr %[[VTABLE]], i64 0 // CHECK-NEXT: %[[VDTOR:.*]] = load ptr, ptr %[[PVDTOR]] -// CHECK-NEXT: %[[CALL:.*]] = call x86_thiscallcc ptr %[[VDTOR]](ptr {{[^,]*}} %[[OBJ_PTR_VALUE]], i32 0) -// CHECK-NEXT: call void @"??3@YAXPAX@Z"(ptr %[[CALL]]) +// CHECK-NEXT: %[[CALL:.*]] = call x86_thiscallcc ptr %[[VDTOR]](ptr {{[^,]*}} %[[OBJ_PTR_VALUE]], i32 5) // CHECK: ret void } @@ -458,3 +458,41 @@ class G { extern void testG() { G g; } + +namespace operator_delete { + +class H { virtual ~H(); + void operator delete(void *); +}; +H::~H() { } + +void checkH() { + new H(); +} +// DTORS: define linkonce_odr dso_local x86_thiscallcc ptr @"??_GH@operator_delete@@EAEPAXI@Z"(ptr {{[^,]*}} %this, i32 %should_call_delete) {{.*}} comdat {{.*}} { +// DTORS: store i32 %should_call_delete, ptr %[[SHOULD_DELETE_VAR:[0-9a-z._]+]], align 4 +// DTORS: store ptr %{{.*}}, ptr %[[RETVAL:retval]] +// DTORS: %[[SHOULD_DELETE_VALUE:[0-9a-z._]+]] = load i32, ptr %[[SHOULD_DELETE_VAR]] +// DTORS: call x86_thiscallcc void @"??1H@operator_delete@@EAE@XZ"(ptr {{[^,]*}} %[[THIS:[0-9a-z]+]]) +// DTORS-NEXT: %[[AND:[0-9]+]] = and i32 %[[SHOULD_DELETE_VALUE]], 1 +// DTORS-NEXT: %[[CONDITION:[0-9]+]] = icmp eq i32 %[[AND]], 0 +// DTORS-NEXT: br i1 %[[CONDITION]], label %[[CONTINUE_LABEL:[0-9a-z._]+]], label %[[CALL_DELETE_LABEL:[0-9a-z._]+]] +// +// DTORS: [[CALL_DELETE_LABEL]] +// DTORS-NEXT: %[[AND:[0-9]+]] = and i32 %[[SHOULD_DELETE_VALUE]], 4 +// DTORS-NEXT: %[[CONDITION1:[0-9]+]] = icmp eq i32 %[[AND]], 0 +// DTORS-NEXT: br i1 %[[CONDITION1]], label %[[CALL_CLASS_DELETE:[0-9a-z._]+]], label %[[CALL_GLOB_DELETE:[0-9a-z._]+]] +// +// DTORS: [[CALL_GLOB_DELETE]] +// DTORS-NEXT: call void @"??3@YAXPAX@Z"(ptr %[[THIS]]) +// DTORS-NEXT: br label %[[CONTINUE_LABEL]] +// +// DTORS: [[CALL_CLASS_DELETE]] +// DTORS-NEXT: call void @"??3H@operator_delete@@CAXPAX@Z"(ptr %[[THIS]]) +// DTORS-NEXT: br label %[[CONTINUE_LABEL]] +// +// DTORS: [[CONTINUE_LABEL]] +// DTORS-NEXT: %[[RET:.*]] = load ptr, ptr %[[RETVAL]] +// DTORS-NEXT: ret ptr %[[RET]] + +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits