Skip to content

Commit a304e09

Browse files
authored
[CIR] Handle expression with cleanups (#151600)
This adds code to handle expressions with cleanup, including materializing a temporary object for the expression.
1 parent 605f652 commit a304e09

File tree

8 files changed

+243
-16
lines changed

8 files changed

+243
-16
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ struct MissingFeatures {
223223
static bool lowerAggregateLoadStore() { return false; }
224224
static bool lowerModeOptLevel() { return false; }
225225
static bool maybeHandleStaticInExternC() { return false; }
226+
static bool mergeAllConstants() { return false; }
226227
static bool metaDataNode() { return false; }
227228
static bool moduleNameHash() { return false; }
228229
static bool msabi() { return false; }

clang/lib/CIR/CodeGen/Address.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,17 @@ class Address {
101101
}
102102

103103
clang::CharUnits getAlignment() const { return alignment; }
104+
105+
/// Get the operation which defines this address.
106+
mlir::Operation *getDefiningOp() const {
107+
if (!isValid())
108+
return nullptr;
109+
return getPointer().getDefiningOp();
110+
}
111+
112+
template <typename OpTy> OpTy getDefiningOp() const {
113+
return mlir::dyn_cast_or_null<OpTy>(getDefiningOp());
114+
}
104115
};
105116

106117
} // namespace clang::CIRGen

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,27 @@ void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs,
653653
assert(!cir::MissingFeatures::sanitizers());
654654
}
655655

656+
namespace {
657+
struct DestroyObject final : EHScopeStack::Cleanup {
658+
DestroyObject(Address addr, QualType type,
659+
CIRGenFunction::Destroyer *destroyer)
660+
: addr(addr), type(type), destroyer(destroyer) {}
661+
662+
Address addr;
663+
QualType type;
664+
CIRGenFunction::Destroyer *destroyer;
665+
666+
void emit(CIRGenFunction &cgf) override {
667+
cgf.emitDestroy(addr, type, destroyer);
668+
}
669+
};
670+
} // namespace
671+
672+
void CIRGenFunction::pushDestroy(CleanupKind cleanupKind, Address addr,
673+
QualType type, Destroyer *destroyer) {
674+
pushFullExprCleanup<DestroyObject>(cleanupKind, addr, type, destroyer);
675+
}
676+
656677
/// Destroys all the elements of the given array, beginning from last to first.
657678
/// The array cannot be zero-length.
658679
///
@@ -740,22 +761,6 @@ CIRGenFunction::getDestroyer(QualType::DestructionKind kind) {
740761
llvm_unreachable("Unknown DestructionKind");
741762
}
742763

