[clang] a304e09 - [CIR] Handle expression with cleanups (#151600)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 1 11:38:39 PDT 2025
Author: Andy Kaylor
Date: 2025-08-01T11:38:35-07:00
New Revision: a304e09fe767eb6e15680602cdabf5c405894061
URL: https://github.com/llvm/llvm-project/commit/a304e09fe767eb6e15680602cdabf5c405894061
DIFF: https://github.com/llvm/llvm-project/commit/a304e09fe767eb6e15680602cdabf5c405894061.diff
LOG: [CIR] Handle expression with cleanups (#151600)
This adds code to handle expressions with cleanup, including
materializing a temporary object for the expression.
Added:
Modified:
clang/include/clang/CIR/MissingFeatures.h
clang/lib/CIR/CodeGen/Address.h
clang/lib/CIR/CodeGen/CIRGenDecl.cpp
clang/lib/CIR/CodeGen/CIRGenExpr.cpp
clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
clang/lib/CIR/CodeGen/CIRGenFunction.cpp
clang/lib/CIR/CodeGen/CIRGenFunction.h
clang/test/CIR/CodeGen/cleanup.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index adc7b5f239ea9..27dd181f2fb37 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -223,6 +223,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..6c927e9eda9cc 100644
--- a/clang/lib/CIR/CodeGen/Address.h
+++ b/clang/lib/CIR/CodeGen/Address.h
@@ -101,6 +101,17 @@ 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();
+ }
+
+ template <typename OpTy> OpTy getDefiningOp() const {
+ return mlir::dyn_cast_or_null<OpTy>(getDefiningOp());
+ }
};
} // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index fa0d8a2a13432..9cdbebead5419 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -653,6 +653,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.
///
@@ -740,22 +761,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 d267504dedd85..27a53b7c41090 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1105,6 +1105,151 @@ 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 ValueDecl *extDecl = m->getExtendingDecl()) {
+ auto extDeclAddrIter = cgf.localDeclMap.find(extDecl);
+ if (extDeclAddrIter != cgf.localDeclMap.end())
+ extDeclAlloca = extDeclAddrIter->second.getDefiningOp<cir::AllocaOp>();
+ }
+ 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;
+ }
+
+ CXXDestructorDecl *referenceTemporaryDtor = nullptr;
+ if (const clang::RecordType *rt = e->getType()
+ ->getBaseElementTypeUnsafe()
+ ->getAs<clang::RecordType>()) {
+ // Get the destructor for the reference temporary.
+ auto *classDecl = cast<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 = object.getPointer().getDefiningOp<cir::GlobalOp>()) {
+ // 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 f62be49ee7af3..a95b51d96b30a 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 0c9bc3823f2f5..3ed1e3093245b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -800,6 +800,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));
}
}
@@ -810,6 +812,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 f22ba2a5301ad..68d54bb966cdb 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);
/// ----------------------
@@ -1139,6 +1158,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
More information about the cfe-commits
mailing list