[CIR] Implement cleanups for temporaries with automatic duration (#189754)
This implements handling for cleanup of temporary variables with automatic storage duration. This is a simplified implementation that doesn't yet handle the possibility of exceptions being thrown within this cleanup scope or the cleanup scope being inside a conditional operation. Support for those cases will be added later.
This commit is contained in:
parent
62bbe3fffc
commit
5b56352757
@ -542,3 +542,18 @@ void CIRGenFunction::popCleanupBlocks(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pops cleanup blocks until the given savepoint is reached, then add the
|
||||
/// cleanups from the given savepoint in the lifetime-extended cleanups stack.
|
||||
void CIRGenFunction::popCleanupBlocks(
|
||||
EHScopeStack::stable_iterator oldCleanupStackDepth,
|
||||
size_t oldLifetimeExtendedSize, ArrayRef<mlir::Value *> valuesToReload) {
|
||||
popCleanupBlocks(oldCleanupStackDepth, valuesToReload);
|
||||
|
||||
// Promote deferred lifetime-extended cleanups onto the EH scope stack.
|
||||
for (const LifetimeExtendedCleanupEntry &cleanup : llvm::make_range(
|
||||
lifetimeExtendedCleanupStack.begin() + oldLifetimeExtendedSize,
|
||||
lifetimeExtendedCleanupStack.end()))
|
||||
pushLifetimeExtendedCleanupToEHStack(cleanup);
|
||||
lifetimeExtendedCleanupStack.truncate(oldLifetimeExtendedSize);
|
||||
}
|
||||
|
||||
@ -1007,6 +1007,37 @@ void CIRGenFunction::pushDestroy(CleanupKind cleanupKind, Address addr,
|
||||
pushFullExprCleanup<DestroyObject>(cleanupKind, addr, type, destroyer);
|
||||
}
|
||||
|
||||
void CIRGenFunction::pushLifetimeExtendedDestroy(CleanupKind cleanupKind,
|
||||
Address addr, QualType type,
|
||||
Destroyer *destroyer,
|
||||
bool useEHCleanupForArray) {
|
||||
if (isInConditionalBranch()) {
|
||||
cgm.errorNYI("conditional lifetime-extended destroy");
|
||||
return;
|
||||
}
|
||||
|
||||
// Classic codegen also uses pushDestroyAndDeferDeactivation here to push an
|
||||
// EH cleanup that protects the temporary during the rest of the full
|
||||
// expression, then deactivates it when the full expression ends. We don't
|
||||
// have deferred deactivation yet, so we only queue the lifetime-extended
|
||||
// cleanup below. When deferred deactivation is implemented, add the
|
||||
// pushDestroyAndDeferDeactivation call here.
|
||||
if (getLangOpts().Exceptions) {
|
||||
cgm.errorNYI("lifetime-extended cleanup with exceptions enabled");
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!cir::MissingFeatures::useEHCleanupForArray());
|
||||
|
||||
pushCleanupAfterFullExpr(cleanupKind, addr, type, destroyer);
|
||||
}
|
||||
|
||||
void CIRGenFunction::pushLifetimeExtendedCleanupToEHStack(
|
||||
const LifetimeExtendedCleanupEntry &entry) {
|
||||
ehStack.pushCleanup<DestroyObject>(entry.kind, entry.addr, entry.type,
|
||||
entry.destroyer);
|
||||
}
|
||||
|
||||
/// Destroys all the elements of the given array, beginning from last to first.
|
||||
/// The array cannot be zero-length.
|
||||
///
|
||||
|
||||
@ -1719,8 +1719,9 @@ static void pushTemporaryCleanup(CIRGenFunction &cgf,
|
||||
break;
|
||||
|
||||
case SD_Automatic:
|
||||
cgf.cgm.errorNYI(e->getSourceRange(),
|
||||
"pushTemporaryCleanup: automatic storage duration");
|
||||
cgf.pushLifetimeExtendedDestroy(
|
||||
NormalAndEHCleanup, referenceTemporary, e->getType(),
|
||||
CIRGenFunction::destroyCXXObject, cgf.getLangOpts().Exceptions);
|
||||
break;
|
||||
|
||||
case SD_Dynamic:
|
||||
|
||||
@ -92,6 +92,25 @@ public:
|
||||
/// Tracks function scope overall cleanup handling.
|
||||
EHScopeStack ehStack;
|
||||
|
||||
typedef void Destroyer(CIRGenFunction &cgf, Address addr, QualType ty);
|
||||
|
||||
/// An entry in the lifetime-extended cleanup stack. Each entry represents a
|
||||
/// cleanup that was deferred past a full-expression boundary (e.g.,
|
||||
/// destroying a temporary bound to a local reference). When the enclosing
|
||||
/// scope exits, these entries are promoted to the EH scope stack.
|
||||
///
|
||||
/// Currently only DestroyObject cleanups are lifetime-extended. When other
|
||||
/// cleanup types are needed (e.g., CallLifetimeEnd), this struct can be
|
||||
/// extended with a std::variant of cleanup data types.
|
||||
struct LifetimeExtendedCleanupEntry {
|
||||
CleanupKind kind;
|
||||
Address addr;
|
||||
QualType type;
|
||||
Destroyer *destroyer;
|
||||
};
|
||||
|
||||
llvm::SmallVector<LifetimeExtendedCleanupEntry> lifetimeExtendedCleanupStack;
|
||||
|
||||
GlobalDecl curSEHParent;
|
||||
|
||||
/// A mapping from NRVO variables to the flags used to indicate
|
||||
@ -970,6 +989,12 @@ public:
|
||||
/// that have been added.
|
||||
void popCleanupBlocks(EHScopeStack::stable_iterator oldCleanupStackDepth,
|
||||
ArrayRef<mlir::Value *> valuesToReload = {});
|
||||
|
||||
/// Pops cleanup blocks until the given savepoint is reached, then adds the
|
||||
/// cleanups from the given savepoint in the lifetime-extended cleanups stack.
|
||||
void popCleanupBlocks(EHScopeStack::stable_iterator oldCleanupStackDepth,
|
||||
size_t oldLifetimeExtendedSize,
|
||||
ArrayRef<mlir::Value *> valuesToReload = {});
|
||||
void popCleanupBlock();
|
||||
|
||||
void terminateStructuredRegionBody(mlir::Region &r, mlir::Location loc);
|
||||
@ -997,10 +1022,19 @@ public:
|
||||
cgm.errorNYI("pushFullExprCleanup in conditional branch");
|
||||
}
|
||||
|
||||
/// Queue a cleanup to be pushed after finishing the current full-expression.
|
||||
/// When the enclosing RunCleanupsScope exits, popCleanupBlocks promotes these
|
||||
/// entries onto the EH scope stack for the enclosing scope.
|
||||
void pushCleanupAfterFullExpr(CleanupKind kind, Address addr, QualType type,
|
||||
Destroyer *destroyer) {
|
||||
lifetimeExtendedCleanupStack.push_back({kind, addr, type, destroyer});
|
||||
}
|
||||
|
||||
/// Enters a new scope for capturing cleanups, all of which
|
||||
/// will be executed once the scope is exited.
|
||||
class RunCleanupsScope {
|
||||
EHScopeStack::stable_iterator cleanupStackDepth, oldCleanupStackDepth;
|
||||
size_t lifetimeExtendedCleanupStackSize;
|
||||
|
||||
protected:
|
||||
bool performCleanup;
|
||||
@ -1018,6 +1052,8 @@ public:
|
||||
explicit RunCleanupsScope(CIRGenFunction &cgf)
|
||||
: performCleanup(true), cgf(cgf) {
|
||||
cleanupStackDepth = cgf.ehStack.stable_begin();
|
||||
lifetimeExtendedCleanupStackSize =
|
||||
cgf.lifetimeExtendedCleanupStack.size();
|
||||
oldDidCallStackSave = cgf.didCallStackSave;
|
||||
cgf.didCallStackSave = false;
|
||||
oldCleanupStackDepth = cgf.currentCleanupStackDepth;
|
||||
@ -1035,7 +1071,8 @@ public:
|
||||
void forceCleanup(ArrayRef<mlir::Value *> valuesToReload = {}) {
|
||||
assert(performCleanup && "Already forced cleanup");
|
||||
cgf.didCallStackSave = oldDidCallStackSave;
|
||||
cgf.popCleanupBlocks(cleanupStackDepth, valuesToReload);
|
||||
cgf.popCleanupBlocks(cleanupStackDepth, lifetimeExtendedCleanupStackSize,
|
||||
valuesToReload);
|
||||
performCleanup = false;
|
||||
cgf.currentCleanupStackDepth = oldCleanupStackDepth;
|
||||
}
|
||||
@ -1247,8 +1284,6 @@ public:
|
||||
|
||||
LexicalScope *curLexScope = nullptr;
|
||||
|
||||
typedef void Destroyer(CIRGenFunction &cgf, Address addr, QualType ty);
|
||||
|
||||
static Destroyer destroyCXXObject;
|
||||
|
||||
void pushDestroy(QualType::DestructionKind dtorKind, Address addr,
|
||||
@ -1257,6 +1292,15 @@ public:
|
||||
void pushDestroy(CleanupKind kind, Address addr, QualType type,
|
||||
Destroyer *destroyer);
|
||||
|
||||
void pushLifetimeExtendedDestroy(CleanupKind kind, Address addr,
|
||||
QualType type, Destroyer *destroyer,
|
||||
bool useEHCleanupForArray);
|
||||
|
||||
/// Promote a single lifetime-extended cleanup entry onto the EH scope stack.
|
||||
/// Defined in CIRGenDecl.cpp where the concrete cleanup types are visible.
|
||||
void pushLifetimeExtendedCleanupToEHStack(
|
||||
const LifetimeExtendedCleanupEntry &entry);
|
||||
|
||||
Destroyer *getDestroyer(clang::QualType::DestructionKind kind);
|
||||
|
||||
/// Start generating a thunk function.
|
||||
|
||||
@ -206,6 +206,21 @@ public:
|
||||
return new (buffer) T(n, a...);
|
||||
}
|
||||
|
||||
/// Push a cleanup by copying a serialized cleanup object from the
|
||||
/// LifetimeExtendedCleanupStack onto the EH scope stack. This is used when
|
||||
/// a full-expression's RunCleanupsScope exits: cleanups that were deferred
|
||||
/// for lifetime extension (e.g. destroying a temporary bound to a local
|
||||
/// reference) are promoted from the byte buffer to the enclosing scope's
|
||||
/// EH stack so they run when that scope ends.
|
||||
///
|
||||
/// The memcpy is safe because Cleanup subclasses are required to be POD-like
|
||||
/// (see the Cleanup class comment), and the vtable pointer is part of the
|
||||
/// copied bytes, so the clone dispatches to the correct emit() override.
|
||||
void pushCopyOfCleanup(CleanupKind kind, const void *cleanup, size_t size) {
|
||||
void *buffer = pushCleanup(kind, size);
|
||||
std::memcpy(buffer, cleanup, size);
|
||||
}
|
||||
|
||||
void setCGF(CIRGenFunction *inCGF) { cgf = inCGF; }
|
||||
|
||||
/// Pops a cleanup scope off the stack. This is private to CIRGenCleanup.cpp.
|
||||
|
||||
@ -149,3 +149,18 @@ void complex_expr_with_cleanup_inside_cleanupscope() {
|
||||
// CHECK: }
|
||||
// CHECK: %[[RELOAD:.*]] = cir.load {{.*}} %[[TEMP_ADDR]] : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i>
|
||||
// CHECK: cir.store {{.*}} %[[RELOAD]], %[[RESULT]] : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>
|
||||
|
||||
void test_cleanup_with_automatic_storage_duration() {
|
||||
const Struk &ref = Struk{};
|
||||
}
|
||||
|
||||
// CHECK: cir.func{{.*}} @_Z44test_cleanup_with_automatic_storage_durationv()
|
||||
// CHECK: %[[REF_TMP:.*]] = cir.alloca !rec_Struk, !cir.ptr<!rec_Struk>, ["ref.tmp0"]
|
||||
// CHECK: %[[REF:.*]] = cir.alloca !cir.ptr<!rec_Struk>, !cir.ptr<!cir.ptr<!rec_Struk>>, ["ref", init, const]
|
||||
// CHECK: cir.cleanup.scope {
|
||||
// CHECK: cir.store{{.*}} %[[REF_TMP]], %[[REF]]
|
||||
// CHECK: cir.yield
|
||||
// CHECK: } cleanup normal {
|
||||
// CHECK: cir.call @_ZN5StrukD1Ev(%[[REF_TMP]]) nothrow
|
||||
// CHECK: cir.yield
|
||||
// CHECK: }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user