Skip to content

Commit d90efb8

Browse files
committed
[CIR] Handle expression with cleanups
This adds code to handle expressions with cleanup, including materializing a temporary object for the expression.
1 parent 254b90f commit d90efb8

File tree

8 files changed

+242
-16
lines changed

8 files changed

+242
-16
lines changed

clang/include/clang/CIR/MissingFeatures.h

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

clang/lib/CIR/CodeGen/Address.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@ 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+
}
104111
};
105112

106113
} // namespace clang::CIRGen

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

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

652+
namespace {
653+
struct DestroyObject final : EHScopeStack::Cleanup {
654+
DestroyObject(Address addr, QualType type,
655+
CIRGenFunction::Destroyer *destroyer)
656+
: addr(addr), type(type), destroyer(destroyer) {}
657+
658+
Address addr;
659+
QualType type;
660+
CIRGenFunction::Destroyer *destroyer;
661+
662+
void emit(CIRGenFunction &cgf) override {
663+
cgf.emitDestroy(addr, type, destroyer);
664+
}
665+
};
666+
} // namespace
667+
668+
void CIRGenFunction::pushDestroy(CleanupKind cleanupKind, Address addr,
669+
QualType type, Destroyer *destroyer) {
670+
pushFullExprCleanup<DestroyObject>(cleanupKind, addr, type, destroyer);
671+
}
672+
652673
/// Destroys all the elements of the given array, beginning from last to first.
653674
/// The array cannot be zero-length.
654675
///
@@ -736,22 +757,6 @@ CIRGenFunction::getDestroyer(QualType::DestructionKind kind) {
736757
llvm_unreachable("Unknown DestructionKind");
737758
}
738759

739-
namespace {
740-
struct DestroyObject final : EHScopeStack::Cleanup {
741-
DestroyObject(Address addr, QualType type,
742-
CIRGenFunction::Destroyer *destroyer)
743-
: addr(addr), type(type), destroyer(destroyer) {}
744-
745-
Address addr;
746-
QualType type;
747-
CIRGenFunction::Destroyer *destroyer;
748-
749-
void emit(CIRGenFunction &cgf) override {
750-
cgf.emitDestroy(addr, type, destroyer);
751-
}
752-
};
753-
} // namespace
754-
755760
/// Enter a destroy cleanup for the given local variable.
756761
void CIRGenFunction::emitAutoVarTypeCleanup(
757762
const CIRGenFunction::AutoVarEmission &emission,

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,154 @@ void CIRGenFunction::emitAnyExprToMem(const Expr *e, Address location,
10961096
llvm_unreachable("bad evaluation kind");
10971097
}
10981098

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

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

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

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
/// ----------------------
@@ -1136,6 +1155,8 @@ class CIRGenFunction : public CIRGenTypeCache {
11361155
const clang::FieldDecl *field,
11371156
llvm::StringRef fieldName);
11381157

1158+
LValue emitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *e);
1159+
11391160
LValue emitMemberExpr(const MemberExpr *e);
11401161

11411162
/// 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)