743-
namespace {
744-
struct DestroyObject final : EHScopeStack::Cleanup {
745-
DestroyObject(Address addr, QualType type,
746-
CIRGenFunction::Destroyer *destroyer)
747-
: addr(addr), type(type), destroyer(destroyer) {}
748-
749-
Address addr;
750-
QualType type;
751-
CIRGenFunction::Destroyer *destroyer;
752-
753-
void emit(CIRGenFunction &cgf) override {
754-
cgf.emitDestroy(addr, type, destroyer);
755-
}
756-
};
757-
} // namespace
758-
759764
/// Enter a destroy cleanup for the given local variable.
760765
void CIRGenFunction::emitAutoVarTypeCleanup(
761766
const CIRGenFunction::AutoVarEmission &emission,

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,151 @@ void CIRGenFunction::emitAnyExprToMem(const Expr *e, Address ___location,
11051105
llvm_unreachable("bad evaluation kind");
11061106
}
11071107

1108+
static Address createReferenceTemporary(CIRGenFunction &cgf,
1109+
const MaterializeTemporaryExpr *m,
1110+
const Expr *inner) {
1111+
// TODO(cir): cgf.getTargetHooks();
1112+
switch (m->getStorageDuration()) {
1113+
case SD_FullExpression:
1114+
case SD_Automatic: {
1115+
QualType ty = inner->getType();
1116+
1117+
assert(!cir::MissingFeatures::mergeAllConstants());
1118+
1119+
// The temporary memory should be created in the same scope as the extending
1120+
// declaration of the temporary materialization expression.
1121+
cir::AllocaOp extDeclAlloca;
1122+
if (const ValueDecl *extDecl = m->getExtendingDecl()) {
1123+
auto extDeclAddrIter = cgf.localDeclMap.find(extDecl);
1124+
if (extDeclAddrIter != cgf.localDeclMap.end())
1125+
extDeclAlloca = extDeclAddrIter->second.getDefiningOp<cir::AllocaOp>();
1126+
}
1127+
mlir::OpBuilder::InsertPoint ip;
1128+
if (extDeclAlloca)
1129+
ip = {extDeclAlloca->getBlock(), extDeclAlloca->getIterator()};
1130+
return cgf.createMemTemp(ty, cgf.getLoc(m->getSourceRange()),
1131+
cgf.getCounterRefTmpAsString(), /*alloca=*/nullptr,
1132+
ip);
1133+
}
1134+
case SD_Thread:
1135+
case SD_Static: {
1136+
cgf.cgm.errorNYI(
1137+
m->getSourceRange(),
1138+
"createReferenceTemporary: static/thread storage duration");
1139+
return Address::invalid();
1140+
}
1141+
1142+
case SD_Dynamic:
1143+
llvm_unreachable("temporary can't have dynamic storage duration");
1144+
}
1145+
llvm_unreachable("unknown storage duration");
1146+
}
1147+
1148+
static void pushTemporaryCleanup(CIRGenFunction &cgf,
1149+
const MaterializeTemporaryExpr *m,
1150+
const Expr *e, Address referenceTemporary) {
1151+
// Objective-C++ ARC:
1152+
// If we are binding a reference to a temporary that has ownership, we
1153+
// need to perform retain/release operations on the temporary.
1154+
//
1155+
// FIXME(ogcg): This should be looking at e, not m.
1156+
if (m->getType().getObjCLifetime()) {
1157+
cgf.cgm.errorNYI(e->getSourceRange(), "pushTemporaryCleanup: ObjCLifetime");
1158+
return;
1159+
}
1160+
1161+
CXXDestructorDecl *referenceTemporaryDtor = nullptr;
1162+
if (const clang::RecordType *rt = e->getType()
1163+
->getBaseElementTypeUnsafe()
1164+
->getAs<clang::RecordType>()) {
1165+
// Get the destructor for the reference temporary.
1166+
auto *classDecl = cast<CXXRecordDecl>(rt->getDecl());
1167+
if (!classDecl->hasTrivialDestructor())
1168+
referenceTemporaryDtor = classDecl->getDestructor();
1169+
}
1170+
1171+
if (!referenceTemporaryDtor)
1172+
return;
1173+
1174+
// Call the destructor for the temporary.
1175+
switch (m->getStorageDuration()) {
1176+
case SD_Static:
1177+
case SD_Thread:
1178+
cgf.cgm.errorNYI(e->getSourceRange(),
1179+
"pushTemporaryCleanup: static/thread storage duration");
1180+
return;
1181+
1182+
case SD_FullExpression:
1183+
cgf.pushDestroy(NormalAndEHCleanup, referenceTemporary, e->getType(),
1184+
CIRGenFunction::destroyCXXObject);
1185+
break;
1186+
1187+
case SD_Automatic:
1188+
cgf.cgm.errorNYI(e->getSourceRange(),
1189+
"pushTemporaryCleanup: automatic storage duration");
1190+
break;
1191+
1192+
case SD_Dynamic:
1193+
llvm_unreachable("temporary cannot have dynamic storage duration");
1194+
}
1195+
}
1196+
1197+
LValue CIRGenFunction::emitMaterializeTemporaryExpr(
1198+
const MaterializeTemporaryExpr *m) {
1199+
const Expr *e = m->getSubExpr();
1200+
1201+
assert((!m->getExtendingDecl() || !isa<VarDecl>(m->getExtendingDecl()) ||
1202+
!cast<VarDecl>(m->getExtendingDecl())->isARCPseudoStrong()) &&
1203+
"Reference should never be pseudo-strong!");
1204+
1205+
// FIXME: ideally this would use emitAnyExprToMem, however, we cannot do so
1206+
// as that will cause the lifetime adjustment to be lost for ARC
1207+
auto ownership = m->getType().getObjCLifetime();
1208+
if (ownership != Qualifiers::OCL_None &&
1209+
ownership != Qualifiers::OCL_ExplicitNone) {
1210+
cgm.errorNYI(e->getSourceRange(),
1211+
"emitMaterializeTemporaryExpr: ObjCLifetime");
1212+
return {};
1213+
}
1214+
1215+
SmallVector<const Expr *, 2> commaLHSs;
1216+
SmallVector<SubobjectAdjustment, 2> adjustments;
1217+
e = e->skipRValueSubobjectAdjustments(commaLHSs, adjustments);
1218+
1219+
for (const Expr *ignored : commaLHSs)
1220+
emitIgnoredExpr(ignored);
1221+
1222+
if (isa<OpaqueValueExpr>(e)) {
1223+
cgm.errorNYI(e->getSourceRange(),
1224+
"emitMaterializeTemporaryExpr: OpaqueValueExpr");
1225+
return {};
1226+
}
1227+
1228+
// Create and initialize the reference temporary.
1229+
Address object = createReferenceTemporary(*this, m, e);
1230+
1231+
if (auto var = object.getPointer().getDefiningOp<cir::GlobalOp>()) {
1232+
// TODO(cir): add something akin to stripPointerCasts() to ptr above
1233+
cgm.errorNYI(e->getSourceRange(), "emitMaterializeTemporaryExpr: GlobalOp");
1234+
return {};
1235+
} else {
1236+
assert(!cir::MissingFeatures::emitLifetimeMarkers());
1237+
emitAnyExprToMem(e, object, Qualifiers(), /*isInitializer=*/true);
1238+
}
1239+
pushTemporaryCleanup(*this, m, e, object);
1240+
1241+
// Perform derived-to-base casts and/or field accesses, to get from the
1242+
// temporary object we created (and, potentially, for which we extended
1243+
// the lifetime) to the subobject we're binding the reference to.
1244+
if (!adjustments.empty()) {
1245+
cgm.errorNYI(e->getSourceRange(),
1246+
"emitMaterializeTemporaryExpr: Adjustments");
1247+
return {};
1248+
}
1249+
1250+
return makeAddrLValue(object, m->getType(), AlignmentSource::Decl);
1251+
}
1252+
11081253
LValue CIRGenFunction::emitCompoundLiteralLValue(const CompoundLiteralExpr *e) {
11091254
if (e->isFileScope()) {
11101255
cgm.errorNYI(e->getSourceRange(), "emitCompoundLiteralLValue: FileScope");

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
626626

627627
mlir::Value VisitCXXThisExpr(CXXThisExpr *te) { return cgf.loadCXXThis(); }
628628

629+
mlir::Value VisitExprWithCleanups(ExprWithCleanups *e);
629630
mlir::Value VisitCXXNewExpr(const CXXNewExpr *e) {
630631
return cgf.emitCXXNewExpr(e);
631632
}
@@ -1217,6 +1218,29 @@ mlir::Value ScalarExprEmitter::emitCompoundAssign(
12171218
return emitLoadOfLValue(lhs, e->getExprLoc());
12181219
}
12191220

1221+
mlir::Value ScalarExprEmitter::VisitExprWithCleanups(ExprWithCleanups *e) {
1222+
mlir::Location scopeLoc = cgf.getLoc(e->getSourceRange());
1223+
mlir::OpBuilder &builder = cgf.builder;
1224+
1225+
auto scope = cir::ScopeOp::create(
1226+
builder, scopeLoc,
1227+
/*scopeBuilder=*/
1228+
[&](mlir::OpBuilder &b, mlir::Type &yieldTy, mlir::Location loc) {
1229+
CIRGenFunction::LexicalScope lexScope{cgf, loc,
1230+
builder.getInsertionBlock()};
1231+
mlir::Value scopeYieldVal = Visit(e->getSubExpr());
1232+
if (scopeYieldVal) {
1233+
// Defend against dominance problems caused by jumps out of expression
1234+
// evaluation through the shared cleanup block.
1235+
lexScope.forceCleanup();
1236+
cir::YieldOp::create(builder, loc, scopeYieldVal);
1237+
yieldTy = scopeYieldVal.getType();
1238+
}
1239+
});
1240+
1241+
return scope.getNumResults() > 0 ? scope->getResult(0) : nullptr;
1242+
}
1243+
12201244
} // namespace
12211245

12221246
LValue

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,8 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
800800
case Expr::CXXDynamicCastExprClass:
801801
case Expr::ImplicitCastExprClass:
802802
return emitCastLValue(cast<CastExpr>(e));
803+
case Expr::MaterializeTemporaryExprClass:
804+
return emitMaterializeTemporaryExpr(cast<MaterializeTemporaryExpr>(e));
803805
}
804806
}
805807

