-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[CIR] Fix destructor calls with temporary objects #161922
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
This fixes a few problems where destructors were not called for temporary objects and, after calling was enabled, they were placed incorrectly relative to cir.yield operations.
@llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) ChangesThis fixes a few problems where destructors were not called for temporary objects and, after calling was enabled, they were placed incorrectly relative to cir.yield operations. Full diff: https://github.com/llvm/llvm-project/pull/161922.diff 7 Files Affected:
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 9d12a13dd79c0..8f4377b435775 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -690,7 +690,7 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
// every temporary created in a default argument expression is sequenced
// before the construction of the next array element, if any.
{
- assert(!cir::MissingFeatures::runCleanupsScope());
+ RunCleanupsScope scope(*this);
// Evaluate the constructor and its arguments in a regular
// partial-destroy cleanup.
diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
index 4d4d10be40024..870069715df22 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
@@ -28,6 +28,12 @@ using namespace clang::CIRGen;
// CIRGenFunction cleanup related
//===----------------------------------------------------------------------===//
+/// Emits all the code to cause the given temporary to be cleaned up.
+void CIRGenFunction::emitCXXTemporary(const CXXTemporary *temporary,
+ QualType tempType, Address ptr) {
+ pushDestroy(NormalAndEHCleanup, ptr, tempType, destroyCXXObject);
+}
+
//===----------------------------------------------------------------------===//
// EHScopeStack
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index 1e987f3bedc7e..828f86966f0cd 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -46,6 +46,12 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
return dest;
}
+ void ensureDest(mlir::Location loc, QualType ty) {
+ if (!dest.isIgnored())
+ return;
+ dest = cgf.createAggTemp(ty, loc, "agg.tmp.ensured");
+ }
+
public:
AggExprEmitter(CIRGenFunction &cgf, AggValueSlot dest)
: cgf(cgf), dest(dest) {}
@@ -96,10 +102,22 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
Visit(die->getExpr());
}
void VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *e) {
- assert(!cir::MissingFeatures::aggValueSlotDestructedFlag());
+ // Ensure that we have a slot, but if we already do, remember
+ // whether it was externally destructed.
+ bool wasExternallyDestructed = dest.isExternallyDestructed();
+ ensureDest(cgf.getLoc(e->getSourceRange()), e->getType());
+
+ // We're going to push a destructor if there isn't already one.
+ dest.setExternallyDestructed();
+
Visit(e->getSubExpr());
+
+ // Push that destructor we promised.
+ if (!wasExternallyDestructed)
+ cgf.emitCXXTemporary(e->getTemporary(), e->getType(), dest.getAddress());
}
void VisitLambdaExpr(LambdaExpr *e);
+ void VisitExprWithCleanups(ExprWithCleanups *e);
// Stubs -- These should be moved up when they are implemented.
void VisitCastExpr(CastExpr *e) {
@@ -239,11 +257,6 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
cgf.cgm.errorNYI(e->getSourceRange(),
"AggExprEmitter: VisitCXXStdInitializerListExpr");
}
-
- void VisitExprWithCleanups(ExprWithCleanups *e) {
- cgf.cgm.errorNYI(e->getSourceRange(),
- "AggExprEmitter: VisitExprWithCleanups");
- }
void VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *e) {
cgf.cgm.errorNYI(e->getSourceRange(),
"AggExprEmitter: VisitCXXScalarValueInitExpr");
@@ -586,6 +599,11 @@ void AggExprEmitter::VisitLambdaExpr(LambdaExpr *e) {
}
}
+void AggExprEmitter::VisitExprWithCleanups(ExprWithCleanups *e) {
+ CIRGenFunction::RunCleanupsScope cleanups(cgf);
+ Visit(e->getSubExpr());
+}
+
void AggExprEmitter::VisitCallExpr(const CallExpr *e) {
if (e->getCallReturnType(cgf.getContext())->isReferenceType()) {
cgf.cgm.errorNYI(e->getSourceRange(), "reference return type");
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 500007f6f241b..2a24f72df6800 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1099,7 +1099,9 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
CIRGenFunction::LexicalScope lexScope{cgf, loc,
b.getInsertionBlock()};
cgf.curLexScope->setAsTernary();
- b.create<cir::YieldOp>(loc, cgf.evaluateExprAsBool(e->getRHS()));
+ mlir::Value res = cgf.evaluateExprAsBool(e->getRHS());
+ lexScope.forceCleanup();
+ b.create<cir::YieldOp>(loc, res);
},
/*falseBuilder*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
@@ -1151,7 +1153,9 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
CIRGenFunction::LexicalScope lexScope{cgf, loc,
b.getInsertionBlock()};
cgf.curLexScope->setAsTernary();
- b.create<cir::YieldOp>(loc, cgf.evaluateExprAsBool(e->getRHS()));
+ mlir::Value res = cgf.evaluateExprAsBool(e->getRHS());
+ lexScope.forceCleanup();
+ b.create<cir::YieldOp>(loc, res);
});
return maybePromoteBoolResult(resOp.getResult(), resTy);
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 3eba242550c2c..f4cefc42de197 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1240,6 +1240,9 @@ class CIRGenFunction : public CIRGenTypeCache {
RValue emitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *expr);
+ void emitCXXTemporary(const CXXTemporary *temporary, QualType tempType,
+ Address ptr);
+
void emitCXXThrowExpr(const CXXThrowExpr *e);
void emitCtorPrologue(const clang::CXXConstructorDecl *ctor,
diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h
index ea8625a0fbee5..25b6ecb503a6e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenValue.h
+++ b/clang/lib/CIR/CodeGen/CIRGenValue.h
@@ -371,6 +371,13 @@ class AggValueSlot {
mayOverlap, isZeroed);
}
+ IsDestructed_t isExternallyDestructed() const {
+ return IsDestructed_t(destructedFlag);
+ }
+ void setExternallyDestructed(bool destructed = true) {
+ destructedFlag = destructed;
+ }
+
clang::Qualifiers getQualifiers() const { return quals; }
Address getAddress() const { return addr; }
diff --git a/clang/test/CIR/CodeGen/dtors.cpp b/clang/test/CIR/CodeGen/dtors.cpp
new file mode 100644
index 0000000000000..66554b70e1700
--- /dev/null
+++ b/clang/test/CIR/CodeGen/dtors.cpp
@@ -0,0 +1,173 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -mconstructor-aliases -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -mconstructor-aliases -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -mconstructor-aliases -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
+
+struct A {
+ ~A();
+};
+
+void test_temporary_dtor() {
+ A();
+}
+
+// CIR: cir.func dso_local @_Z19test_temporary_dtorv()
+// CIR: %[[ALLOCA:.*]] = cir.alloca !rec_A, !cir.ptr<!rec_A>, ["agg.tmp0"]
+// CIR: cir.call @_ZN1AD1Ev(%[[ALLOCA]]) nothrow : (!cir.ptr<!rec_A>) -> ()
+
+// LLVM: define dso_local void @_Z19test_temporary_dtorv()
+// LLVM: %[[ALLOCA:.*]] = alloca %struct.A, i64 1, align 1
+// LLVM: call void @_ZN1AD1Ev(ptr %[[ALLOCA]])
+
+// OGCG: define dso_local void @_Z19test_temporary_dtorv()
+// OGCG: %[[ALLOCA:.*]] = alloca %struct.A, align 1
+// OGCG: call void @_ZN1AD1Ev(ptr {{.*}} %[[ALLOCA]])
+
+struct B {
+ int n;
+ B(int n) : n(n) {}
+ ~B() {}
+};
+
+bool make_temp(const B &) { return false; }
+bool test_temp_or() { return make_temp(1) || make_temp(2); }
+
+// CIR: cir.func{{.*}} @_Z12test_temp_orv()
+// CIR: %[[SCOPE:.*]] = cir.scope {
+// CIR: %[[REF_TMP0:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["ref.tmp0"]
+// CIR: %[[ONE:.*]] = cir.const #cir.int<1>
+// CIR: cir.call @_ZN1BC2Ei(%[[REF_TMP0]], %[[ONE]])
+// CIR: %[[MAKE_TEMP0:.*]] = cir.call @_Z9make_tempRK1B(%[[REF_TMP0]])
+// CIR: %[[TERNARY:.*]] = cir.ternary(%[[MAKE_TEMP0]], true {
+// CIR: %[[TRUE:.*]] = cir.const #true
+// CIR: cir.yield %[[TRUE]] : !cir.bool
+// CIR: }, false {
+// CIR: %[[REF_TMP1:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["ref.tmp1"]
+// CIR: %[[TWO:.*]] = cir.const #cir.int<2>
+// CIR: cir.call @_ZN1BC2Ei(%[[REF_TMP1]], %[[TWO]])
+// CIR: %[[MAKE_TEMP1:.*]] = cir.call @_Z9make_tempRK1B(%[[REF_TMP1]])
+// CIR: cir.call @_ZN1BD2Ev(%[[REF_TMP1]])
+// CIR: cir.yield %[[MAKE_TEMP1]] : !cir.bool
+// CIR: })
+// CIR: cir.call @_ZN1BD2Ev(%[[REF_TMP0]])
+// CIR: cir.yield %[[TERNARY]] : !cir.bool
+// CIR: } : !cir.bool
+
+// LLVM: define{{.*}} i1 @_Z12test_temp_orv() {
+// LLVM: %[[REF_TMP0:.*]] = alloca %struct.B
+// LLVM: %[[REF_TMP1:.*]] = alloca %struct.B
+// LLVM: br label %[[LOR_BEGIN:.*]]
+// LLVM: [[LOR_BEGIN]]:
+// LLVM: call void @_ZN1BC2Ei(ptr %[[REF_TMP0]], i32 1)
+// LLVM: %[[MAKE_TEMP0:.*]] = call i1 @_Z9make_tempRK1B(ptr %[[REF_TMP0]])
+// LLVM: br i1 %[[MAKE_TEMP0]], label %[[LHS_TRUE_BLOCK:.*]], label %[[LHS_FALSE_BLOCK:.*]]
+// LLVM: [[LHS_TRUE_BLOCK]]:
+// LLVM: br label %[[RESULT_BLOCK:.*]]
+// LLVM: [[LHS_FALSE_BLOCK]]:
+// LLVM: call void @_ZN1BC2Ei(ptr %[[REF_TMP1]], i32 2)
+// LLVM: %[[MAKE_TEMP1:.*]] = call i1 @_Z9make_tempRK1B(ptr %[[REF_TMP1]])
+// LLVM: call void @_ZN1BD2Ev(ptr %[[REF_TMP1]])
+// LLVM: br label %[[RESULT_BLOCK]]
+// LLVM: [[RESULT_BLOCK]]:
+// LLVM: %[[RESULT:.*]] = phi i1 [ %[[MAKE_TEMP1]], %[[LHS_FALSE_BLOCK]] ], [ true, %[[LHS_TRUE_BLOCK]] ]
+// LLVM: br label %[[LOR_END:.*]]
+// LLVM: [[LOR_END]]:
+// LLVM: call void @_ZN1BD2Ev(ptr %[[REF_TMP0]])
+
+// OGCG: define {{.*}} i1 @_Z12test_temp_orv()
+// OGCG: [[ENTRY:.*]]:
+// OGCG: %[[RETVAL:.*]] = alloca i1
+// OGCG: %[[REF_TMP0:.*]] = alloca %struct.B
+// OGCG: %[[REF_TMP1:.*]] = alloca %struct.B
+// OGCG: %[[CLEANUP_COND:.*]] = alloca i1
+// OGCG: call void @_ZN1BC2Ei(ptr {{.*}} %[[REF_TMP0]], i32 {{.*}} 1)
+// OGCG: %[[MAKE_TEMP0:.*]] = call {{.*}} i1 @_Z9make_tempRK1B(ptr {{.*}} %[[REF_TMP0]])
+// OGCG: store i1 false, ptr %cleanup.cond
+// OGCG: br i1 %[[MAKE_TEMP0]], label %[[LOR_END:.*]], label %[[LOR_RHS:.*]]
+// OGCG: [[LOR_RHS]]:
+// OGCG: call void @_ZN1BC2Ei(ptr {{.*}} %[[REF_TMP1]], i32 {{.*}} 2)
+// OGCG: store i1 true, ptr %[[CLEANUP_COND]]
+// OGCG: %[[MAKE_TEMP1:.*]] = call {{.*}} i1 @_Z9make_tempRK1B(ptr {{.*}} %[[REF_TMP1]])
+// OGCG: br label %[[LOR_END]]
+// OGCG: [[LOR_END]]:
+// OGCG: %[[PHI:.*]] = phi i1 [ true, %[[ENTRY]] ], [ %[[MAKE_TEMP1]], %[[LOR_RHS]] ]
+// OGCG: store i1 %[[PHI]], ptr %[[RETVAL]]
+// OGCG: %[[CLEANUP_IS_ACTIVE:.*]] = load i1, ptr %[[CLEANUP_COND]]
+// OGCG: br i1 %[[CLEANUP_IS_ACTIVE]], label %[[CLEANUP_ACTION:.*]], label %[[CLEANUP_DONE:.*]]
+// OGCG: [[CLEANUP_ACTION]]:
+// OGCG: call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP1]])
+// OGCG: br label %[[CLEANUP_DONE]]
+// OGCG: [[CLEANUP_DONE]]:
+// OGCG: call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP0]])
+
+bool test_temp_and() { return make_temp(1) && make_temp(2); }
+
+// CIR: cir.func{{.*}} @_Z13test_temp_andv()
+// CIR: %[[SCOPE:.*]] = cir.scope {
+// CIR: %[[REF_TMP0:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["ref.tmp0"]
+// CIR: %[[ONE:.*]] = cir.const #cir.int<1>
+// CIR: cir.call @_ZN1BC2Ei(%[[REF_TMP0]], %[[ONE]])
+// CIR: %[[MAKE_TEMP0:.*]] = cir.call @_Z9make_tempRK1B(%[[REF_TMP0]])
+// CIR: %[[TERNARY:.*]] = cir.ternary(%[[MAKE_TEMP0]], true {
+// CIR: %[[REF_TMP1:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["ref.tmp1"]
+// CIR: %[[TWO:.*]] = cir.const #cir.int<2>
+// CIR: cir.call @_ZN1BC2Ei(%[[REF_TMP1]], %[[TWO]])
+// CIR: %[[MAKE_TEMP1:.*]] = cir.call @_Z9make_tempRK1B(%[[REF_TMP1]])
+// CIR: cir.call @_ZN1BD2Ev(%[[REF_TMP1]])
+// CIR: cir.yield %[[MAKE_TEMP1]] : !cir.bool
+// CIR: }, false {
+// CIR: %[[FALSE:.*]] = cir.const #false
+// CIR: cir.yield %[[FALSE]] : !cir.bool
+// CIR: })
+// CIR: cir.call @_ZN1BD2Ev(%[[REF_TMP0]])
+// CIR: cir.yield %[[TERNARY]] : !cir.bool
+// CIR: } : !cir.bool
+
+// LLVM: define{{.*}} i1 @_Z13test_temp_andv() {
+// LLVM: %[[REF_TMP0:.*]] = alloca %struct.B
+// LLVM: %[[REF_TMP1:.*]] = alloca %struct.B
+// LLVM: br label %[[LAND_BEGIN:.*]]
+// LLVM: [[LAND_BEGIN]]:
+// LLVM: call void @_ZN1BC2Ei(ptr %[[REF_TMP0]], i32 1)
+// LLVM: %[[MAKE_TEMP0:.*]] = call i1 @_Z9make_tempRK1B(ptr %[[REF_TMP0]])
+// LLVM: br i1 %[[MAKE_TEMP0]], label %[[LHS_TRUE_BLOCK:.*]], label %[[LHS_FALSE_BLOCK:.*]]
+// LLVM: [[LHS_TRUE_BLOCK]]:
+// LLVM: call void @_ZN1BC2Ei(ptr %[[REF_TMP1]], i32 2)
+// LLVM: %[[MAKE_TEMP1:.*]] = call i1 @_Z9make_tempRK1B(ptr %[[REF_TMP1]])
+// LLVM: call void @_ZN1BD2Ev(ptr %[[REF_TMP1]])
+// LLVM: br label %[[RESULT_BLOCK:.*]]
+// LLVM: [[LHS_FALSE_BLOCK]]:
+// LLVM: br label %[[RESULT_BLOCK]]
+// LLVM: [[RESULT_BLOCK]]:
+// LLVM: %[[RESULT:.*]] = phi i1 [ false, %[[LHS_FALSE_BLOCK]] ], [ %[[MAKE_TEMP1]], %[[LHS_TRUE_BLOCK]] ]
+// LLVM: br label %[[LAND_END:.*]]
+// LLVM: [[LAND_END]]:
+// LLVM: call void @_ZN1BD2Ev(ptr %[[REF_TMP0]])
+
+// OGCG: define {{.*}} i1 @_Z13test_temp_andv()
+// OGCG: [[ENTRY:.*]]:
+// OGCG: %[[RETVAL:.*]] = alloca i1
+// OGCG: %[[REF_TMP0:.*]] = alloca %struct.B
+// OGCG: %[[REF_TMP1:.*]] = alloca %struct.B
+// OGCG: %[[CLEANUP_COND:.*]] = alloca i1
+// OGCG: call void @_ZN1BC2Ei(ptr {{.*}} %[[REF_TMP0]], i32 {{.*}} 1)
+// OGCG: %[[MAKE_TEMP0:.*]] = call {{.*}} i1 @_Z9make_tempRK1B(ptr {{.*}} %[[REF_TMP0]])
+// OGCG: store i1 false, ptr %cleanup.cond
+// OGCG: br i1 %[[MAKE_TEMP0]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]]
+// OGCG: [[LAND_RHS]]:
+// OGCG: call void @_ZN1BC2Ei(ptr {{.*}} %[[REF_TMP1]], i32 {{.*}} 2)
+// OGCG: store i1 true, ptr %[[CLEANUP_COND]]
+// OGCG: %[[MAKE_TEMP1:.*]] = call {{.*}} i1 @_Z9make_tempRK1B(ptr {{.*}} %[[REF_TMP1]])
+// OGCG: br label %[[LAND_END]]
+// OGCG: [[LAND_END]]:
+// OGCG: %[[PHI:.*]] = phi i1 [ false, %[[ENTRY]] ], [ %[[MAKE_TEMP1]], %[[LAND_RHS]] ]
+// OGCG: store i1 %[[PHI]], ptr %[[RETVAL]]
+// OGCG: %[[CLEANUP_IS_ACTIVE:.*]] = load i1, ptr %[[CLEANUP_COND]]
+// OGCG: br i1 %[[CLEANUP_IS_ACTIVE]], label %[[CLEANUP_ACTION:.*]], label %[[CLEANUP_DONE:.*]]
+// OGCG: [[CLEANUP_ACTION]]:
+// OGCG: call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP1]])
+// OGCG: br label %[[CLEANUP_DONE]]
+// OGCG: [[CLEANUP_DONE]]:
+// OGCG: call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP0]])
|
@llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) ChangesThis fixes a few problems where destructors were not called for temporary objects and, after calling was enabled, they were placed incorrectly relative to cir.yield operations. Full diff: https://github.com/llvm/llvm-project/pull/161922.diff 7 Files Affected:
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 9d12a13dd79c0..8f4377b435775 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -690,7 +690,7 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
// every temporary created in a default argument expression is sequenced
// before the construction of the next array element, if any.
{
- assert(!cir::MissingFeatures::runCleanupsScope());
+ RunCleanupsScope scope(*this);
// Evaluate the constructor and its arguments in a regular
// partial-destroy cleanup.
diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
index 4d4d10be40024..870069715df22 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
@@ -28,6 +28,12 @@ using namespace clang::CIRGen;
// CIRGenFunction cleanup related
//===----------------------------------------------------------------------===//
+/// Emits all the code to cause the given temporary to be cleaned up.
+void CIRGenFunction::emitCXXTemporary(const CXXTemporary *temporary,
+ QualType tempType, Address ptr) {
+ pushDestroy(NormalAndEHCleanup, ptr, tempType, destroyCXXObject);
+}
+
//===----------------------------------------------------------------------===//
// EHScopeStack
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index 1e987f3bedc7e..828f86966f0cd 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -46,6 +46,12 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
return dest;
}
+ void ensureDest(mlir::Location loc, QualType ty) {
+ if (!dest.isIgnored())
+ return;
+ dest = cgf.createAggTemp(ty, loc, "agg.tmp.ensured");
+ }
+
public:
AggExprEmitter(CIRGenFunction &cgf, AggValueSlot dest)
: cgf(cgf), dest(dest) {}
@@ -96,10 +102,22 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
Visit(die->getExpr());
}
void VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *e) {
- assert(!cir::MissingFeatures::aggValueSlotDestructedFlag());
+ // Ensure that we have a slot, but if we already do, remember
+ // whether it was externally destructed.
+ bool wasExternallyDestructed = dest.isExternallyDestructed();
+ ensureDest(cgf.getLoc(e->getSourceRange()), e->getType());
+
+ // We're going to push a destructor if there isn't already one.
+ dest.setExternallyDestructed();
+
Visit(e->getSubExpr());
+
+ // Push that destructor we promised.
+ if (!wasExternallyDestructed)
+ cgf.emitCXXTemporary(e->getTemporary(), e->getType(), dest.getAddress());
}
void VisitLambdaExpr(LambdaExpr *e);
+ void VisitExprWithCleanups(ExprWithCleanups *e);
// Stubs -- These should be moved up when they are implemented.
void VisitCastExpr(CastExpr *e) {
@@ -239,11 +257,6 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
cgf.cgm.errorNYI(e->getSourceRange(),
"AggExprEmitter: VisitCXXStdInitializerListExpr");
}
-
- void VisitExprWithCleanups(ExprWithCleanups *e) {
- cgf.cgm.errorNYI(e->getSourceRange(),
- "AggExprEmitter: VisitExprWithCleanups");
- }
void VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *e) {
cgf.cgm.errorNYI(e->getSourceRange(),
"AggExprEmitter: VisitCXXScalarValueInitExpr");
@@ -586,6 +599,11 @@ void AggExprEmitter::VisitLambdaExpr(LambdaExpr *e) {
}
}
+void AggExprEmitter::VisitExprWithCleanups(ExprWithCleanups *e) {
+ CIRGenFunction::RunCleanupsScope cleanups(cgf);
+ Visit(e->getSubExpr());
+}
+
void AggExprEmitter::VisitCallExpr(const CallExpr *e) {
if (e->getCallReturnType(cgf.getContext())->isReferenceType()) {
cgf.cgm.errorNYI(e->getSourceRange(), "reference return type");
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 500007f6f241b..2a24f72df6800 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1099,7 +1099,9 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
CIRGenFunction::LexicalScope lexScope{cgf, loc,
b.getInsertionBlock()};
cgf.curLexScope->setAsTernary();
- b.create<cir::YieldOp>(loc, cgf.evaluateExprAsBool(e->getRHS()));
+ mlir::Value res = cgf.evaluateExprAsBool(e->getRHS());
+ lexScope.forceCleanup();
+ b.create<cir::YieldOp>(loc, res);
},
/*falseBuilder*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
@@ -1151,7 +1153,9 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
CIRGenFunction::LexicalScope lexScope{cgf, loc,
b.getInsertionBlock()};
cgf.curLexScope->setAsTernary();
- b.create<cir::YieldOp>(loc, cgf.evaluateExprAsBool(e->getRHS()));
+ mlir::Value res = cgf.evaluateExprAsBool(e->getRHS());
+ lexScope.forceCleanup();
+ b.create<cir::YieldOp>(loc, res);
});
return maybePromoteBoolResult(resOp.getResult(), resTy);
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 3eba242550c2c..f4cefc42de197 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1240,6 +1240,9 @@ class CIRGenFunction : public CIRGenTypeCache {
RValue emitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *expr);
+ void emitCXXTemporary(const CXXTemporary *temporary, QualType tempType,
+ Address ptr);
+
void emitCXXThrowExpr(const CXXThrowExpr *e);
void emitCtorPrologue(const clang::CXXConstructorDecl *ctor,
diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h
index ea8625a0fbee5..25b6ecb503a6e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenValue.h
+++ b/clang/lib/CIR/CodeGen/CIRGenValue.h
@@ -371,6 +371,13 @@ class AggValueSlot {
mayOverlap, isZeroed);
}
+ IsDestructed_t isExternallyDestructed() const {
+ return IsDestructed_t(destructedFlag);
+ }
+ void setExternallyDestructed(bool destructed = true) {
+ destructedFlag = destructed;
+ }
+
clang::Qualifiers getQualifiers() const { return quals; }
Address getAddress() const { return addr; }
diff --git a/clang/test/CIR/CodeGen/dtors.cpp b/clang/test/CIR/CodeGen/dtors.cpp
new file mode 100644
index 0000000000000..66554b70e1700
--- /dev/null
+++ b/clang/test/CIR/CodeGen/dtors.cpp
@@ -0,0 +1,173 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -mconstructor-aliases -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -mconstructor-aliases -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -mconstructor-aliases -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
+
+struct A {
+ ~A();
+};
+
+void test_temporary_dtor() {
+ A();
+}
+
+// CIR: cir.func dso_local @_Z19test_temporary_dtorv()
+// CIR: %[[ALLOCA:.*]] = cir.alloca !rec_A, !cir.ptr<!rec_A>, ["agg.tmp0"]
+// CIR: cir.call @_ZN1AD1Ev(%[[ALLOCA]]) nothrow : (!cir.ptr<!rec_A>) -> ()
+
+// LLVM: define dso_local void @_Z19test_temporary_dtorv()
+// LLVM: %[[ALLOCA:.*]] = alloca %struct.A, i64 1, align 1
+// LLVM: call void @_ZN1AD1Ev(ptr %[[ALLOCA]])
+
+// OGCG: define dso_local void @_Z19test_temporary_dtorv()
+// OGCG: %[[ALLOCA:.*]] = alloca %struct.A, align 1
+// OGCG: call void @_ZN1AD1Ev(ptr {{.*}} %[[ALLOCA]])
+
+struct B {
+ int n;
+ B(int n) : n(n) {}
+ ~B() {}
+};
+
+bool make_temp(const B &) { return false; }
+bool test_temp_or() { return make_temp(1) || make_temp(2); }
+
+// CIR: cir.func{{.*}} @_Z12test_temp_orv()
+// CIR: %[[SCOPE:.*]] = cir.scope {
+// CIR: %[[REF_TMP0:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["ref.tmp0"]
+// CIR: %[[ONE:.*]] = cir.const #cir.int<1>
+// CIR: cir.call @_ZN1BC2Ei(%[[REF_TMP0]], %[[ONE]])
+// CIR: %[[MAKE_TEMP0:.*]] = cir.call @_Z9make_tempRK1B(%[[REF_TMP0]])
+// CIR: %[[TERNARY:.*]] = cir.ternary(%[[MAKE_TEMP0]], true {
+// CIR: %[[TRUE:.*]] = cir.const #true
+// CIR: cir.yield %[[TRUE]] : !cir.bool
+// CIR: }, false {
+// CIR: %[[REF_TMP1:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["ref.tmp1"]
+// CIR: %[[TWO:.*]] = cir.const #cir.int<2>
+// CIR: cir.call @_ZN1BC2Ei(%[[REF_TMP1]], %[[TWO]])
+// CIR: %[[MAKE_TEMP1:.*]] = cir.call @_Z9make_tempRK1B(%[[REF_TMP1]])
+// CIR: cir.call @_ZN1BD2Ev(%[[REF_TMP1]])
+// CIR: cir.yield %[[MAKE_TEMP1]] : !cir.bool
+// CIR: })
+// CIR: cir.call @_ZN1BD2Ev(%[[REF_TMP0]])
+// CIR: cir.yield %[[TERNARY]] : !cir.bool
+// CIR: } : !cir.bool
+
+// LLVM: define{{.*}} i1 @_Z12test_temp_orv() {
+// LLVM: %[[REF_TMP0:.*]] = alloca %struct.B
+// LLVM: %[[REF_TMP1:.*]] = alloca %struct.B
+// LLVM: br label %[[LOR_BEGIN:.*]]
+// LLVM: [[LOR_BEGIN]]:
+// LLVM: call void @_ZN1BC2Ei(ptr %[[REF_TMP0]], i32 1)
+// LLVM: %[[MAKE_TEMP0:.*]] = call i1 @_Z9make_tempRK1B(ptr %[[REF_TMP0]])
+// LLVM: br i1 %[[MAKE_TEMP0]], label %[[LHS_TRUE_BLOCK:.*]], label %[[LHS_FALSE_BLOCK:.*]]
+// LLVM: [[LHS_TRUE_BLOCK]]:
+// LLVM: br label %[[RESULT_BLOCK:.*]]
+// LLVM: [[LHS_FALSE_BLOCK]]:
+// LLVM: call void @_ZN1BC2Ei(ptr %[[REF_TMP1]], i32 2)
+// LLVM: %[[MAKE_TEMP1:.*]] = call i1 @_Z9make_tempRK1B(ptr %[[REF_TMP1]])
+// LLVM: call void @_ZN1BD2Ev(ptr %[[REF_TMP1]])
+// LLVM: br label %[[RESULT_BLOCK]]
+// LLVM: [[RESULT_BLOCK]]:
+// LLVM: %[[RESULT:.*]] = phi i1 [ %[[MAKE_TEMP1]], %[[LHS_FALSE_BLOCK]] ], [ true, %[[LHS_TRUE_BLOCK]] ]
+// LLVM: br label %[[LOR_END:.*]]
+// LLVM: [[LOR_END]]:
+// LLVM: call void @_ZN1BD2Ev(ptr %[[REF_TMP0]])
+
+// OGCG: define {{.*}} i1 @_Z12test_temp_orv()
+// OGCG: [[ENTRY:.*]]:
+// OGCG: %[[RETVAL:.*]] = alloca i1
+// OGCG: %[[REF_TMP0:.*]] = alloca %struct.B
+// OGCG: %[[REF_TMP1:.*]] = alloca %struct.B
+// OGCG: %[[CLEANUP_COND:.*]] = alloca i1
+// OGCG: call void @_ZN1BC2Ei(ptr {{.*}} %[[REF_TMP0]], i32 {{.*}} 1)
+// OGCG: %[[MAKE_TEMP0:.*]] = call {{.*}} i1 @_Z9make_tempRK1B(ptr {{.*}} %[[REF_TMP0]])
+// OGCG: store i1 false, ptr %cleanup.cond
+// OGCG: br i1 %[[MAKE_TEMP0]], label %[[LOR_END:.*]], label %[[LOR_RHS:.*]]
+// OGCG: [[LOR_RHS]]:
+// OGCG: call void @_ZN1BC2Ei(ptr {{.*}} %[[REF_TMP1]], i32 {{.*}} 2)
+// OGCG: store i1 true, ptr %[[CLEANUP_COND]]
+// OGCG: %[[MAKE_TEMP1:.*]] = call {{.*}} i1 @_Z9make_tempRK1B(ptr {{.*}} %[[REF_TMP1]])
+// OGCG: br label %[[LOR_END]]
+// OGCG: [[LOR_END]]:
+// OGCG: %[[PHI:.*]] = phi i1 [ true, %[[ENTRY]] ], [ %[[MAKE_TEMP1]], %[[LOR_RHS]] ]
+// OGCG: store i1 %[[PHI]], ptr %[[RETVAL]]
+// OGCG: %[[CLEANUP_IS_ACTIVE:.*]] = load i1, ptr %[[CLEANUP_COND]]
+// OGCG: br i1 %[[CLEANUP_IS_ACTIVE]], label %[[CLEANUP_ACTION:.*]], label %[[CLEANUP_DONE:.*]]
+// OGCG: [[CLEANUP_ACTION]]:
+// OGCG: call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP1]])
+// OGCG: br label %[[CLEANUP_DONE]]
+// OGCG: [[CLEANUP_DONE]]:
+// OGCG: call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP0]])
+
+bool test_temp_and() { return make_temp(1) && make_temp(2); }
+
+// CIR: cir.func{{.*}} @_Z13test_temp_andv()
+// CIR: %[[SCOPE:.*]] = cir.scope {
+// CIR: %[[REF_TMP0:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["ref.tmp0"]
+// CIR: %[[ONE:.*]] = cir.const #cir.int<1>
+// CIR: cir.call @_ZN1BC2Ei(%[[REF_TMP0]], %[[ONE]])
+// CIR: %[[MAKE_TEMP0:.*]] = cir.call @_Z9make_tempRK1B(%[[REF_TMP0]])
+// CIR: %[[TERNARY:.*]] = cir.ternary(%[[MAKE_TEMP0]], true {
+// CIR: %[[REF_TMP1:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["ref.tmp1"]
+// CIR: %[[TWO:.*]] = cir.const #cir.int<2>
+// CIR: cir.call @_ZN1BC2Ei(%[[REF_TMP1]], %[[TWO]])
+// CIR: %[[MAKE_TEMP1:.*]] = cir.call @_Z9make_tempRK1B(%[[REF_TMP1]])
+// CIR: cir.call @_ZN1BD2Ev(%[[REF_TMP1]])
+// CIR: cir.yield %[[MAKE_TEMP1]] : !cir.bool
+// CIR: }, false {
+// CIR: %[[FALSE:.*]] = cir.const #false
+// CIR: cir.yield %[[FALSE]] : !cir.bool
+// CIR: })
+// CIR: cir.call @_ZN1BD2Ev(%[[REF_TMP0]])
+// CIR: cir.yield %[[TERNARY]] : !cir.bool
+// CIR: } : !cir.bool
+
+// LLVM: define{{.*}} i1 @_Z13test_temp_andv() {
+// LLVM: %[[REF_TMP0:.*]] = alloca %struct.B
+// LLVM: %[[REF_TMP1:.*]] = alloca %struct.B
+// LLVM: br label %[[LAND_BEGIN:.*]]
+// LLVM: [[LAND_BEGIN]]:
+// LLVM: call void @_ZN1BC2Ei(ptr %[[REF_TMP0]], i32 1)
+// LLVM: %[[MAKE_TEMP0:.*]] = call i1 @_Z9make_tempRK1B(ptr %[[REF_TMP0]])
+// LLVM: br i1 %[[MAKE_TEMP0]], label %[[LHS_TRUE_BLOCK:.*]], label %[[LHS_FALSE_BLOCK:.*]]
+// LLVM: [[LHS_TRUE_BLOCK]]:
+// LLVM: call void @_ZN1BC2Ei(ptr %[[REF_TMP1]], i32 2)
+// LLVM: %[[MAKE_TEMP1:.*]] = call i1 @_Z9make_tempRK1B(ptr %[[REF_TMP1]])
+// LLVM: call void @_ZN1BD2Ev(ptr %[[REF_TMP1]])
+// LLVM: br label %[[RESULT_BLOCK:.*]]
+// LLVM: [[LHS_FALSE_BLOCK]]:
+// LLVM: br label %[[RESULT_BLOCK]]
+// LLVM: [[RESULT_BLOCK]]:
+// LLVM: %[[RESULT:.*]] = phi i1 [ false, %[[LHS_FALSE_BLOCK]] ], [ %[[MAKE_TEMP1]], %[[LHS_TRUE_BLOCK]] ]
+// LLVM: br label %[[LAND_END:.*]]
+// LLVM: [[LAND_END]]:
+// LLVM: call void @_ZN1BD2Ev(ptr %[[REF_TMP0]])
+
+// OGCG: define {{.*}} i1 @_Z13test_temp_andv()
+// OGCG: [[ENTRY:.*]]:
+// OGCG: %[[RETVAL:.*]] = alloca i1
+// OGCG: %[[REF_TMP0:.*]] = alloca %struct.B
+// OGCG: %[[REF_TMP1:.*]] = alloca %struct.B
+// OGCG: %[[CLEANUP_COND:.*]] = alloca i1
+// OGCG: call void @_ZN1BC2Ei(ptr {{.*}} %[[REF_TMP0]], i32 {{.*}} 1)
+// OGCG: %[[MAKE_TEMP0:.*]] = call {{.*}} i1 @_Z9make_tempRK1B(ptr {{.*}} %[[REF_TMP0]])
+// OGCG: store i1 false, ptr %cleanup.cond
+// OGCG: br i1 %[[MAKE_TEMP0]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]]
+// OGCG: [[LAND_RHS]]:
+// OGCG: call void @_ZN1BC2Ei(ptr {{.*}} %[[REF_TMP1]], i32 {{.*}} 2)
+// OGCG: store i1 true, ptr %[[CLEANUP_COND]]
+// OGCG: %[[MAKE_TEMP1:.*]] = call {{.*}} i1 @_Z9make_tempRK1B(ptr {{.*}} %[[REF_TMP1]])
+// OGCG: br label %[[LAND_END]]
+// OGCG: [[LAND_END]]:
+// OGCG: %[[PHI:.*]] = phi i1 [ false, %[[ENTRY]] ], [ %[[MAKE_TEMP1]], %[[LAND_RHS]] ]
+// OGCG: store i1 %[[PHI]], ptr %[[RETVAL]]
+// OGCG: %[[CLEANUP_IS_ACTIVE:.*]] = load i1, ptr %[[CLEANUP_COND]]
+// OGCG: br i1 %[[CLEANUP_IS_ACTIVE]], label %[[CLEANUP_ACTION:.*]], label %[[CLEANUP_DONE:.*]]
+// OGCG: [[CLEANUP_ACTION]]:
+// OGCG: call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP1]])
+// OGCG: br label %[[CLEANUP_DONE]]
+// OGCG: [[CLEANUP_DONE]]:
+// OGCG: call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP0]])
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm % nits
b.create<cir::YieldOp>(loc, cgf.evaluateExprAsBool(e->getRHS())); | ||
mlir::Value res = cgf.evaluateExprAsBool(e->getRHS()); | ||
lexScope.forceCleanup(); | ||
b.create<cir::YieldOp>(loc, res); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
b.create<cir::YieldOp>(loc, res); | |
cir::YieldOp::create(b, loc, res); |
b.create<cir::YieldOp>(loc, cgf.evaluateExprAsBool(e->getRHS())); | ||
mlir::Value res = cgf.evaluateExprAsBool(e->getRHS()); | ||
lexScope.forceCleanup(); | ||
b.create<cir::YieldOp>(loc, res); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
b.create<cir::YieldOp>(loc, res); | |
cir::YieldOp::create(b, loc, res); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm % nits
This fixes a few problems where destructors were not called for temporary objects and, after calling was enabled, they were placed incorrectly relative to cir.yield operations.