diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp index 55c27107e709d..2353f661f83b1 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp @@ -86,6 +86,19 @@ Address CIRGenFunction::createCleanupActiveFlag() { return active; } +void CIRGenFunction::initFullExprCleanup() { + initFullExprCleanupWithFlag(createCleanupActiveFlag()); +} + +void CIRGenFunction::initFullExprCleanupWithFlag(Address activeFlag) { + EHCleanupScope &cleanup = cast(*ehStack.begin()); + assert(!cleanup.hasActiveFlag() && "cleanup already has active flag?"); + cleanup.setActiveFlag(activeFlag); + + cleanup.setTestFlagInNormalCleanup(cleanup.isNormalCleanup()); + cleanup.setTestFlagInEHCleanup(cleanup.isEHCleanup()); +} + CIRGenFunction::FullExprCleanupScope::FullExprCleanupScope(CIRGenFunction &cgf, const Expr *subExpr) : cgf(cgf), cleanups(cgf), scope(nullptr), @@ -361,11 +374,8 @@ static void setupCleanupBlockDeactivation(CIRGenFunction &cgf, assert((scope.isNormalCleanup() || scope.isEHCleanup()) && "cleanup block is neither normal nor EH?"); - if (scope.isNormalCleanup()) - scope.setTestFlagInNormalCleanup(); - - if (scope.isEHCleanup()) - scope.setTestFlagInEHCleanup(); + scope.setTestFlagInNormalCleanup(scope.isNormalCleanup()); + scope.setTestFlagInEHCleanup(scope.isEHCleanup()); CIRGenBuilderTy &builder = cgf.getBuilder(); @@ -528,7 +538,7 @@ void CIRGenFunction::popCleanupBlock(bool forDeactivation) { builder.createFlagStore(loc, false, activeFlag.getPointer()); scope.setActiveFlag(activeFlag); - scope.setTestFlagInNormalCleanup(); + scope.setTestFlagInNormalCleanup(true); } else { // If the cleanup was pushed on the stack as normal+eh, downgrade it to // eh-only. diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.h b/clang/lib/CIR/CodeGen/CIRGenCleanup.h index fc1e41919b3d2..bae04a2452006 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCleanup.h +++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.h @@ -162,14 +162,16 @@ class alignas(EHScopeStack::ScopeStackAlignment) EHCleanupScope Address getActiveFlag() const { return activeFlag; } void setActiveFlag(Address var) { activeFlag = var; } - void setTestFlagInNormalCleanup() { - cleanupBits.testFlagInNormalCleanup = true; + void setTestFlagInNormalCleanup(bool value) { + cleanupBits.testFlagInNormalCleanup = value; } bool shouldTestFlagInNormalCleanup() const { return cleanupBits.testFlagInNormalCleanup; } - void setTestFlagInEHCleanup() { cleanupBits.testFlagInEHCleanup = true; } + void setTestFlagInEHCleanup(bool value) { + cleanupBits.testFlagInEHCleanup = value; + } bool shouldTestFlagInEHCleanup() const { return cleanupBits.testFlagInEHCleanup; } diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index be86d34545a2c..66f6f7f11e76f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -1141,10 +1141,8 @@ void CIRGenFunction::pushPendingCleanupToEHStack( if (entry.activeFlag.isValid()) { EHCleanupScope &scope = cast(*ehStack.begin()); scope.setActiveFlag(entry.activeFlag); - if (scope.isNormalCleanup()) - scope.setTestFlagInNormalCleanup(); - if (scope.isEHCleanup()) - scope.setTestFlagInEHCleanup(); + scope.setTestFlagInNormalCleanup(scope.isNormalCleanup()); + scope.setTestFlagInEHCleanup(scope.isEHCleanup()); } } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp index ac145b41b3901..b5e97a3628ef6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp @@ -20,6 +20,7 @@ #include "clang/AST/ExprObjC.h" #include "clang/Basic/OperatorKinds.h" #include "clang/CIR/MissingFeatures.h" +#include "llvm/ADT/Sequence.h" #include "llvm/Support/TrailingObjects.h" using namespace clang; @@ -802,12 +803,12 @@ class CallDeleteDuringNew final PlacementArg *getPlacementArgs() { return getTrailingObjects(); } +public: void setPlacementArg(unsigned i, RValueTy argValue, QualType argType) { assert(i < numPlacementArgs && "index out of range"); getPlacementArgs()[i] = {argValue, argType}; } -public: static size_t getExtraSize(size_t numPlacementArgs) { return TrailingObj::template additionalSizeToAlloc>( numPlacementArgs); @@ -817,18 +818,11 @@ class CallDeleteDuringNew final const FunctionDecl *operatorDelete, ValueTy ptr, ValueTy allocSize, const ImplicitAllocationParameters &iap, - CharUnits allocAlign, const CallArgList *newArgs, - unsigned numNonPlacementArgs, CIRGenFunction *cgf, - mlir::Location loc) + CharUnits allocAlign) : numPlacementArgs(numPlacementArgs), passAlignmentToPlacementDelete(isAlignedAllocation(iap.PassAlignment)), operatorDelete(operatorDelete), ptr(ptr), allocSize(allocSize), - allocAlign(allocAlign) { - for (unsigned i = 0, n = numPlacementArgs; i != n; ++i) { - const CallArg &arg = (*newArgs)[i + numNonPlacementArgs]; - setPlacementArg(i, arg.getRValue(*cgf, loc), arg.ty); - } - } + allocAlign(allocAlign) {} void emit(CIRGenFunction &cgf, Flags flags) override { const auto *fpt = operatorDelete->getType()->castAs(); @@ -905,17 +899,60 @@ static void enterNewDeleteCleanup(CIRGenFunction &cgf, const CXXNewExpr *e, typedef CallDeleteDuringNew DirectCleanup; assert(!cir::MissingFeatures::typeAwareAllocation()); - cgf.ehStack.pushCleanupWithExtra( + DirectCleanup *cleanup = cgf.ehStack.pushCleanupWithExtra( EHCleanup, e->getNumPlacementArgs(), e->getOperatorDelete(), newPtr.getPointer(), allocSize, e->implicitAllocationParameters(), - allocAlign, &newArgs, numNonPlacementArgs, &cgf, - cgf.getLoc(e->getSourceRange())); + allocAlign); + for (auto i : llvm::seq(0, e->getNumPlacementArgs())) { + const CallArg &arg = newArgs[i + numNonPlacementArgs]; + cleanup->setPlacementArg( + i, arg.getRValue(cgf, cgf.getLoc(e->getSourceRange())), arg.ty); + } return; } - cgf.cgm.errorNYI(e->getSourceRange(), - "enterNewDeleteCleanup: conditional branch"); + // Otherwise, we need to save all this stuff. + auto saveValue = [&](mlir::Value value) -> mlir::Value { + CharUnits align = CharUnits::fromQuantity( + cgf.cgm.getDataLayout().getABITypeAlign(value.getType())); + Address alloca = cgf.createTempAlloca(value.getType(), align, + value.getLoc(), "cond-cleanup.save"); + cgf.getBuilder().createStore(value.getLoc(), value, alloca); + return alloca.emitRawPointer(); + }; + + mlir::Value savedNewPtr = saveValue(newPtr.getPointer()); + mlir::Value savedAllocSize = saveValue(allocSize); + + struct ConditionalCleanupTraits { + typedef mlir::Value ValueTy; + typedef mlir::Value RValueTy; + static RValue get(CIRGenFunction &cgf, ValueTy v) { + auto alloca = v.getDefiningOp(); + return RValue::get(cgf.getBuilder().createAlignedLoad( + alloca.getLoc(), alloca.getAllocaType(), alloca, + llvm::MaybeAlign(alloca.getAlignment()))); + } + }; + typedef CallDeleteDuringNew ConditionalCleanup; + + assert(!cir::MissingFeatures::typeAwareAllocation()); + ConditionalCleanup *cleanup = + cgf.ehStack.pushCleanupWithExtra( + EHCleanup, e->getNumPlacementArgs(), e->getOperatorDelete(), + savedNewPtr, savedAllocSize, e->implicitAllocationParameters(), + allocAlign); + for (auto i : llvm::seq(0, e->getNumPlacementArgs())) { + const CallArg &arg = newArgs[i + numNonPlacementArgs]; + cleanup->setPlacementArg( + i, + saveValue( + arg.getRValue(cgf, cgf.getLoc(e->getSourceRange())).getValue()), + arg.ty); + } + + cgf.initFullExprCleanup(); } static void storeAnyExprIntoOneUnit(CIRGenFunction &cgf, const Expr *init, diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 7cbea29a7c3f9..c798833d877ea 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -1094,6 +1094,11 @@ class CIRGenFunction : public CIRGenTypeCache { /// true at the current insertion point (inside the conditional branch). Address createCleanupActiveFlag(); + /// Set up the last cleanup that was pushed as a conditional + /// full-expression cleanup. + void initFullExprCleanup(); + void initFullExprCleanupWithFlag(Address activeFlag); + /// Promote a single pending cleanup entry onto the EH scope stack. If the /// entry has a valid activeFlag, the cleanup is configured as conditional. /// Defined in CIRGenDecl.cpp where the concrete cleanup types are visible. diff --git a/clang/test/CIR/CodeGen/new-delete.cpp b/clang/test/CIR/CodeGen/new-delete.cpp index d0c8c7d851c70..6902d37b25e56 100644 --- a/clang/test/CIR/CodeGen/new-delete.cpp +++ b/clang/test/CIR/CodeGen/new-delete.cpp @@ -247,6 +247,332 @@ B *c() { // OGCG: %[[EXN_INSERT_2:.*]] = insertvalue { ptr, i32 } %[[EXN_INSERT]], i32 %[[EHSELECTOR]], 1 // OGCG: resume { ptr, i32 } %[[EXN_INSERT_2]] +struct C { + C(); + ~C(); +}; + +C *test_new_delete_conditional(bool cond) { + return cond ? new C : 0; +} + +// CIR-LABEL: @_Z27test_new_delete_conditionalb +// CIR: %[[CLEANUP_COND:.*]] = cir.alloca !cir.bool, !cir.ptr, ["cleanup.cond"] +// CIR: %[[FALSE:.*]] = cir.const #false +// CIR: cir.store %[[FALSE]], %[[CLEANUP_COND]] +// CIR: %[[TERN_RESULT:.*]] = cir.ternary +// CIR: %[[PTR_SAVE:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["cond-cleanup.save"] +// CIR: %[[SIZE_SAVE:.*]] = cir.alloca !u64i, !cir.ptr, ["cond-cleanup.save"] +// CIR: %[[NEW_RESULT:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["__new_result"] +// CIR: %[[ALLOC_SIZE:.*]] = cir.const #cir.int<1> : !u64i +// CIR: %[[NEW_PTR:.*]] = cir.call @_Znwm(%[[ALLOC_SIZE]]) +// CIR: cir.store {{.*}}%[[NEW_PTR]], %[[PTR_SAVE]] +// CIR: cir.store {{.*}}%[[ALLOC_SIZE]], %[[SIZE_SAVE]] +// CIR: cir.cleanup.scope { +// CIR: %[[TRUE:.*]] = cir.const #true +// CIR: cir.store %[[TRUE]], %[[CLEANUP_COND]] +// CIR: %[[NEW_AS_C:.*]] = cir.cast bitcast %[[NEW_PTR]] : !cir.ptr -> !cir.ptr +// CIR: cir.store{{.*}} %[[NEW_AS_C]], %[[NEW_RESULT]] +// CIR: cir.call @_ZN1CC1Ev(%[[NEW_AS_C]]) +// CIR: cir.yield +// CIR: } cleanup eh { +// CIR: %[[FLAG:.*]] = cir.load {{.*}} %[[CLEANUP_COND]] +// CIR: cir.if %[[FLAG]] { +// CIR: %[[RESTORED_PTR:.*]] = cir.load {{.*}} %[[PTR_SAVE]] +// CIR: cir.call @_ZdlPv(%[[RESTORED_PTR]]) +// CIR: } +// CIR: cir.yield +// CIR: } +// CIR: %[[TRUE_RESULT:.*]] = cir.load{{.*}} %[[NEW_RESULT]] +// CIR: cir.yield %[[TRUE_RESULT]] : !cir.ptr +// CIR: }, false { +// CIR: %[[FALSE_RESULT:.*]] = cir.const #cir.ptr : !cir.ptr +// CIR: cir.yield %[[FALSE_RESULT]] : !cir.ptr +// CIR: }) : (!cir.bool) -> !cir.ptr + +// LLVM-LABEL: @_Z27test_new_delete_conditionalb +// LLVM: store i8 0, ptr %[[CLEANUP_FLAG:.*]], align 1 +// LLVM: br i1 {{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: %[[NEWP:.*]] = call ptr @_Znwm(i64 1) #[[ATTR_BUILTIN_NEW]] +// LLVM: store ptr %[[NEWP]], ptr %[[SAVE_PTR:.*]], align 8 +// LLVM: store i8 1, ptr %[[CLEANUP_FLAG]], align 1 +// LLVM: invoke void @_ZN1CC1Ev(ptr {{.*}}%[[NEWP]]) +// LLVM: to label %{{.*}} unwind label %[[LPAD:.*]] +// LLVM: [[LPAD]]: +// LLVM: landingpad { ptr, i32 } +// LLVM-NEXT: cleanup +// LLVM: %[[IS_ACTIVE:.*]] = trunc i8 %{{.*}} to i1 +// LLVM: br i1 %[[IS_ACTIVE]], label %[[DO_DEL:.*]], label %[[SKIP_DEL:.*]] +// LLVM: [[DO_DEL]]: +// LLVM: %[[LOAD_PTR:.*]] = load ptr, ptr %[[SAVE_PTR]], align 8 +// LLVM: call void @_ZdlPv(ptr %[[LOAD_PTR]]) #[[ATTR_BUILTIN_DEL]] +// LLVM: resume + +// OGCG-LABEL: @_Z27test_new_delete_conditionalb +// OGCG: store i1 false, ptr %[[OG_FLAG:.*]], align 1 +// OGCG: br i1 {{.*}}, label %[[OG_TRUE:.*]], label %[[OG_FALSE:.*]] +// OGCG: [[OG_TRUE]]: +// OGCG: %[[OG_NEWP:.*]] = call noalias nonnull ptr @_Znwm(i64 1) #[[OGCG_ATTR_BUILTIN_NEW]] +// OGCG: store ptr %[[OG_NEWP]], ptr %[[OG_SAVE:.*]], align 8 +// OGCG: store i1 true, ptr %[[OG_FLAG]], align 1 +// OGCG: invoke void @_ZN1CC1Ev(ptr {{.*}}%[[OG_NEWP]]) +// OGCG: to label %{{.*}} unwind label %[[OG_LPAD:.*]] +// OGCG: [[OG_LPAD]]: +// OGCG: landingpad { ptr, i32 } +// OGCG-NEXT: cleanup +// OGCG: %[[OG_ACTIVE:.*]] = load i1, ptr %[[OG_FLAG]], align 1 +// OGCG: br i1 %[[OG_ACTIVE]], label %[[OG_DO_DEL:.*]], label %[[OG_SKIP_DEL:.*]] +// OGCG: [[OG_DO_DEL]]: +// OGCG: %[[OG_LOAD:.*]] = load ptr, ptr %[[OG_SAVE]], align 8 +// OGCG: call void @_ZdlPv(ptr %[[OG_LOAD]]) #[[OGCG_ATTR_BUILTIN_DEL]] +// OGCG: resume + +void *operator new(unsigned long, int); +void operator delete(void *, int); + +C *test_new_delete_conditional_with_placement(bool cond, int tag) { + return cond ? new (tag) C : 0; +} + +// CIR-LABEL: @_Z42test_new_delete_conditional_with_placementbi +// CIR: %[[CLEANUP_COND:.*]] = cir.alloca !cir.bool, !cir.ptr, ["cleanup.cond"] +// CIR: %[[TERN_RESULT:.*]] = cir.ternary +// CIR: %[[PTR_SAVE:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["cond-cleanup.save"] +// CIR: %[[SIZE_SAVE:.*]] = cir.alloca !u64i, !cir.ptr, ["cond-cleanup.save"] +// CIR: %[[TAG_SAVE:.*]] = cir.alloca !s32i, !cir.ptr, ["cond-cleanup.save"] +// CIR: %[[NEW_RESULT:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["__new_result"] +// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !u64i +// CIR: %[[TAG_VAL:.*]] = cir.load{{.*}} +// CIR: %[[NEW_PTR:.*]] = cir.call @_Znwmi(%[[ONE]], %[[TAG_VAL]]) +// CIR: cir.store{{.*}} %[[NEW_PTR]], %[[PTR_SAVE]] +// CIR: cir.store{{.*}} %[[ONE]], %[[SIZE_SAVE]] +// CIR: cir.cleanup.scope { +// CIR: cir.store{{.*}} %[[TAG_VAL]], %[[TAG_SAVE]] +// CIR: %[[TRUE:.*]] = cir.const #true +// CIR: cir.store %[[TRUE]], %[[CLEANUP_COND]] +// CIR: %[[NEW_AS_C:.*]] = cir.cast bitcast %[[NEW_PTR]] : !cir.ptr -> !cir.ptr +// CIR: cir.store{{.*}} %[[NEW_AS_C]], %[[NEW_RESULT]] +// CIR: cir.call @_ZN1CC1Ev(%[[NEW_AS_C]]) +// CIR: } cleanup eh { +// CIR: %[[FLAG:.*]] = cir.load{{.*}} %[[CLEANUP_COND]] +// CIR: cir.if %[[FLAG]] { +// CIR: %[[RESTORED_PTR:.*]] = cir.load {{.*}} %[[PTR_SAVE]] +// CIR: %[[RESTORED_TAG:.*]] = cir.load {{.*}} %[[TAG_SAVE]] +// CIR: cir.call @_ZdlPvi(%[[RESTORED_PTR]], %[[RESTORED_TAG]]) +// CIR: } +// CIR: cir.yield +// CIR: } +// CIR: %[[TRUE_RESULT:.*]] = cir.load{{.*}} %[[NEW_RESULT]] +// CIR: cir.yield %[[TRUE_RESULT]] : !cir.ptr +// CIR: }, false { +// CIR: %[[FALSE_RESULT:.*]] = cir.const #cir.ptr : !cir.ptr +// CIR: cir.yield %[[FALSE_RESULT]] : !cir.ptr +// CIR: }) : (!cir.bool) -> !cir.ptr + +// LLVM-LABEL: @_Z42test_new_delete_conditional_with_placementbi +// LLVM: store i8 0, ptr %[[PL_FLAG:.*]], align 1 +// LLVM: br i1 {{.*}}, label %[[PL_TRUE:.*]], label %[[PL_FALSE:.*]] +// LLVM: [[PL_TRUE]]: +// LLVM: %[[PL_TAG:.*]] = load i32, ptr %{{.*}}, align 4 +// LLVM: %[[PL_NEWP:.*]] = call ptr @_Znwmi(i64 1, i32 %[[PL_TAG]]) +// LLVM: store ptr %[[PL_NEWP]], ptr %[[PL_SAVE_PTR:.*]], align 8 +// LLVM: store i32 %[[PL_TAG]], ptr %[[PL_SAVE_TAG:.*]], align 4 +// LLVM: store i8 1, ptr %[[PL_FLAG]], align 1 +// LLVM: invoke void @_ZN1CC1Ev(ptr {{.*}}%[[PL_NEWP]]) +// LLVM: to label %{{.*}} unwind label %[[PL_LPAD:.*]] +// LLVM: [[PL_LPAD]]: +// LLVM: landingpad { ptr, i32 } +// LLVM-NEXT: cleanup +// LLVM: %[[PL_ACTIVE:.*]] = trunc i8 %{{.*}} to i1 +// LLVM: br i1 %[[PL_ACTIVE]], label %[[PL_DO_DEL:.*]], label %[[PL_SKIP_DEL:.*]] +// LLVM: [[PL_DO_DEL]]: +// LLVM: %[[PL_LOAD_PTR:.*]] = load ptr, ptr %[[PL_SAVE_PTR]], align 8 +// LLVM: %[[PL_LOAD_TAG:.*]] = load i32, ptr %[[PL_SAVE_TAG]], align 4 +// LLVM: invoke void @_ZdlPvi(ptr %[[PL_LOAD_PTR]], i32 %[[PL_LOAD_TAG]]) + +// OGCG-LABEL: @_Z42test_new_delete_conditional_with_placementbi +// OGCG: store i1 false, ptr %[[OGP_FLAG:.*]], align 1 +// OGCG: br i1 {{.*}}, label %[[OGP_TRUE:.*]], label %[[OGP_FALSE:.*]] +// OGCG: [[OGP_TRUE]]: +// OGCG: %[[OGP_TAG:.*]] = load i32, ptr %{{.*}}, align 4 +// OGCG: %[[OGP_NEWP:.*]] = call ptr @_Znwmi(i64 1, i32 %[[OGP_TAG]]) +// OGCG: store ptr %[[OGP_NEWP]], ptr %[[OGP_SAVE_PTR:.*]], align 8 +// OGCG: store i32 %[[OGP_TAG]], ptr %[[OGP_SAVE_TAG:.*]], align 4 +// OGCG: store i1 true, ptr %[[OGP_FLAG]], align 1 +// OGCG: invoke void @_ZN1CC1Ev(ptr {{.*}}%[[OGP_NEWP]]) +// OGCG: to label %{{.*}} unwind label %[[OGP_LPAD:.*]] +// OGCG: [[OGP_LPAD]]: +// OGCG: landingpad { ptr, i32 } +// OGCG-NEXT: cleanup +// OGCG: %[[OGP_ACTIVE:.*]] = load i1, ptr %[[OGP_FLAG]], align 1 +// OGCG: br i1 %[[OGP_ACTIVE]], label %[[OGP_DO_DEL:.*]], label %[[OGP_SKIP_DEL:.*]] +// OGCG: [[OGP_DO_DEL]]: +// OGCG: %[[OGP_LOAD_PTR:.*]] = load ptr, ptr %[[OGP_SAVE_PTR]], align 8 +// OGCG: %[[OGP_LOAD_TAG:.*]] = load i32, ptr %[[OGP_SAVE_TAG]], align 4 +// OGCG: invoke void @_ZdlPvi(ptr %[[OGP_LOAD_PTR]], i32 %[[OGP_LOAD_TAG]]) + +struct D { + D(); + static void operator delete(void *, unsigned long); + static void operator delete[](void *, unsigned long); +}; + +D *test_new_delete_conditional_with_size(bool cond) { + return cond ? new D : 0; +} + +// CIR-LABEL: @_Z37test_new_delete_conditional_with_sizeb +// CIR: %[[CLEANUP_COND:.*]] = cir.alloca !cir.bool, !cir.ptr, ["cleanup.cond"] +// CIR: %[[FALSE:.*]] = cir.const #false +// CIR: cir.store %[[FALSE]], %[[CLEANUP_COND]] +// CIR: cir.ternary +// CIR: %[[PTR_SAVE:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["cond-cleanup.save"] +// CIR: %[[SIZE_SAVE:.*]] = cir.alloca !u64i, !cir.ptr, ["cond-cleanup.save"] +// CIR: %[[NEW_RESULT:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["__new_result"] +// CIR: %[[ALLOC_SIZE:.*]] = cir.const #cir.int<1> : !u64i +// CIR: %[[NEW_PTR:.*]] = cir.call @_Znwm(%[[ALLOC_SIZE]]) +// CIR: cir.store {{.*}}%[[NEW_PTR]], %[[PTR_SAVE]] +// CIR: cir.store {{.*}}%[[ALLOC_SIZE]], %[[SIZE_SAVE]] +// CIR: cir.cleanup.scope { +// CIR: %[[TRUE:.*]] = cir.const #true +// CIR: cir.store %[[TRUE]], %[[CLEANUP_COND]] +// CIR: cir.call @_ZN1DC1Ev +// CIR: cir.yield +// CIR: } cleanup eh { +// CIR: %[[FLAG:.*]] = cir.load {{.*}} %[[CLEANUP_COND]] +// CIR: cir.if %[[FLAG]] { +// CIR: %[[RESTORED_PTR:.*]] = cir.load {{.*}} %[[PTR_SAVE]] +// CIR: %[[RESTORED_SIZE:.*]] = cir.load {{.*}} %[[SIZE_SAVE]] +// CIR: cir.call @_ZN1DdlEPvm(%[[RESTORED_PTR]], %[[RESTORED_SIZE]]) +// CIR: } +// CIR: cir.yield +// CIR: } +// CIR: %[[TRUE_RESULT:.*]] = cir.load{{.*}} %[[NEW_RESULT]] +// CIR: cir.yield %[[TRUE_RESULT]] : !cir.ptr +// CIR: }, false { +// CIR: %[[FALSE_RESULT:.*]] = cir.const #cir.ptr : !cir.ptr +// CIR: cir.yield %[[FALSE_RESULT]] : !cir.ptr +// CIR: }) : (!cir.bool) -> !cir.ptr + +// LLVM-LABEL: @_Z37test_new_delete_conditional_with_sizeb +// LLVM: store i8 0, ptr %[[SD_FLAG:.*]], align 1 +// LLVM: br i1 {{.*}}, label %[[SD_TRUE:.*]], label %[[SD_FALSE:.*]] +// LLVM: [[SD_TRUE]]: +// LLVM: %[[SD_NEWP:.*]] = call ptr @_Znwm(i64 1) +// LLVM: store ptr %[[SD_NEWP]], ptr %[[SD_SAVE_PTR:.*]], align 8 +// LLVM: store i64 1, ptr %[[SD_SAVE_SIZE:.*]], align 8 +// LLVM: store i8 1, ptr %[[SD_FLAG]], align 1 +// LLVM: invoke void @_ZN1DC1Ev(ptr {{.*}}%[[SD_NEWP]]) +// LLVM: to label %{{.*}} unwind label %[[SD_LPAD:.*]] +// LLVM: [[SD_LPAD]]: +// LLVM: landingpad { ptr, i32 } +// LLVM-NEXT: cleanup +// LLVM: %[[SD_ACTIVE:.*]] = trunc i8 %{{.*}} to i1 +// LLVM: br i1 %[[SD_ACTIVE]], label %[[SD_DO_DEL:.*]], label %[[SD_SKIP_DEL:.*]] +// LLVM: [[SD_DO_DEL]]: +// LLVM: %[[SD_LOAD_PTR:.*]] = load ptr, ptr %[[SD_SAVE_PTR]], align 8 +// LLVM: %[[SD_LOAD_SIZE:.*]] = load i64, ptr %[[SD_SAVE_SIZE]], align 8 +// LLVM: invoke void @_ZN1DdlEPvm(ptr %[[SD_LOAD_PTR]], i64 %[[SD_LOAD_SIZE]]) + +// OGCG-LABEL: @_Z37test_new_delete_conditional_with_sizeb +// OGCG: store i1 false, ptr %[[OGS_FLAG:.*]], align 1 +// OGCG: br i1 {{.*}}, label %[[OGS_TRUE:.*]], label %[[OGS_FALSE:.*]] +// OGCG: [[OGS_TRUE]]: +// OGCG: %[[OGS_NEWP:.*]] = call noalias nonnull ptr @_Znwm(i64 1) +// OGCG: store ptr %[[OGS_NEWP]], ptr %[[OGS_SAVE:.*]], align 8 +// OGCG: store i1 true, ptr %[[OGS_FLAG]], align 1 +// OGCG: invoke void @_ZN1DC1Ev(ptr {{.*}}%[[OGS_NEWP]]) +// OGCG: to label %{{.*}} unwind label %[[OGS_LPAD:.*]] +// OGCG: [[OGS_LPAD]]: +// OGCG: landingpad { ptr, i32 } +// OGCG-NEXT: cleanup +// OGCG: %[[OGS_ACTIVE:.*]] = load i1, ptr %[[OGS_FLAG]], align 1 +// OGCG: br i1 %[[OGS_ACTIVE]], label %[[OGS_DO_DEL:.*]], label %[[OGS_SKIP_DEL:.*]] +// OGCG: [[OGS_DO_DEL]]: +// OGCG: %[[OGS_LOAD_PTR:.*]] = load ptr, ptr %[[OGS_SAVE]], align 8 +// OGCG: invoke void @_ZN1DdlEPvm(ptr %[[OGS_LOAD_PTR]], i64 1) + +D *test_new_delete_conditional_array(bool cond, int n) { + return cond ? new D[n] : 0; +} + +// CIR-LABEL: @_Z33test_new_delete_conditional_arraybi +// CIR: %[[CLEANUP_COND:.*]] = cir.alloca !cir.bool, !cir.ptr, ["cleanup.cond"] +// CIR: %[[FALSE:.*]] = cir.const #false +// CIR: cir.store %[[FALSE]], %[[CLEANUP_COND]] +// CIR: cir.ternary +// CIR: %[[PTR_SAVE:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["cond-cleanup.save"] +// CIR: %[[SIZE_SAVE:.*]] = cir.alloca !u64i, !cir.ptr, ["cond-cleanup.save"] +// CIR: %[[NEW_RESULT:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["__new_result"] +// CIR: %[[N:.*]] = cir.load {{.*}} : !cir.ptr, !s32i +// CIR: %[[N_EXT:.*]] = cir.cast integral %[[N]] : !s32i -> !s64i +// CIR: %result, %overflow = cir.add.overflow %{{.*}}, %{{.*}} : !u64i -> !u64i +// CIR: %[[ALLOC_SIZE:.*]] = cir.select +// CIR: %[[NEW_PTR:.*]] = cir.call @_Znam(%[[ALLOC_SIZE]]) +// CIR: cir.store {{.*}}%[[NEW_PTR]], %[[PTR_SAVE]] +// CIR: cir.store {{.*}}%[[ALLOC_SIZE]], %[[SIZE_SAVE]] +// CIR: cir.cleanup.scope { +// CIR: %[[TRUE:.*]] = cir.const #true +// CIR: cir.store %[[TRUE]], %[[CLEANUP_COND]] +// CIR: cir.call @_ZN1DC1Ev +// CIR: cir.yield +// CIR: } cleanup eh { +// CIR: %[[FLAG:.*]] = cir.load {{.*}} %[[CLEANUP_COND]] +// CIR: cir.if %[[FLAG]] { +// CIR: %[[RESTORED_PTR:.*]] = cir.load {{.*}} %[[PTR_SAVE]] +// CIR: %[[RESTORED_SIZE:.*]] = cir.load {{.*}} %[[SIZE_SAVE]] +// CIR: cir.call @_ZN1DdaEPvm(%[[RESTORED_PTR]], %[[RESTORED_SIZE]]) +// CIR: } +// CIR: cir.yield +// CIR: } +// CIR: %[[TRUE_RESULT:.*]] = cir.load{{.*}} %[[NEW_RESULT]] +// CIR: cir.yield %[[TRUE_RESULT]] : !cir.ptr +// CIR: }, false { +// CIR: %[[FALSE_RESULT:.*]] = cir.const #cir.ptr : !cir.ptr +// CIR: cir.yield %[[FALSE_RESULT]] : !cir.ptr +// CIR: }) : (!cir.bool) -> !cir.ptr + +// LLVM-LABEL: @_Z33test_new_delete_conditional_arraybi +// LLVM: store i8 0, ptr %[[ARR_FLAG:.*]], align 1 +// LLVM: br i1 {{.*}}, label %[[ARR_TRUE:.*]], label %[[ARR_FALSE:.*]] +// LLVM: [[ARR_TRUE]]: +// LLVM: %[[ARR_ALLOC_SIZE:.*]] = select i1 %{{.*}}, i64 -1, i64 %{{.*}} +// LLVM: %[[ARR_NEWP:.*]] = call ptr @_Znam(i64 %[[ARR_ALLOC_SIZE]]) +// LLVM: store ptr %[[ARR_NEWP]], ptr %[[ARR_SAVE_PTR:.*]], align 8 +// LLVM: store i64 %[[ARR_ALLOC_SIZE]], ptr %[[ARR_SAVE_SIZE:.*]], align 8 +// LLVM: store i8 1, ptr %[[ARR_FLAG]], align 1 +// LLVM: invoke void @_ZN1DC1Ev +// LLVM: [[ARR_LPAD:.*]]: +// LLVM: landingpad { ptr, i32 } +// LLVM-NEXT: cleanup +// LLVM: %[[ARR_ACTIVE:.*]] = trunc i8 %{{.*}} to i1 +// LLVM: br i1 %[[ARR_ACTIVE]], label %[[ARR_DO_DEL:.*]], label %[[ARR_SKIP_DEL:.*]] +// LLVM: [[ARR_DO_DEL]]: +// LLVM: %[[ARR_LOAD_PTR:.*]] = load ptr, ptr %[[ARR_SAVE_PTR]], align 8 +// LLVM: %[[ARR_LOAD_SIZE:.*]] = load i64, ptr %[[ARR_SAVE_SIZE]], align 8 +// LLVM: invoke void @_ZN1DdaEPvm(ptr %[[ARR_LOAD_PTR]], i64 %[[ARR_LOAD_SIZE]]) + +// OGCG-LABEL: @_Z33test_new_delete_conditional_arraybi +// OGCG: store i1 false, ptr %[[OGA_FLAG:.*]], align 1 +// OGCG: br i1 {{.*}}, label %[[OGA_TRUE:.*]], label %[[OGA_FALSE:.*]] +// OGCG: [[OGA_TRUE]]: +// OGCG: %[[OGA_ALLOC_SIZE:.*]] = select i1 %{{.*}}, i64 -1, i64 %{{.*}} +// OGCG: %[[OGA_NEWP:.*]] = call noalias nonnull ptr @_Znam(i64 %[[OGA_ALLOC_SIZE]]) +// OGCG: store ptr %[[OGA_NEWP]], ptr %[[OGA_SAVE_PTR:.*]], align 8 +// OGCG: store i64 %[[OGA_ALLOC_SIZE]], ptr %[[OGA_SAVE_SIZE:.*]], align 8 +// OGCG: store i1 true, ptr %[[OGA_FLAG]], align 1 +// OGCG: invoke void @_ZN1DC1Ev +// OGCG: [[OGA_LPAD:.*]]: +// OGCG: landingpad { ptr, i32 } +// OGCG-NEXT: cleanup +// OGCG: %[[OGA_ACTIVE:.*]] = load i1, ptr %[[OGA_FLAG]], align 1 +// OGCG: br i1 %[[OGA_ACTIVE]], label %[[OGA_DO_DEL:.*]], label %[[OGA_SKIP_DEL:.*]] +// OGCG: [[OGA_DO_DEL]]: +// OGCG: %[[OGA_LOAD_PTR:.*]] = load ptr, ptr %[[OGA_SAVE_PTR]], align 8 +// OGCG: %[[OGA_LOAD_SIZE:.*]] = load i64, ptr %[[OGA_SAVE_SIZE]], align 8 +// OGCG: invoke void @_ZN1DdaEPvm(ptr %[[OGA_LOAD_PTR]], i64 %[[OGA_LOAD_SIZE]]) + // LLVM-DAG: attributes #[[ATTR_BUILTIN_NEW]] = {{{.*}}builtin{{.*}}} // LLVM-DAG: attributes #[[ATTR_BUILTIN_DEL]] = {{{.*}}builtin{{.*}}} // OGCG-DAG: attributes #[[OGCG_ATTR_BUILTIN_NEW]] = {{{.*}}builtin{{.*}}}