@@ -810,6 +812,10 @@ static std::string getVersionedTmpName(llvm::StringRef name, unsigned cnt) {
810812
return std::string(out.str());
811813
}
812814

815+
std::string CIRGenFunction::getCounterRefTmpAsString() {
816+
return getVersionedTmpName("ref.tmp", counterRefTmp++);
817+
}
818+
813819
std::string CIRGenFunction::getCounterAggTmpAsString() {
814820
return getVersionedTmpName("agg.tmp", counterAggTmp++);
815821
}

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,9 @@ class CIRGenFunction : public CIRGenTypeCache {
325325
};
326326

327327
/// Hold counters for incrementally naming temporaries
328+
unsigned counterRefTmp = 0;
328329
unsigned counterAggTmp = 0;
330+
std::string getCounterRefTmpAsString();
329331
std::string getCounterAggTmpAsString();
330332

331333
/// Helpers to convert Clang's SourceLocation to a MLIR Location.
@@ -604,6 +606,19 @@ class CIRGenFunction : public CIRGenTypeCache {
604606
void popCleanupBlocks(size_t oldCleanupStackDepth);
605607
void popCleanupBlock();
606608

609+
/// Push a cleanup to be run at the end of the current full-expression. Safe
610+
/// against the possibility that we're currently inside a
611+
/// conditionally-evaluated expression.
612+
template <class T, class... As>
613+
void pushFullExprCleanup(CleanupKind kind, As... a) {
614+
// If we're not in a conditional branch, or if none of the
615+
// arguments requires saving, then use the unconditional cleanup.
616+
if (!isInConditionalBranch())
617+
return ehStack.pushCleanup<T>(kind, a...);
618+
619+
cgm.errorNYI("pushFullExprCleanup in conditional branch");
620+
}
621+
607622
/// Enters a new scope for capturing cleanups, all of which
608623
/// will be executed once the scope is exited.
609624
class RunCleanupsScope {
@@ -619,6 +634,7 @@ class CIRGenFunction : public CIRGenTypeCache {
619634
protected:
620635
CIRGenFunction &cgf;
621636

637+
public:
622638
/// Enter a new cleanup scope.
623639
explicit RunCleanupsScope(CIRGenFunction &cgf)
624640
: performCleanup(true), cgf(cgf) {
@@ -801,6 +817,9 @@ class CIRGenFunction : public CIRGenTypeCache {
801817

802818
static Destroyer destroyCXXObject;
803819

820+
void pushDestroy(CleanupKind kind, Address addr, QualType type,
821+
Destroyer *destroyer);
822+
804823
Destroyer *getDestroyer(clang::QualType::DestructionKind kind);
805824

806825
/// ----------------------
@@ -1139,6 +1158,8 @@ class CIRGenFunction : public CIRGenTypeCache {
11391158
const clang::FieldDecl *field,
11401159
llvm::StringRef fieldName);
11411160

1161+
LValue emitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *e);
1162+
11421163
LValue emitMemberExpr(const MemberExpr *e);
11431164

11441165
/// Given an expression with a pointer type, emit the value and compute our

clang/test/CIR/CodeGen/cleanup.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,17 @@ void test_cleanup_nested() {
8181
// CHECK: }
8282
// CHECK: cir.call @_ZN5StrukD1Ev(%[[OUTER]]) nothrow : (!cir.ptr<!rec_Struk>) -> ()
8383
// CHECK: cir.return
84+
85+
void use_ref(const Struk &);
86+
87+
void test_expr_with_cleanup() {
88+
use_ref(Struk{});
89+
}
90+
91+
// CHECK: cir.func{{.*}} @_Z22test_expr_with_cleanupv()
92+
// CHECK: cir.scope {
93+
// CHECK: %[[S:.*]] = cir.alloca !rec_Struk, !cir.ptr<!rec_Struk>
94+
// CHECK: cir.call @_Z7use_refRK5Struk(%[[S]])
95+
// CHECK: cir.call @_ZN5StrukD1Ev(%[[S]]) nothrow : (!cir.ptr<!rec_Struk>) -> ()
96+
// CHECK: }
97+
// CHECK: cir.return

0 commit comments

Comments
 (0)