[clang] b6cd213 - [CIR] Simplify try-catch handling (#180857)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 13 10:40:57 PST 2026
Author: Andy Kaylor
Date: 2026-02-13T10:40:53-08:00
New Revision: b6cd2133d49c8b5e8375241cc2c101a1f1e960f8
URL: https://github.com/llvm/llvm-project/commit/b6cd2133d49c8b5e8375241cc2c101a1f1e960f8
DIFF: https://github.com/llvm/llvm-project/commit/b6cd2133d49c8b5e8375241cc2c101a1f1e960f8.diff
LOG: [CIR] Simplify try-catch handling (#180857)
With the new exception handling device, we no longer need to track the
location of potential catch blocks, and so we no longer need to
represent catch handlers on the EH stack. This allows us to
significantly simplify the generation of try-catch blocks.
This change emits catch blocks directly when visiting the try operation
and removes unnecessary EH management code.
This is not an exhaustive cleanup of the EH management code. Some things
that may appear to be obvious simplifications (such as removing the
union of bitfields in EHScope, and possibly making EHScopeStack just a
stack of EHCleanups) are deferred until we have a better idea of how
things like EH filters will be implemented.
This change breaks lowering of cir.try operations to LLVM IR in a
somewhat trivial way. Even before this PR, lowering of `cir.try` ops
with catch handlers was not implemented. However, we had one test case
where the only call in a try-region was marked with the `noexcept`
attribute. Previously, we did not emit catch handlers in that
circumstance. Now we do. As such, nothing that previously worked is
broken by this patch, but there is one test that would now fail. I
handled this by removing the RUN line for lowering this test to LLVM IR.
I left the checks so this will be easy to re-enable when proper try-op
lowering is implemented.
Added:
Modified:
clang/lib/CIR/CodeGen/CIRGenCall.cpp
clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
clang/lib/CIR/CodeGen/CIRGenCleanup.h
clang/lib/CIR/CodeGen/CIRGenException.cpp
clang/lib/CIR/CodeGen/CIRGenFunction.h
clang/lib/CIR/CodeGen/EHScopeStack.h
clang/test/CIR/CodeGen/try-catch.cpp
Removed:
################################################################################
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 7a8bd544593c7..2039b439c783c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -942,42 +942,6 @@ static cir::CIRCallOpInterface emitCallLikeOp(
assert(!cir::MissingFeatures::opCallSurroundingTry());
- if (isInvoke) {
- // This call may throw and requires catch and/or cleanup handling.
- // If this call does not appear within the `try` region of an existing
- // TryOp, we must create a synthetic TryOp to contain the call. This
- // happens when a call that may throw appears within a cleanup
- // scope.
-
- // In OG, we build the landing pad for this scope. In CIR, we emit a
- // synthetic cir.try because this didn't come from code generating from a
- // try/catch in C++.
- assert(cgf.curLexScope && "expected scope");
- cir::TryOp tryOp = cgf.curLexScope->getClosestTryParent();
- if (!tryOp) {
- cgf.cgm.errorNYI(
- "emitCallLikeOp: call does not have an associated cir.try");
- return {};
- }
-
- if (tryOp.getSynthetic()) {
- cgf.cgm.errorNYI("emitCallLikeOp: tryOp synthetic");
- return {};
- }
-
- cir::CallOp callOpWithExceptions;
- if (indirectFuncTy) {
- cgf.cgm.errorNYI("emitCallLikeOp: indirect function type");
- return {};
- }
-
- callOpWithExceptions =
- builder.createCallOp(callLoc, directFuncOp, cirCallArgs);
-
- cgf.populateCatchHandlersIfRequired(tryOp);
- return callOpWithExceptions;
- }
-
assert(builder.getInsertionBlock() && "expected valid basic block");
cir::CallOp op;
diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
index 01fd9e1004bc3..cc841c6474537 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
@@ -214,14 +214,6 @@ bool EHScopeStack::requiresCatchOrCleanup() const {
return false;
}
-EHCatchScope *EHScopeStack::pushCatch(unsigned numHandlers) {
- char *buffer = allocate(EHCatchScope::getSizeForNumHandlers(numHandlers));
- EHCatchScope *scope =
- new (buffer) EHCatchScope(numHandlers, innermostEHScope);
- innermostEHScope = stable_begin();
- return scope;
-}
-
static void emitCleanup(CIRGenFunction &cgf, EHScopeStack::Cleanup *cleanup,
EHScopeStack::Cleanup::Flags flags) {
// Ask the cleanup to emit itself.
diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.h b/clang/lib/CIR/CodeGen/CIRGenCleanup.h
index 6ad90a9072968..a39eb02cf87d3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCleanup.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.h
@@ -42,12 +42,6 @@ class EHScope {
bool scopeMayThrow;
protected:
- class CatchBitFields {
- friend class EHCatchScope;
- unsigned : NumCommonBits;
- unsigned numHandlers : 32 - NumCommonBits;
- };
-
class CleanupBitFields {
friend class EHCleanupScope;
unsigned : NumCommonBits;
@@ -77,12 +71,11 @@ class EHScope {
union {
CommonBitFields commonBits;
- CatchBitFields catchBits;
CleanupBitFields cleanupBits;
};
public:
- enum Kind { Cleanup, Catch, Terminate, Filter };
+ enum Kind { Cleanup, Terminate, Filter };
EHScope(Kind kind, EHScopeStack::stable_iterator enclosingEHScope)
: enclosingEHScope(enclosingEHScope) {
@@ -105,86 +98,6 @@ class EHScope {
}
};
-/// A scope which attempts to handle some, possibly all, types of
-/// exceptions.
-///
-/// Objective C \@finally blocks are represented using a cleanup scope
-/// after the catch scope.
-
-class EHCatchScope : public EHScope {
- // In effect, we have a flexible array member
- // Handler Handlers[0];
- // But that's only standard in C99, not C++, so we have to do
- // annoying pointer arithmetic instead.
-
-public:
- struct Handler {
- /// A type info value, or null MLIR attribute for a catch-all
- CatchTypeInfo type;
-
- /// The catch handler for this type.
- mlir::Region *region;
-
- /// The catch handler stmt.
- const CXXCatchStmt *stmt;
-
- bool isCatchAll() const { return type.rtti == nullptr; }
- };
-
-private:
- friend class EHScopeStack;
-
- Handler *getHandlers() { return reinterpret_cast<Handler *>(this + 1); }
-
- const Handler *getHandlers() const {
- return reinterpret_cast<const Handler *>(this + 1);
- }
-
-public:
- static size_t getSizeForNumHandlers(unsigned n) {
- return sizeof(EHCatchScope) + n * sizeof(Handler);
- }
-
- EHCatchScope(unsigned numHandlers,
- EHScopeStack::stable_iterator enclosingEHScope)
- : EHScope(Catch, enclosingEHScope) {
- catchBits.numHandlers = numHandlers;
- assert(catchBits.numHandlers == numHandlers && "NumHandlers overflow?");
- }
-
- unsigned getNumHandlers() const { return catchBits.numHandlers; }
-
- void setHandler(unsigned i, CatchTypeInfo type, mlir::Region *region,
- const CXXCatchStmt *stmt) {
- assert(i < getNumHandlers());
- Handler *handler = &getHandlers()[i];
- handler->type = type;
- handler->region = region;
- handler->stmt = stmt;
- }
-
- const Handler &getHandler(unsigned i) const {
- assert(i < getNumHandlers());
- return getHandlers()[i];
- }
-
- // Clear all handler blocks.
- // FIXME: it's better to always call clearHandlerBlocks in DTOR and have a
- // 'takeHandler' or some such function which removes ownership from the
- // EHCatchScope object if the handlers should live longer than EHCatchScope.
- void clearHandlerBlocks() {
- // The blocks are owned by TryOp, nothing to delete.
- }
-
- using iterator = const Handler *;
- iterator begin() const { return getHandlers(); }
- iterator end() const { return getHandlers() + getNumHandlers(); }
-
- static bool classof(const EHScope *scope) {
- return scope->getKind() == Catch;
- }
-};
-
/// A cleanup scope which generates the cleanup blocks lazily.
class alignas(EHScopeStack::ScopeStackAlignment) EHCleanupScope
: public EHScope {
@@ -280,17 +193,12 @@ class EHScopeStack::iterator {
iterator &operator++() {
size_t size;
switch (get()->getKind()) {
- case EHScope::Catch:
- size = EHCatchScope::getSizeForNumHandlers(
- static_cast<const EHCatchScope *>(get())->getNumHandlers());
- break;
-
case EHScope::Filter:
llvm_unreachable("EHScopeStack::iterator Filter");
break;
case EHScope::Cleanup:
- llvm_unreachable("EHScopeStack::iterator Cleanup");
+ size = static_cast<const EHCleanupScope *>(get())->getAllocatedSize();
break;
case EHScope::Terminate:
@@ -321,14 +229,6 @@ EHScopeStack::find(stable_iterator savePoint) const {
return iterator(endOfBuffer - savePoint.size);
}
-inline void EHScopeStack::popCatch() {
- assert(!empty() && "popping exception stack when not empty");
-
- EHCatchScope &scope = llvm::cast<EHCatchScope>(*begin());
- innermostEHScope = scope.getEnclosingEHScope();
- deallocate(EHCatchScope::getSizeForNumHandlers(scope.getNumHandlers()));
-}
-
/// The exceptions personality for a function.
struct EHPersonality {
const char *personalityFn = nullptr;
diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp
index fbb5bcf797c58..2d1d5e5e65f61 100644
--- a/clang/lib/CIR/CodeGen/CIRGenException.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -243,27 +243,32 @@ void CIRGenFunction::emitAnyExprToExn(const Expr *e, Address addr) {
assert(!cir::MissingFeatures::ehCleanupScope());
}
-void CIRGenFunction::populateUnwindResumeBlock(bool isCleanup,
- cir::TryOp tryOp) {
- const EHPersonality &personality = EHPersonality::get(*this);
- // This can always be a call because we necessarily didn't find
- // anything on the EH stack which needs our help.
- const char *rethrowName = personality.catchallRethrowFn;
- if (rethrowName != nullptr && !isCleanup) {
- cgm.errorNYI("populateUnwindResumeBlock CatchallRethrowFn");
- return;
- }
-
- unsigned regionsNum = tryOp->getNumRegions();
- mlir::Region *unwindRegion = &tryOp->getRegion(regionsNum - 1);
- mlir::Block *unwindResumeBlock = &unwindRegion->front();
- if (!unwindResumeBlock->empty())
- return;
+void CIRGenFunction::addCatchHandlerAttr(
+ const CXXCatchStmt *catchStmt, SmallVector<mlir::Attribute> &handlerAttrs) {
+ mlir::Location catchLoc = getLoc(catchStmt->getBeginLoc());
+
+ if (catchStmt->getExceptionDecl()) {
+ // FIXME: Dropping the reference type on the type into makes it
+ // impossible to correctly implement catch-by-reference
+ // semantics for pointers. Unfortunately, this is what all
+ // existing compilers do, and it's not clear that the standard
+ // personality routine is capable of doing this right. See C++ DR 388:
+ // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#388
+ Qualifiers caughtTypeQuals;
+ QualType caughtType = cgm.getASTContext().getUnqualifiedArrayType(
+ catchStmt->getCaughtType().getNonReferenceType(), caughtTypeQuals);
+ if (caughtType->isObjCObjectPointerType()) {
+ cgm.errorNYI("addCatchHandlerAttr: caughtType ObjCObjectPointerType");
+ return;
+ }
- // Emit cir.resume into the unwind region last block
- mlir::OpBuilder::InsertionGuard guard(builder);
- builder.setInsertionPointToStart(unwindResumeBlock);
- cir::ResumeOp::create(builder, tryOp.getLoc());
+ CatchTypeInfo typeInfo = cgm.getCXXABI().getAddrOfCXXCatchHandlerType(
+ catchLoc, caughtType, catchStmt->getCaughtType());
+ handlerAttrs.push_back(typeInfo.rtti);
+ } else {
+ // No exception decl indicates '...', a catch-all.
+ handlerAttrs.push_back(cir::CatchAllAttr::get(&getMLIRContext()));
+ }
}
mlir::LogicalResult CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s) {
@@ -280,224 +285,92 @@ mlir::LogicalResult CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s) {
scopeIP = builder.saveInsertionPoint();
});
+ // Set personality function if not already set
+ auto funcOp = mlir::cast<cir::FuncOp>(curFn);
+ if (!funcOp.getPersonality())
+ funcOp.setPersonality(getPersonalityFn(cgm, EHPersonality::get(*this)));
+
mlir::OpBuilder::InsertionGuard guard(builder);
builder.restoreInsertionPoint(scopeIP);
- mlir::LogicalResult result = emitCXXTryStmtUnderScope(s);
- cir::YieldOp::create(builder, loc);
- return result;
-}
-mlir::LogicalResult
-CIRGenFunction::emitCXXTryStmtUnderScope(const CXXTryStmt &s) {
const llvm::Triple &t = getTarget().getTriple();
// If we encounter a try statement on in an OpenMP target region offloaded to
// a GPU, we treat it as a basic block.
const bool isTargetDevice =
(cgm.getLangOpts().OpenMPIsTargetDevice && (t.isNVPTX() || t.isAMDGCN()));
if (isTargetDevice) {
- cgm.errorNYI(
- "emitCXXTryStmtUnderScope: OpenMP target region offloaded to GPU");
+ cgm.errorNYI("emitCXXTryStmt: OpenMP target region offloaded to GPU");
return mlir::success();
}
- unsigned numHandlers = s.getNumHandlers();
mlir::Location tryLoc = getLoc(s.getBeginLoc());
- mlir::OpBuilder::InsertPoint beginInsertTryBody;
+ SmallVector<mlir::Attribute> handlerAttrs;
- bool hasCatchAll = false;
- for (unsigned i = 0; i != numHandlers; ++i) {
- hasCatchAll |= s.getHandler(i)->getExceptionDecl() == nullptr;
- if (hasCatchAll)
- break;
+ CIRGenFunction::LexicalScope tryBodyScope{*this, tryLoc,
+ builder.getInsertionBlock()};
+
+ if (getLangOpts().EHAsynch) {
+ cgm.errorNYI("enterCXXTryStmt: EHAsynch");
+ return mlir::failure();
}
- // Create the scope to represent only the C/C++ `try {}` part. However,
- // don't populate right away. Create regions for the catch handlers,
- // but don't emit the handler bodies yet. For now, only make sure the
- // scope returns the exception information.
+ // Create the try operation.
+ mlir::LogicalResult tryRes = mlir::success();
auto tryOp = cir::TryOp::create(
builder, tryLoc,
/*tryBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
- beginInsertTryBody = builder.saveInsertionPoint();
+ if (emitStmt(s.getTryBlock(), /*useCurrentScope=*/true).failed())
+ tryRes = mlir::failure();
+ cir::YieldOp::create(builder, loc);
},
/*handlersBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc,
mlir::OperationState &result) {
mlir::OpBuilder::InsertionGuard guard(b);
-
- // We create an extra region for an unwind catch handler in case the
- // catch-all handler doesn't exists
- unsigned numRegionsToCreate =
- hasCatchAll ? numHandlers : numHandlers + 1;
-
- for (unsigned i = 0; i != numRegionsToCreate; ++i) {
+ bool hasCatchAll = false;
+ unsigned numHandlers = s.getNumHandlers();
+ for (unsigned i = 0; i != numHandlers; ++i) {
+ const CXXCatchStmt *catchStmt = s.getHandler(i);
+ if (!catchStmt->getExceptionDecl())
+ hasCatchAll = true;
+ mlir::Region *region = result.addRegion();
+ builder.createBlock(region);
+ addCatchHandlerAttr(catchStmt, handlerAttrs);
+ }
+ if (!hasCatchAll) {
+ // Create unwind region.
mlir::Region *region = result.addRegion();
builder.createBlock(region);
+ cir::ResumeOp::create(builder, loc);
+ handlerAttrs.push_back(cir::UnwindAttr::get(&getMLIRContext()));
}
});
- // Finally emit the body for try/catch.
- {
- mlir::Location loc = tryOp.getLoc();
- mlir::OpBuilder::InsertionGuard guard(builder);
- builder.restoreInsertionPoint(beginInsertTryBody);
- CIRGenFunction::LexicalScope tryScope{*this, loc,
- builder.getInsertionBlock()};
-
- tryScope.setAsTry(tryOp);
-
- // Attach the basic blocks for the catch regions.
- enterCXXTryStmt(s, tryOp);
-
- // Emit the body for the `try {}` part.
- {
- mlir::OpBuilder::InsertionGuard guard(builder);
- CIRGenFunction::LexicalScope tryBodyScope{*this, loc,
- builder.getInsertionBlock()};
- if (emitStmt(s.getTryBlock(), /*useCurrentScope=*/true).failed())
- return mlir::failure();
- }
+ if (tryRes.failed())
+ return mlir::failure();
- // Emit catch clauses.
- exitCXXTryStmt(s);
- }
+ // Add final array of clauses into TryOp.
+ tryOp.setHandlerTypesAttr(
+ mlir::ArrayAttr::get(&getMLIRContext(), handlerAttrs));
- return mlir::success();
-}
-
-/// Emit the structure of the dispatch block for the given catch scope.
-/// It is an invariant that the dispatch block already exists.
-static void emitCatchDispatchBlock(CIRGenFunction &cgf,
- EHCatchScope &catchScope, cir::TryOp tryOp) {
- if (EHPersonality::get(cgf).isWasmPersonality()) {
- cgf.cgm.errorNYI("emitCatchDispatchBlock: WasmPersonality");
- return;
- }
-
- if (EHPersonality::get(cgf).usesFuncletPads()) {
- cgf.cgm.errorNYI("emitCatchDispatchBlock: usesFuncletPads");
- return;
- }
-
- assert(std::find_if(catchScope.begin(), catchScope.end(),
- [](const auto &handler) {
- return !handler.type.rtti && handler.type.flags != 0;
- }) == catchScope.end() &&
- "catch handler without type value or with not supported flags");
-
- // There was no catch all handler, populate th EH regions for the
- // enclosing scope.
- if (!std::prev(catchScope.end())->isCatchAll())
- cgf.populateEHCatchRegions(catchScope.getEnclosingEHScope(), tryOp);
-}
-
-void CIRGenFunction::enterCXXTryStmt(const CXXTryStmt &s, cir::TryOp tryOp,
- bool isFnTryBlock) {
+ // Emit the catch handler bodies. This has to be done after the try op is
+ // created and in place so that we can find the insertion point for the
+ // catch parameter alloca.
unsigned numHandlers = s.getNumHandlers();
- EHCatchScope *catchScope = ehStack.pushCatch(numHandlers);
for (unsigned i = 0; i != numHandlers; ++i) {
const CXXCatchStmt *catchStmt = s.getHandler(i);
mlir::Region *handler = &tryOp.getHandlerRegions()[i];
- if (catchStmt->getExceptionDecl()) {
- // FIXME: Dropping the reference type on the type into makes it
- // impossible to correctly implement catch-by-reference
- // semantics for pointers. Unfortunately, this is what all
- // existing compilers do, and it's not clear that the standard
- // personality routine is capable of doing this right. See C++ DR 388:
- // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#388
- Qualifiers caughtTypeQuals;
- QualType caughtType = cgm.getASTContext().getUnqualifiedArrayType(
- catchStmt->getCaughtType().getNonReferenceType(), caughtTypeQuals);
- if (caughtType->isObjCObjectPointerType()) {
- cgm.errorNYI("enterCXXTryStmt: caughtType ObjCObjectPointerType");
- return;
- }
-
- CatchTypeInfo typeInfo = cgm.getCXXABI().getAddrOfCXXCatchHandlerType(
- getLoc(catchStmt->getSourceRange()), caughtType,
- catchStmt->getCaughtType());
- catchScope->setHandler(i, typeInfo, handler, catchStmt);
- } else {
- // No exception decl indicates '...', a catch-all.
- catchScope->setHandler(i, cgm.getCXXABI().getCatchAllTypeInfo(), handler,
- s.getHandler(i));
- }
-
- // Under async exceptions, catch(...) needs to catch HW exception too
- // Mark scope with SehTryBegin as a SEH __try scope
- if (getLangOpts().EHAsynch) {
- cgm.errorNYI("enterCXXTryStmt: EHAsynch");
- return;
- }
- }
-}
-
-void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) {
- unsigned numHandlers = s.getNumHandlers();
- EHCatchScope &catchScope = cast<EHCatchScope>(*ehStack.begin());
- assert(catchScope.getNumHandlers() == numHandlers);
- cir::TryOp tryOp = curLexScope->getTry();
-
- // If the catch was not required, bail out now.
- if (!catchScope.mayThrow()) {
- catchScope.clearHandlerBlocks();
- ehStack.popCatch();
-
- // Drop all basic block from all catch regions.
- SmallVector<mlir::Block *> eraseBlocks;
- for (mlir::Region &handlerRegion : tryOp.getHandlerRegions()) {
- if (handlerRegion.empty())
- continue;
-
- for (mlir::Block &b : handlerRegion.getBlocks())
- eraseBlocks.push_back(&b);
- }
-
- for (mlir::Block *b : eraseBlocks)
- b->erase();
-
- tryOp.setHandlerTypesAttr({});
- return;
- }
-
- // Emit the structure of the EH dispatch for this catch.
- emitCatchDispatchBlock(*this, catchScope, tryOp);
-
- // Copy the handler blocks off before we pop the EH stack. Emitting
- // the handlers might scribble on this memory.
- SmallVector<EHCatchScope::Handler> handlers(catchScope.begin(),
- catchScope.begin() + numHandlers);
-
- ehStack.popCatch();
-
- // Determine if we need an implicit rethrow for all these catch handlers;
- // see the comment below.
- bool doImplicitRethrow =
- isFnTryBlock && isa<CXXDestructorDecl, CXXConstructorDecl>(curCodeDecl);
-
- // Wasm uses Windows-style EH instructions, but merges all catch clauses into
- // one big catchpad. So we save the old funclet pad here before we traverse
- // each catch handler.
- if (EHPersonality::get(*this).isWasmPersonality()) {
- cgm.errorNYI("exitCXXTryStmt: WASM personality");
- return;
- }
-
- bool hasCatchAll = false;
- for (auto &handler : llvm::reverse(handlers)) {
- hasCatchAll |= handler.isCatchAll();
- mlir::Region *catchRegion = handler.region;
- const CXXCatchStmt *catchStmt = handler.stmt;
mlir::OpBuilder::InsertionGuard guard(builder);
- builder.setInsertionPointToStart(&catchRegion->front());
+ builder.setInsertionPointToStart(&handler->front());
// Enter a cleanup scope, including the catch variable and the
// end-catch.
- RunCleanupsScope catchScope(*this);
+ RunCleanupsScope handlerScope(*this);
- // Initialize the catch variable and set up the cleanups.
+ // Initialize the catch variable.
+ // TODO(cir): Move this out of CXXABI.
assert(!cir::MissingFeatures::currentFuncletPad());
cgm.getCXXABI().emitBeginCatch(*this, catchStmt);
@@ -514,192 +387,13 @@ void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) {
// reaches the end of a handler of the function-try-block of a
// constructor or destructor.
- // It is important that we only do this on fallthrough and not on
- // return. Note that it's illegal to put a return in a
- // constructor function-try-block's catch handler (p14), so this
- // really only applies to destructors.
- if (doImplicitRethrow) {
- cgm.errorNYI("exitCXXTryStmt: doImplicitRethrow");
- return;
- }
+ // TODO(cir): Handle implicit rethrow?
// Fall out through the catch cleanups.
- catchScope.forceCleanup();
- }
-
- // Because in wasm we merge all catch clauses into one big catchpad, in case
- // none of the types in catch handlers matches after we test against each of
- // them, we should unwind to the next EH enclosing scope. We generate a call
- // to rethrow function here to do that.
- if (EHPersonality::get(*this).isWasmPersonality() && !hasCatchAll) {
- cgm.errorNYI("exitCXXTryStmt: WASM personality without catch all");
+ handlerScope.forceCleanup();
}
- assert(!cir::MissingFeatures::incrementProfileCounter());
-}
-
-void CIRGenFunction::populateCatchHandlers(cir::TryOp tryOp) {
- assert(ehStack.requiresCatchOrCleanup());
- assert(!cgm.getLangOpts().IgnoreExceptions &&
- "LandingPad should not be emitted when -fignore-exceptions are in "
- "effect.");
-
- EHScope &innermostEHScope = *ehStack.find(ehStack.getInnermostEHScope());
- switch (innermostEHScope.getKind()) {
- case EHScope::Terminate:
- cgm.errorNYI("populateCatchHandlers: terminate");
- return;
-
- case EHScope::Catch:
- case EHScope::Cleanup:
- case EHScope::Filter:
- // CIR does not cache landing pads.
- break;
- }
-
- // If there's an existing TryOp, it means we got a `cir.try` scope
- // that leads to this "landing pad" creation site. Otherwise, exceptions
- // are enabled but a throwing function is called anyways (common pattern
- // with function local static initializers).
- mlir::ArrayAttr handlerTypesAttr = tryOp.getHandlerTypesAttr();
- if (!handlerTypesAttr || handlerTypesAttr.empty()) {
- // Accumulate all the handlers in scope.
- bool hasCatchAll = false;
- llvm::SmallPtrSet<mlir::Attribute, 4> catchTypes;
- llvm::SmallVector<mlir::Attribute> handlerAttrs;
- for (EHScopeStack::iterator i = ehStack.begin(), e = ehStack.end(); i != e;
- ++i) {
- switch (i->getKind()) {
- case EHScope::Cleanup:
- cgm.errorNYI("emitLandingPad: Cleanup");
- return;
-
- case EHScope::Filter:
- cgm.errorNYI("emitLandingPad: Filter");
- return;
-
- case EHScope::Terminate:
- cgm.errorNYI("emitLandingPad: Terminate");
- return;
-
- case EHScope::Catch:
- break;
- } // end switch
-
- EHCatchScope &catchScope = cast<EHCatchScope>(*i);
- for (const EHCatchScope::Handler &handler :
- llvm::make_range(catchScope.begin(), catchScope.end())) {
- assert(handler.type.flags == 0 &&
- "landingpads do not support catch handler flags");
-
- // If this is a catch-all, register that and abort.
- if (handler.isCatchAll()) {
- assert(!hasCatchAll);
- hasCatchAll = true;
- break;
- }
-
- // Check whether we already have a handler for this type.
- // If not, keep track to later add to catch op.
- if (catchTypes.insert(handler.type.rtti).second)
- handlerAttrs.push_back(handler.type.rtti);
- }
-
- if (hasCatchAll)
- break;
- }
-
- if (hasCatchAll)
- handlerAttrs.push_back(cir::CatchAllAttr::get(&getMLIRContext()));
-
- assert(!cir::MissingFeatures::ehScopeFilter());
-
- // If there's no catch_all, attach the unwind region. This needs to be the
- // last region in the TryOp catch list.
- if (!hasCatchAll)
- handlerAttrs.push_back(cir::UnwindAttr::get(&getMLIRContext()));
-
- // Add final array of clauses into TryOp.
- tryOp.setHandlerTypesAttr(
- mlir::ArrayAttr::get(&getMLIRContext(), handlerAttrs));
- }
-
- // In traditional LLVM codegen. this tells the backend how to generate the
- // landing pad by generating a branch to the dispatch block. In CIR,
- // this is used to populate blocks for later filing during
- // cleanup handling.
- populateEHCatchRegions(ehStack.getInnermostEHScope(), tryOp);
-}
-
-// Differently from LLVM traditional codegen, there are no dispatch blocks
-// to look at given cir.try_call does not jump to blocks like invoke does.
-// However.
-void CIRGenFunction::populateEHCatchRegions(EHScopeStack::stable_iterator scope,
- cir::TryOp tryOp) {
- if (EHPersonality::get(*this).usesFuncletPads()) {
- cgm.errorNYI("getEHDispatchBlock: usesFuncletPads");
- return;
- }
-
- // The dispatch block for the end of the scope chain is a block that
- // just resumes unwinding.
- if (scope == ehStack.stable_end()) {
- populateUnwindResumeBlock(/*isCleanup=*/true, tryOp);
- return;
- }
-
- // Otherwise, we should look at the actual scope.
- EHScope &ehScope = *ehStack.find(scope);
- bool mayThrow = ehScope.mayThrow();
-
- mlir::Block *originalBlock = nullptr;
- if (mayThrow && tryOp) {
- // If the dispatch is cached but comes from a
diff erent tryOp, make sure:
- // - Populate current `tryOp` with a new dispatch block regardless.
- // - Update the map to enqueue new dispatchBlock to also get a cleanup. See
- // code at the end of the function.
- cgm.errorNYI("getEHDispatchBlock: mayThrow & tryOp");
- return;
- }
-
- if (!mayThrow) {
- switch (ehScope.getKind()) {
- case EHScope::Catch: {
- mayThrow = true;
-
- // LLVM does some optimization with branches here, CIR just keep track of
- // the corresponding calls.
- EHCatchScope &catchScope = cast<EHCatchScope>(ehScope);
- if (catchScope.getNumHandlers() == 1 &&
- catchScope.getHandler(0).isCatchAll()) {
- break;
- }
-
- // TODO(cir): In the incubator we create a new basic block with YieldOp
- // inside the attached cleanup region, but this part will be redesigned
- break;
- }
- case EHScope::Cleanup: {
- cgm.errorNYI("getEHDispatchBlock: mayThrow & cleanup");
- return;
- }
- case EHScope::Filter: {
- cgm.errorNYI("getEHDispatchBlock: mayThrow & Filter");
- return;
- }
- case EHScope::Terminate: {
- cgm.errorNYI("getEHDispatchBlock: mayThrow & Terminate");
- return;
- }
- }
- }
-
- if (originalBlock) {
- cgm.errorNYI("getEHDispatchBlock: originalBlock");
- return;
- }
-
- ehScope.setMayThrow(mayThrow);
+ return mlir::success();
}
// in classic codegen this function is mapping to `isInvokeDest` previously and
@@ -725,25 +419,3 @@ bool CIRGenFunction::isCatchOrCleanupRequired() {
return ehStack.requiresCatchOrCleanup();
}
-
-// In classic codegen this function is equivalent to `getInvokeDestImpl`, in
-// ClangIR we don't need to return to return any landing pad, we just need to
-// populate the catch handlers if they are required
-void CIRGenFunction::populateCatchHandlersIfRequired(cir::TryOp tryOp) {
- assert(ehStack.requiresCatchOrCleanup());
- assert(!ehStack.empty());
-
- const EHPersonality &personality = EHPersonality::get(*this);
-
- // Set personality function if not already set
- auto funcOp = mlir::cast<cir::FuncOp>(curFn);
- if (!funcOp.getPersonality())
- funcOp.setPersonality(getPersonalityFn(cgm, personality));
-
- // CIR does not cache landing pads.
- if (personality.usesFuncletPads()) {
- cgm.errorNYI("getInvokeDestImpl: usesFuncletPads");
- } else {
- populateCatchHandlers(tryOp);
- }
-}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 786a0f6e9e23c..cc0087ba2d6bd 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -975,16 +975,14 @@ class CIRGenFunction : public CIRGenTypeCache {
return false;
}
- void populateUnwindResumeBlock(bool isCleanup, cir::TryOp tryOp);
- void populateEHCatchRegions(EHScopeStack::stable_iterator scope,
- cir::TryOp tryOp);
+ void addCatchHandlerAttr(const CXXCatchStmt *catchStmt,
+ SmallVector<mlir::Attribute> &handlerAttrs);
/// The cleanup depth enclosing all the cleanups associated with the
/// parameters.
EHScopeStack::stable_iterator prologueCleanupDepth;
bool isCatchOrCleanupRequired();
- void populateCatchHandlersIfRequired(cir::TryOp tryOp);
/// Takes the old cleanup stack size and emits the cleanup blocks
/// that have been added.
@@ -1590,13 +1588,6 @@ class CIRGenFunction : public CIRGenTypeCache {
mlir::LogicalResult emitCXXTryStmt(const clang::CXXTryStmt &s);
- mlir::LogicalResult emitCXXTryStmtUnderScope(const clang::CXXTryStmt &s);
-
- void enterCXXTryStmt(const CXXTryStmt &s, cir::TryOp tryOp,
- bool isFnTryBlock = false);
-
- void exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock = false);
-
void emitCtorPrologue(const clang::CXXConstructorDecl *ctor,
clang::CXXCtorType ctorType, FunctionArgList &args);
@@ -1750,8 +1741,6 @@ class CIRGenFunction : public CIRGenTypeCache {
void emitLambdaDelegatingInvokeBody(const CXXMethodDecl *md);
void emitLambdaStaticInvokeBody(const CXXMethodDecl *md);
- void populateCatchHandlers(cir::TryOp tryOp);
-
mlir::LogicalResult emitIfStmt(const clang::IfStmt &s);
/// Emit code to compute the specified expression,
diff --git a/clang/lib/CIR/CodeGen/EHScopeStack.h b/clang/lib/CIR/CodeGen/EHScopeStack.h
index 2ebe6df5c7cba..bbe0fced1c6ba 100644
--- a/clang/lib/CIR/CodeGen/EHScopeStack.h
+++ b/clang/lib/CIR/CodeGen/EHScopeStack.h
@@ -236,14 +236,6 @@ class EHScopeStack {
/// Pops a cleanup scope off the stack. This is private to CIRGenCleanup.cpp.
void popCleanup();
- /// Push a set of catch handlers on the stack. The catch is
- /// uninitialized and will need to have the given number of handlers
- /// set on it.
- class EHCatchScope *pushCatch(unsigned numHandlers);
-
- /// Pops a catch scope off the stack. This is private to CIRGenException.cpp.
- void popCatch();
-
/// Determines whether the exception-scopes stack is empty.
bool empty() const { return startOfData == endOfBuffer; }
diff --git a/clang/test/CIR/CodeGen/try-catch.cpp b/clang/test/CIR/CodeGen/try-catch.cpp
index 27e3d8ef41115..81ac15eeb7016 100644
--- a/clang/test/CIR/CodeGen/try-catch.cpp
+++ b/clang/test/CIR/CodeGen/try-catch.cpp
@@ -1,10 +1,10 @@
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fcxx-exceptions -fexceptions -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
-// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fcxx-exceptions -fexceptions -fclangir -emit-llvm %s -o %t-cir.ll
-// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fcxx-exceptions -fexceptions -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+// TODO(cir): Reenable lowering to LLVM after new try-catch lowering is implemented.
+
void empty_try_block_with_catch_all() {
try {} catch (...) {}
}
More information about the cfe-commits
mailing list