-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[CIR] Handle expression with cleanups #151600
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
Conversation
This adds code to handle expressions with cleanup, including materializing a temporary object for the expression.
|
@llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) ChangesThis adds code to handle expressions with cleanup, including materializing a temporary object for the expression. Full diff: https://github.com/llvm/llvm-project/pull/151600.diff 8 Files Affected:
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index ad3329d3be32e..32a53a1208092 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -222,6 +222,7 @@ struct MissingFeatures {
static bool lowerAggregateLoadStore() { return false; }
static bool lowerModeOptLevel() { return false; }
static bool maybeHandleStaticInExternC() { return false; }
+ static bool mergeAllConstants() { return false; }
static bool metaDataNode() { return false; }
static bool moduleNameHash() { return false; }
static bool msabi() { return false; }
diff --git a/clang/lib/CIR/CodeGen/Address.h b/clang/lib/CIR/CodeGen/Address.h
index 6f76c3ebb1c5e..72c1d524bf804 100644
--- a/clang/lib/CIR/CodeGen/Address.h
+++ b/clang/lib/CIR/CodeGen/Address.h
@@ -101,6 +101,13 @@ class Address {
}
clang::CharUnits getAlignment() const { return alignment; }
+
+ /// Get the operation which defines this address.
+ mlir::Operation *getDefiningOp() const {
+ if (!isValid())
+ return nullptr;
+ return getPointer().getDefiningOp();
+ }
};
} // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index 6527fb5697f7c..cf03a427c47b3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -649,6 +649,27 @@ void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs,
assert(!cir::MissingFeatures::sanitizers());
}
+namespace {
+struct DestroyObject final : EHScopeStack::Cleanup {
+ DestroyObject(Address addr, QualType type,
+ CIRGenFunction::Destroyer *destroyer)
+ : addr(addr), type(type), destroyer(destroyer) {}
+
+ Address addr;
+ QualType type;
+ CIRGenFunction::Destroyer *destroyer;
+
+ void emit(CIRGenFunction &cgf) override {
+ cgf.emitDestroy(addr, type, destroyer);
+ }
+};
+} // namespace
+
+void CIRGenFunction::pushDestroy(CleanupKind cleanupKind, Address addr,
+ QualType type, Destroyer *destroyer) {
+ pushFullExprCleanup<DestroyObject>(cleanupKind, addr, type, destroyer);
+}
+
/// Destroys all the elements of the given array, beginning from last to first.
/// The array cannot be zero-length.
///
@@ -736,22 +757,6 @@ CIRGenFunction::getDestroyer(QualType::DestructionKind kind) {
llvm_unreachable("Unknown DestructionKind");
}
-namespace {
-struct DestroyObject final : EHScopeStack::Cleanup {
- DestroyObject(Address addr, QualType type,
- CIRGenFunction::Destroyer *destroyer)
- : addr(addr), type(type), destroyer(destroyer) {}
-
- Address addr;
- QualType type;
- CIRGenFunction::Destroyer *destroyer;
-
- void emit(CIRGenFunction &cgf) override {
- cgf.emitDestroy(addr, type, destroyer);
- }
-};
-} // namespace
-
/// Enter a destroy cleanup for the given local variable.
void CIRGenFunction::emitAutoVarTypeCleanup(
const CIRGenFunction::AutoVarEmission &emission,
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index c18498f80e99f..16c5b6cc9bad0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1096,6 +1096,154 @@ void CIRGenFunction::emitAnyExprToMem(const Expr *e, Address location,
llvm_unreachable("bad evaluation kind");
}
+static Address createReferenceTemporary(CIRGenFunction &cgf,
+ const MaterializeTemporaryExpr *m,
+ const Expr *inner) {
+ // TODO(cir): cgf.getTargetHooks();
+ switch (m->getStorageDuration()) {
+ case SD_FullExpression:
+ case SD_Automatic: {
+ QualType ty = inner->getType();
+
+ assert(!cir::MissingFeatures::mergeAllConstants());
+
+ // The temporary memory should be created in the same scope as the extending
+ // declaration of the temporary materialization expression.
+ cir::AllocaOp extDeclAlloca;
+ if (const clang::ValueDecl *extDecl = m->getExtendingDecl()) {
+ auto extDeclAddrIter = cgf.localDeclMap.find(extDecl);
+ if (extDeclAddrIter != cgf.localDeclMap.end()) {
+ extDeclAlloca = mlir::dyn_cast_if_present<cir::AllocaOp>(
+ extDeclAddrIter->second.getDefiningOp());
+ }
+ }
+ mlir::OpBuilder::InsertPoint ip;
+ if (extDeclAlloca)
+ ip = {extDeclAlloca->getBlock(), extDeclAlloca->getIterator()};
+ return cgf.createMemTemp(ty, cgf.getLoc(m->getSourceRange()),
+ cgf.getCounterRefTmpAsString(), /*alloca=*/nullptr,
+ ip);
+ }
+ case SD_Thread:
+ case SD_Static: {
+ cgf.cgm.errorNYI(
+ m->getSourceRange(),
+ "createReferenceTemporary: static/thread storage duration");
+ return Address::invalid();
+ }
+
+ case SD_Dynamic:
+ llvm_unreachable("temporary can't have dynamic storage duration");
+ }
+ llvm_unreachable("unknown storage duration");
+}
+
+static void pushTemporaryCleanup(CIRGenFunction &cgf,
+ const MaterializeTemporaryExpr *m,
+ const Expr *e, Address referenceTemporary) {
+ // Objective-C++ ARC:
+ // If we are binding a reference to a temporary that has ownership, we
+ // need to perform retain/release operations on the temporary.
+ //
+ // FIXME(ogcg): This should be looking at e, not m.
+ if (m->getType().getObjCLifetime()) {
+ cgf.cgm.errorNYI(e->getSourceRange(), "pushTemporaryCleanup: ObjCLifetime");
+ return;
+ }
+
+ clang::CXXDestructorDecl *referenceTemporaryDtor = nullptr;
+ if (const clang::RecordType *rt = e->getType()
+ ->getBaseElementTypeUnsafe()
+ ->getAs<clang::RecordType>()) {
+ // Get the destructor for the reference temporary.
+ auto *classDecl = cast<clang::CXXRecordDecl>(rt->getDecl());
+ if (!classDecl->hasTrivialDestructor())
+ referenceTemporaryDtor = classDecl->getDestructor();
+ }
+
+ if (!referenceTemporaryDtor)
+ return;
+
+ // Call the destructor for the temporary.
+ switch (m->getStorageDuration()) {
+ case SD_Static:
+ case SD_Thread:
+ cgf.cgm.errorNYI(e->getSourceRange(),
+ "pushTemporaryCleanup: static/thread storage duration");
+ return;
+
+ case SD_FullExpression:
+ cgf.pushDestroy(NormalAndEHCleanup, referenceTemporary, e->getType(),
+ CIRGenFunction::destroyCXXObject);
+ break;
+
+ case SD_Automatic:
+ cgf.cgm.errorNYI(e->getSourceRange(),
+ "pushTemporaryCleanup: automatic storage duration");
+ break;
+
+ case SD_Dynamic:
+ llvm_unreachable("temporary cannot have dynamic storage duration");
+ }
+}
+
+LValue CIRGenFunction::emitMaterializeTemporaryExpr(
+ const MaterializeTemporaryExpr *m) {
+ const Expr *e = m->getSubExpr();
+
+ assert((!m->getExtendingDecl() || !isa<VarDecl>(m->getExtendingDecl()) ||
+ !cast<VarDecl>(m->getExtendingDecl())->isARCPseudoStrong()) &&
+ "Reference should never be pseudo-strong!");
+
+ // FIXME: ideally this would use emitAnyExprToMem, however, we cannot do so
+ // as that will cause the lifetime adjustment to be lost for ARC
+ auto ownership = m->getType().getObjCLifetime();
+ if (ownership != Qualifiers::OCL_None &&
+ ownership != Qualifiers::OCL_ExplicitNone) {
+ cgm.errorNYI(e->getSourceRange(),
+ "emitMaterializeTemporaryExpr: ObjCLifetime");
+ return {};
+ }
+
+ SmallVector<const Expr *, 2> commaLHSs;
+ SmallVector<SubobjectAdjustment, 2> adjustments;
+ e = e->skipRValueSubobjectAdjustments(commaLHSs, adjustments);
+
+ for (const Expr *ignored : commaLHSs)
+ emitIgnoredExpr(ignored);
+
+ if (isa<OpaqueValueExpr>(e)) {
+ cgm.errorNYI(e->getSourceRange(),
+ "emitMaterializeTemporaryExpr: OpaqueValueExpr");
+ return {};
+ }
+
+ // Create and initialize the reference temporary.
+ Address object = createReferenceTemporary(*this, m, e);
+
+ if (auto var =
+ mlir::dyn_cast<cir::GlobalOp>(object.getPointer().getDefiningOp())) {
+ // TODO(cir): add something akin to stripPointerCasts() to ptr above
+ cgm.errorNYI(e->getSourceRange(), "emitMaterializeTemporaryExpr: GlobalOp");
+ return {};
+ } else {
+ assert(!cir::MissingFeatures::emitLifetimeMarkers());
+ emitAnyExprToMem(e, object, Qualifiers(), /*isInitializer=*/true);
+ }
+ pushTemporaryCleanup(*this, m, e, object);
+
+ // Perform derived-to-base casts and/or field accesses, to get from the
+ // temporary object we created (and, potentially, for which we extended
+ // the lifetime) to the subobject we're binding the reference to.
+ if (!adjustments.empty()) {
+ cgm.errorNYI(e->getSourceRange(),
+ "emitMaterializeTemporaryExpr: Adjustments");
+ return {};
+ }
+
+ return makeAddrLValue(object, m->getType(), AlignmentSource::Decl);
+}
+
LValue CIRGenFunction::emitCompoundLiteralLValue(const CompoundLiteralExpr *e) {
if (e->isFileScope()) {
cgm.errorNYI(e->getSourceRange(), "emitCompoundLiteralLValue: FileScope");
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 2523b0ff33787..d20c15d02b31a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -626,6 +626,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
mlir::Value VisitCXXThisExpr(CXXThisExpr *te) { return cgf.loadCXXThis(); }
+ mlir::Value VisitExprWithCleanups(ExprWithCleanups *e);
mlir::Value VisitCXXNewExpr(const CXXNewExpr *e) {
return cgf.emitCXXNewExpr(e);
}
@@ -1217,6 +1218,29 @@ mlir::Value ScalarExprEmitter::emitCompoundAssign(
return emitLoadOfLValue(lhs, e->getExprLoc());
}
+mlir::Value ScalarExprEmitter::VisitExprWithCleanups(ExprWithCleanups *e) {
+ mlir::Location scopeLoc = cgf.getLoc(e->getSourceRange());
+ mlir::OpBuilder &builder = cgf.builder;
+
+ auto scope = cir::ScopeOp::create(
+ builder, scopeLoc,
+ /*scopeBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Type &yieldTy, mlir::Location loc) {
+ CIRGenFunction::LexicalScope lexScope{cgf, loc,
+ builder.getInsertionBlock()};
+ mlir::Value scopeYieldVal = Visit(e->getSubExpr());
+ if (scopeYieldVal) {
+ // Defend against dominance problems caused by jumps out of expression
+ // evaluation through the shared cleanup block.
+ lexScope.forceCleanup();
+ cir::YieldOp::create(builder, loc, scopeYieldVal);
+ yieldTy = scopeYieldVal.getType();
+ }
+ });
+
+ return scope.getNumResults() > 0 ? scope->getResult(0) : nullptr;
+}
+
} // namespace
LValue
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index c65d0254bf8e6..7b2efb7a1b423 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -801,6 +801,8 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
case Expr::CXXDynamicCastExprClass:
case Expr::ImplicitCastExprClass:
return emitCastLValue(cast<CastExpr>(e));
+ case Expr::MaterializeTemporaryExprClass:
+ return emitMaterializeTemporaryExpr(cast<MaterializeTemporaryExpr>(e));
}
}
@@ -811,6 +813,10 @@ static std::string getVersionedTmpName(llvm::StringRef name, unsigned cnt) {
return std::string(out.str());
}
+std::string CIRGenFunction::getCounterRefTmpAsString() {
+ return getVersionedTmpName("ref.tmp", counterRefTmp++);
+}
+
std::string CIRGenFunction::getCounterAggTmpAsString() {
return getVersionedTmpName("agg.tmp", counterAggTmp++);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 603f75078c519..a7880ae6dad40 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -325,7 +325,9 @@ class CIRGenFunction : public CIRGenTypeCache {
};
/// Hold counters for incrementally naming temporaries
+ unsigned counterRefTmp = 0;
unsigned counterAggTmp = 0;
+ std::string getCounterRefTmpAsString();
std::string getCounterAggTmpAsString();
/// Helpers to convert Clang's SourceLocation to a MLIR Location.
@@ -604,6 +606,19 @@ class CIRGenFunction : public CIRGenTypeCache {
void popCleanupBlocks(size_t oldCleanupStackDepth);
void popCleanupBlock();
+ /// Push a cleanup to be run at the end of the current full-expression. Safe
+ /// against the possibility that we're currently inside a
+ /// conditionally-evaluated expression.
+ template <class T, class... As>
+ void pushFullExprCleanup(CleanupKind kind, As... a) {
+ // If we're not in a conditional branch, or if none of the
+ // arguments requires saving, then use the unconditional cleanup.
+ if (!isInConditionalBranch())
+ return ehStack.pushCleanup<T>(kind, a...);
+
+ cgm.errorNYI("pushFullExprCleanup in conditional branch");
+ }
+
/// Enters a new scope for capturing cleanups, all of which
/// will be executed once the scope is exited.
class RunCleanupsScope {
@@ -619,6 +634,7 @@ class CIRGenFunction : public CIRGenTypeCache {
protected:
CIRGenFunction &cgf;
+ public:
/// Enter a new cleanup scope.
explicit RunCleanupsScope(CIRGenFunction &cgf)
: performCleanup(true), cgf(cgf) {
@@ -801,6 +817,9 @@ class CIRGenFunction : public CIRGenTypeCache {
static Destroyer destroyCXXObject;
+ void pushDestroy(CleanupKind kind, Address addr, QualType type,
+ Destroyer *destroyer);
+
Destroyer *getDestroyer(clang::QualType::DestructionKind kind);
/// ----------------------
@@ -1136,6 +1155,8 @@ class CIRGenFunction : public CIRGenTypeCache {
const clang::FieldDecl *field,
llvm::StringRef fieldName);
+ LValue emitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *e);
+
LValue emitMemberExpr(const MemberExpr *e);
/// Given an expression with a pointer type, emit the value and compute our
diff --git a/clang/test/CIR/CodeGen/cleanup.cpp b/clang/test/CIR/CodeGen/cleanup.cpp
index 4196151352b79..0400d4b275fbb 100644
--- a/clang/test/CIR/CodeGen/cleanup.cpp
+++ b/clang/test/CIR/CodeGen/cleanup.cpp
@@ -81,3 +81,17 @@ void test_cleanup_nested() {
// CHECK: }
// CHECK: cir.call @_ZN5StrukD1Ev(%[[OUTER]]) nothrow : (!cir.ptr<!rec_Struk>) -> ()
// CHECK: cir.return
+
+void use_ref(const Struk &);
+
+void test_expr_with_cleanup() {
+ use_ref(Struk{});
+}
+
+// CHECK: cir.func{{.*}} @_Z22test_expr_with_cleanupv()
+// CHECK: cir.scope {
+// CHECK: %[[S:.*]] = cir.alloca !rec_Struk, !cir.ptr<!rec_Struk>
+// CHECK: cir.call @_Z7use_refRK5Struk(%[[S]])
+// CHECK: cir.call @_ZN5StrukD1Ev(%[[S]]) nothrow : (!cir.ptr<!rec_Struk>) -> ()
+// CHECK: }
+// CHECK: cir.return
|
|
@llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) ChangesThis adds code to handle expressions with cleanup, including materializing a temporary object for the expression. Full diff: https://github.com/llvm/llvm-project/pull/151600.diff 8 Files Affected:
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index ad3329d3be32e..32a53a1208092 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -222,6 +222,7 @@ struct MissingFeatures {
static bool lowerAggregateLoadStore() { return false; }
static bool lowerModeOptLevel() { return false; }
static bool maybeHandleStaticInExternC() { return false; }
+ static bool mergeAllConstants() { return false; }
static bool metaDataNode() { return false; }
static bool moduleNameHash() { return false; }
static bool msabi() { return false; }
diff --git a/clang/lib/CIR/CodeGen/Address.h b/clang/lib/CIR/CodeGen/Address.h
index 6f76c3ebb1c5e..72c1d524bf804 100644
--- a/clang/lib/CIR/CodeGen/Address.h
+++ b/clang/lib/CIR/CodeGen/Address.h
@@ -101,6 +101,13 @@ class Address {
}
clang::CharUnits getAlignment() const { return alignment; }
+
+ /// Get the operation which defines this address.
+ mlir::Operation *getDefiningOp() const {
+ if (!isValid())
+ return nullptr;
+ return getPointer().getDefiningOp();
+ }
};
} // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index 6527fb5697f7c..cf03a427c47b3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -649,6 +649,27 @@ void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs,
assert(!cir::MissingFeatures::sanitizers());
}
+namespace {
+struct DestroyObject final : EHScopeStack::Cleanup {
+ DestroyObject(Address addr, QualType type,
+ CIRGenFunction::Destroyer *destroyer)
+ : addr(addr), type(type), destroyer(destroyer) {}
+
+ Address addr;
+ QualType type;
+ CIRGenFunction::Destroyer *destroyer;
+
+ void emit(CIRGenFunction &cgf) override {
+ cgf.emitDestroy(addr, type, destroyer);
+ }
+};
+} // namespace
+
+void CIRGenFunction::pushDestroy(CleanupKind cleanupKind, Address addr,
+ QualType type, Destroyer *destroyer) {
+ pushFullExprCleanup<DestroyObject>(cleanupKind, addr, type, destroyer);
+}
+
/// Destroys all the elements of the given array, beginning from last to first.
/// The array cannot be zero-length.
///
@@ -736,22 +757,6 @@ CIRGenFunction::getDestroyer(QualType::DestructionKind kind) {
llvm_unreachable("Unknown DestructionKind");
}
-namespace {
-struct DestroyObject final : EHScopeStack::Cleanup {
- DestroyObject(Address addr, QualType type,
- CIRGenFunction::Destroyer *destroyer)
- : addr(addr), type(type), destroyer(destroyer) {}
-
- Address addr;
- QualType type;
- CIRGenFunction::Destroyer *destroyer;
-
- void emit(CIRGenFunction &cgf) override {
- cgf.emitDestroy(addr, type, destroyer);
- }
-};
-} // namespace
-
/// Enter a destroy cleanup for the given local variable.
void CIRGenFunction::emitAutoVarTypeCleanup(
const CIRGenFunction::AutoVarEmission &emission,
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index c18498f80e99f..16c5b6cc9bad0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1096,6 +1096,154 @@ void CIRGenFunction::emitAnyExprToMem(const Expr *e, Address location,
llvm_unreachable("bad evaluation kind");
}
+static Address createReferenceTemporary(CIRGenFunction &cgf,
+ const MaterializeTemporaryExpr *m,
+ const Expr *inner) {
+ // TODO(cir): cgf.getTargetHooks();
+ switch (m->getStorageDuration()) {
+ case SD_FullExpression:
+ case SD_Automatic: {
+ QualType ty = inner->getType();
+
+ assert(!cir::MissingFeatures::mergeAllConstants());
+
+ // The temporary memory should be created in the same scope as the extending
+ // declaration of the temporary materialization expression.
+ cir::AllocaOp extDeclAlloca;
+ if (const clang::ValueDecl *extDecl = m->getExtendingDecl()) {
+ auto extDeclAddrIter = cgf.localDeclMap.find(extDecl);
+ if (extDeclAddrIter != cgf.localDeclMap.end()) {
+ extDeclAlloca = mlir::dyn_cast_if_present<cir::AllocaOp>(
+ extDeclAddrIter->second.getDefiningOp());
+ }
+ }
+ mlir::OpBuilder::InsertPoint ip;
+ if (extDeclAlloca)
+ ip = {extDeclAlloca->getBlock(), extDeclAlloca->getIterator()};
+ return cgf.createMemTemp(ty, cgf.getLoc(m->getSourceRange()),
+ cgf.getCounterRefTmpAsString(), /*alloca=*/nullptr,
+ ip);
+ }
+ case SD_Thread:
+ case SD_Static: {
+ cgf.cgm.errorNYI(
+ m->getSourceRange(),
+ "createReferenceTemporary: static/thread storage duration");
+ return Address::invalid();
+ }
+
+ case SD_Dynamic:
+ llvm_unreachable("temporary can't have dynamic storage duration");
+ }
+ llvm_unreachable("unknown storage duration");
+}
+
+static void pushTemporaryCleanup(CIRGenFunction &cgf,
+ const MaterializeTemporaryExpr *m,
+ const Expr *e, Address referenceTemporary) {
+ // Objective-C++ ARC:
+ // If we are binding a reference to a temporary that has ownership, we
+ // need to perform retain/release operations on the temporary.
+ //
+ // FIXME(ogcg): This should be looking at e, not m.
+ if (m->getType().getObjCLifetime()) {
+ cgf.cgm.errorNYI(e->getSourceRange(), "pushTemporaryCleanup: ObjCLifetime");
+ return;
+ }
+
+ clang::CXXDestructorDecl *referenceTemporaryDtor = nullptr;
+ if (const clang::RecordType *rt = e->getType()
+ ->getBaseElementTypeUnsafe()
+ ->getAs<clang::RecordType>()) {
+ // Get the destructor for the reference temporary.
+ auto *classDecl = cast<clang::CXXRecordDecl>(rt->getDecl());
+ if (!classDecl->hasTrivialDestructor())
+ referenceTemporaryDtor = classDecl->getDestructor();
+ }
+
+ if (!referenceTemporaryDtor)
+ return;
+
+ // Call the destructor for the temporary.
+ switch (m->getStorageDuration()) {
+ case SD_Static:
+ case SD_Thread:
+ cgf.cgm.errorNYI(e->getSourceRange(),
+ "pushTemporaryCleanup: static/thread storage duration");
+ return;
+
+ case SD_FullExpression:
+ cgf.pushDestroy(NormalAndEHCleanup, referenceTemporary, e->getType(),
+ CIRGenFunction::destroyCXXObject);
+ break;
+
+ case SD_Automatic:
+ cgf.cgm.errorNYI(e->getSourceRange(),
+ "pushTemporaryCleanup: automatic storage duration");
+ break;
+
+ case SD_Dynamic:
+ llvm_unreachable("temporary cannot have dynamic storage duration");
+ }
+}
+
+LValue CIRGenFunction::emitMaterializeTemporaryExpr(
+ const MaterializeTemporaryExpr *m) {
+ const Expr *e = m->getSubExpr();
+
+ assert((!m->getExtendingDecl() || !isa<VarDecl>(m->getExtendingDecl()) ||
+ !cast<VarDecl>(m->getExtendingDecl())->isARCPseudoStrong()) &&
+ "Reference should never be pseudo-strong!");
+
+ // FIXME: ideally this would use emitAnyExprToMem, however, we cannot do so
+ // as that will cause the lifetime adjustment to be lost for ARC
+ auto ownership = m->getType().getObjCLifetime();
+ if (ownership != Qualifiers::OCL_None &&
+ ownership != Qualifiers::OCL_ExplicitNone) {
+ cgm.errorNYI(e->getSourceRange(),
+ "emitMaterializeTemporaryExpr: ObjCLifetime");
+ return {};
+ }
+
+ SmallVector<const Expr *, 2> commaLHSs;
+ SmallVector<SubobjectAdjustment, 2> adjustments;
+ e = e->skipRValueSubobjectAdjustments(commaLHSs, adjustments);
+
+ for (const Expr *ignored : commaLHSs)
+ emitIgnoredExpr(ignored);
+
+ if (isa<OpaqueValueExpr>(e)) {
+ cgm.errorNYI(e->getSourceRange(),
+ "emitMaterializeTemporaryExpr: OpaqueValueExpr");
+ return {};
+ }
+
+ // Create and initialize the reference temporary.
+ Address object = createReferenceTemporary(*this, m, e);
+
+ if (auto var =
+ mlir::dyn_cast<cir::GlobalOp>(object.getPointer().getDefiningOp())) {
+ // TODO(cir): add something akin to stripPointerCasts() to ptr above
+ cgm.errorNYI(e->getSourceRange(), "emitMaterializeTemporaryExpr: GlobalOp");
+ return {};
+ } else {
+ assert(!cir::MissingFeatures::emitLifetimeMarkers());
+ emitAnyExprToMem(e, object, Qualifiers(), /*isInitializer=*/true);
+ }
+ pushTemporaryCleanup(*this, m, e, object);
+
+ // Perform derived-to-base casts and/or field accesses, to get from the
+ // temporary object we created (and, potentially, for which we extended
+ // the lifetime) to the subobject we're binding the reference to.
+ if (!adjustments.empty()) {
+ cgm.errorNYI(e->getSourceRange(),
+ "emitMaterializeTemporaryExpr: Adjustments");
+ return {};
+ }
+
+ return makeAddrLValue(object, m->getType(), AlignmentSource::Decl);
+}
+
LValue CIRGenFunction::emitCompoundLiteralLValue(const CompoundLiteralExpr *e) {
if (e->isFileScope()) {
cgm.errorNYI(e->getSourceRange(), "emitCompoundLiteralLValue: FileScope");
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 2523b0ff33787..d20c15d02b31a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -626,6 +626,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
mlir::Value VisitCXXThisExpr(CXXThisExpr *te) { return cgf.loadCXXThis(); }
+ mlir::Value VisitExprWithCleanups(ExprWithCleanups *e);
mlir::Value VisitCXXNewExpr(const CXXNewExpr *e) {
return cgf.emitCXXNewExpr(e);
}
@@ -1217,6 +1218,29 @@ mlir::Value ScalarExprEmitter::emitCompoundAssign(
return emitLoadOfLValue(lhs, e->getExprLoc());
}
+mlir::Value ScalarExprEmitter::VisitExprWithCleanups(ExprWithCleanups *e) {
+ mlir::Location scopeLoc = cgf.getLoc(e->getSourceRange());
+ mlir::OpBuilder &builder = cgf.builder;
+
+ auto scope = cir::ScopeOp::create(
+ builder, scopeLoc,
+ /*scopeBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Type &yieldTy, mlir::Location loc) {
+ CIRGenFunction::LexicalScope lexScope{cgf, loc,
+ builder.getInsertionBlock()};
+ mlir::Value scopeYieldVal = Visit(e->getSubExpr());
+ if (scopeYieldVal) {
+ // Defend against dominance problems caused by jumps out of expression
+ // evaluation through the shared cleanup block.
+ lexScope.forceCleanup();
+ cir::YieldOp::create(builder, loc, scopeYieldVal);
+ yieldTy = scopeYieldVal.getType();
+ }
+ });
+
+ return scope.getNumResults() > 0 ? scope->getResult(0) : nullptr;
+}
+
} // namespace
LValue
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index c65d0254bf8e6..7b2efb7a1b423 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -801,6 +801,8 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
case Expr::CXXDynamicCastExprClass:
case Expr::ImplicitCastExprClass:
return emitCastLValue(cast<CastExpr>(e));
+ case Expr::MaterializeTemporaryExprClass:
+ return emitMaterializeTemporaryExpr(cast<MaterializeTemporaryExpr>(e));
}
}
@@ -811,6 +813,10 @@ static std::string getVersionedTmpName(llvm::StringRef name, unsigned cnt) {
return std::string(out.str());
}
+std::string CIRGenFunction::getCounterRefTmpAsString() {
+ return getVersionedTmpName("ref.tmp", counterRefTmp++);
+}
+
std::string CIRGenFunction::getCounterAggTmpAsString() {
return getVersionedTmpName("agg.tmp", counterAggTmp++);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 603f75078c519..a7880ae6dad40 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -325,7 +325,9 @@ class CIRGenFunction : public CIRGenTypeCache {
};
/// Hold counters for incrementally naming temporaries
+ unsigned counterRefTmp = 0;
unsigned counterAggTmp = 0;
+ std::string getCounterRefTmpAsString();
std::string getCounterAggTmpAsString();
/// Helpers to convert Clang's SourceLocation to a MLIR Location.
@@ -604,6 +606,19 @@ class CIRGenFunction : public CIRGenTypeCache {
void popCleanupBlocks(size_t oldCleanupStackDepth);
void popCleanupBlock();
+ /// Push a cleanup to be run at the end of the current full-expression. Safe
+ /// against the possibility that we're currently inside a
+ /// conditionally-evaluated expression.
+ template <class T, class... As>
+ void pushFullExprCleanup(CleanupKind kind, As... a) {
+ // If we're not in a conditional branch, or if none of the
+ // arguments requires saving, then use the unconditional cleanup.
+ if (!isInConditionalBranch())
+ return ehStack.pushCleanup<T>(kind, a...);
+
+ cgm.errorNYI("pushFullExprCleanup in conditional branch");
+ }
+
/// Enters a new scope for capturing cleanups, all of which
/// will be executed once the scope is exited.
class RunCleanupsScope {
@@ -619,6 +634,7 @@ class CIRGenFunction : public CIRGenTypeCache {
protected:
CIRGenFunction &cgf;
+ public:
/// Enter a new cleanup scope.
explicit RunCleanupsScope(CIRGenFunction &cgf)
: performCleanup(true), cgf(cgf) {
@@ -801,6 +817,9 @@ class CIRGenFunction : public CIRGenTypeCache {
static Destroyer destroyCXXObject;
+ void pushDestroy(CleanupKind kind, Address addr, QualType type,
+ Destroyer *destroyer);
+
Destroyer *getDestroyer(clang::QualType::DestructionKind kind);
/// ----------------------
@@ -1136,6 +1155,8 @@ class CIRGenFunction : public CIRGenTypeCache {
const clang::FieldDecl *field,
llvm::StringRef fieldName);
+ LValue emitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *e);
+
LValue emitMemberExpr(const MemberExpr *e);
/// Given an expression with a pointer type, emit the value and compute our
diff --git a/clang/test/CIR/CodeGen/cleanup.cpp b/clang/test/CIR/CodeGen/cleanup.cpp
index 4196151352b79..0400d4b275fbb 100644
--- a/clang/test/CIR/CodeGen/cleanup.cpp
+++ b/clang/test/CIR/CodeGen/cleanup.cpp
@@ -81,3 +81,17 @@ void test_cleanup_nested() {
// CHECK: }
// CHECK: cir.call @_ZN5StrukD1Ev(%[[OUTER]]) nothrow : (!cir.ptr<!rec_Struk>) -> ()
// CHECK: cir.return
+
+void use_ref(const Struk &);
+
+void test_expr_with_cleanup() {
+ use_ref(Struk{});
+}
+
+// CHECK: cir.func{{.*}} @_Z22test_expr_with_cleanupv()
+// CHECK: cir.scope {
+// CHECK: %[[S:.*]] = cir.alloca !rec_Struk, !cir.ptr<!rec_Struk>
+// CHECK: cir.call @_Z7use_refRK5Struk(%[[S]])
+// CHECK: cir.call @_ZN5StrukD1Ev(%[[S]]) nothrow : (!cir.ptr<!rec_Struk>) -> ()
+// CHECK: }
+// CHECK: cir.return
|
xlauko
left a comment
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 with minor nits
clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Outdated
| extDeclAlloca = mlir::dyn_cast_if_present<cir::AllocaOp>( | ||
| extDeclAddrIter->second.getDefiningOp()); |
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.
| extDeclAlloca = mlir::dyn_cast_if_present<cir::AllocaOp>( | |
| extDeclAddrIter->second.getDefiningOp()); | |
| extDeclAlloca = extDeclAddrIter->second.getDefiningOp<cir::AllocaOp>(); |
clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Outdated
| if (const clang::RecordType *rt = e->getType() | ||
| ->getBaseElementTypeUnsafe() | ||
| ->getAs<clang::RecordType>()) { | ||
| // Get the destructor for the reference temporary. | ||
| auto *classDecl = cast<clang::CXXRecordDecl>(rt->getDecl()); |
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.
| if (const clang::RecordType *rt = e->getType() | |
| ->getBaseElementTypeUnsafe() | |
| ->getAs<clang::RecordType>()) { | |
| // Get the destructor for the reference temporary. | |
| auto *classDecl = cast<clang::CXXRecordDecl>(rt->getDecl()); | |
| if (const RecordType *rt = e->getType() | |
| ->getBaseElementTypeUnsafe() | |
| ->getAs<clang::RecordType>()) { | |
| // Get the destructor for the reference temporary. | |
| auto *classDecl = cast<CXXRecordDecl>(rt->getDecl()); |
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.
Unfortunately, these are necessary because we have a cir::RecordType.
clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Outdated
| if (auto var = | ||
| mlir::dyn_cast<cir::GlobalOp>(object.getPointer().getDefiningOp())) { |
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.
after #151217 is merged:
| if (auto var = | |
| mlir::dyn_cast<cir::GlobalOp>(object.getPointer().getDefiningOp())) { | |
| if (auto var = object.getDefiningOp<cir::GlobalOp>()) { |
This adds code to handle expressions with cleanup, including materializing a temporary object for the expression.