Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRGenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down
30 changes: 24 additions & 6 deletions clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand Down
16 changes: 10 additions & 6 deletions clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1099,15 +1099,17 @@ 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();
cir::YieldOp::create(b, loc, res);
},
/*falseBuilder*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
CIRGenFunction::LexicalScope lexScope{cgf, loc,
b.getInsertionBlock()};
cgf.curLexScope->setAsTernary();
auto res = b.create<cir::ConstantOp>(loc, builder.getFalseAttr());
b.create<cir::YieldOp>(loc, res.getRes());
auto res = cir::ConstantOp::create(b, loc, builder.getFalseAttr());
cir::YieldOp::create(b, loc, res.getRes());
});
return maybePromoteBoolResult(resOp.getResult(), resTy);
}
Expand Down Expand Up @@ -1143,15 +1145,17 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
CIRGenFunction::LexicalScope lexScope{cgf, loc,
b.getInsertionBlock()};
cgf.curLexScope->setAsTernary();
auto res = b.create<cir::ConstantOp>(loc, builder.getTrueAttr());
b.create<cir::YieldOp>(loc, res.getRes());
auto res = cir::ConstantOp::create(b, loc, builder.getTrueAttr());
cir::YieldOp::create(b, loc, res.getRes());
},
/*falseBuilder*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
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();
cir::YieldOp::create(b, loc, res);
});

return maybePromoteBoolResult(resOp.getResult(), resTy);
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
173 changes: 173 additions & 0 deletions clang/test/CIR/CodeGen/dtors.cpp
Original file line number Diff line number Diff line change
@@ -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]])